1.å°åºå¦ä½å¨springä¸ä½¿ç¨redis
2.Spring-data-redis + Lettuce 如何使用 Pipeline
3.SpringBoot注册RedisTemplate乱码问题及解决方案
4.Spring Data Redis切换底层Jedis 和 Lettuce实现
5.RedisTemplate:我不背锅,是你用错了
6.分析SpringBoot 的Redis源码
å°åºå¦ä½å¨springä¸ä½¿ç¨redis
1. Redis使ç¨åºæ¯
Redisæ¯ä¸ä¸ªå¼æºç使ç¨ANSI Cè¯è¨ç¼åãæ¯æç½ç»ãå¯åºäºå å亦å¯æä¹ åçæ¥å¿åãKey-Valueæ°æ®åºï¼å¹¶æä¾å¤ç§è¯è¨çAPIã
æ们é½ç¥éï¼å¨æ¥å¸¸çåºç¨ä¸ï¼æ°æ®åºç¶é¢æ¯æ容æåºç°çãæ°æ®é太大åé¢ç¹çæ¥è¯¢ï¼ç±äºç£çIOæ§è½çå±éæ§ï¼å¯¼è´é¡¹ç®çæ§è½è¶æ¥è¶ä½ã
è¿æ¶åï¼åºäºå åçç¼åæ¡æ¶ï¼å°±è½è§£å³æ们å¾å¤é®é¢ãä¾å¦Memcacheï¼Redisçãå°ä¸äºé¢ç¹ä½¿ç¨çæ°æ®æ¾å ¥ç¼å读åï¼å¤§å¤§éä½äºæ°æ®åºçè´æ ãæåäºç³»ç»çæ§è½ã
å ¶å®ï¼å¯¹äºhibernateçäºçº§ç¼åï¼æ¯åæ ·çéçãå©ç¨å åé«éç读åé度ï¼æ¥è§£å³ç¡¬ççç¶é¢ã
2. é 置使ç¨redis
é¦å ï¼æ们éè¦å¼å ¥åºæ¬çjarå ãmavenä¸çåºæ¬å¼ç¨å¦ä¸ï¼
ãããã<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.4.2.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.6.2</version>
</dependency>
ç¶åï¼å¨applicationContextä¸é ç½®å¦ä¸:
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${ redis.maxIdle}" />
<property name="maxTotal" value="${ redis.maxActive}" />
<property name="maxWaitMillis" value="${ redis.maxWait}" />
<property name="testOnBorrow" value="${ redis.testOnBorrow}" />
</bean>
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:host-name="${ redis.host}" p:port="${ redis.port}" p:password="${ redis.pass}"
p:pool-config-ref="poolConfig" />
<bean id="stringSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
<!-- å¼å¯äºå¡ï¼å¯ä»¥éè¿transcational注解æ§å¶ -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="connectionFactory" />
<property name="keySerializer" ref="stringSerializer" />
<property name="enableTransactionSupport" value="true" />
</bean>
对äºhibernateçé ç½®å¯ç¥ï¼ç¬¬ä¸ä¸ªpoolconfigæ¯å¯¹è¿æ¥æ± çé ç½®ãå æ¬æ大è¿æ¥æ°ï¼éåæ°ï¼åæ´»æ¶é´ï¼æ大çå¾ æ¶é´ççï¼è¿æä¸äºé¢å¤çé ç½®ï¼è¯·ç´æ¥ç¹å»JedisPoolConfigç±»æºç ï¼è¿è¡æ¥çã
è¿äºé ç½®çææå¦æä¸æç½çè¯ï¼ä¸å®è¦å»æ线ç¨æ± 好好å¦ä¹ ä¸ã
第ä¸ä¸ªé ç½®æ¯è¿æ¥å·¥åï¼é¡¾åæä¹ï¼æåºæ¬ç使ç¨ä¸å®æ¯å¯¹è¿æ¥çæå¼åå ³éãæ们éè¦ä¸ºå ¶é ç½®redisæå¡å¨çè´¦æ·å¯ç ï¼ç«¯å£å·ãï¼è¿éè¿å¯ä»¥é ç½®æ°æ®åºçindexï¼ä½æ¯æ使ç¨æ¶åä¸ç´ä½¿ç¨redisçé»è®¤æ°æ®åºï¼ä¹å°±æ¯ç¬¬0个ï¼
æåä¸ä¸ªé ç½®ç¹å«éè¦ãè¿ä¸ªç±»ä¼¼äºspringæä¾çHibernateDaoSupportã
æ¥ä¸æ¥ï¼å ¨é¨è®²è§£é½å°å´ç»è¿ä¸ªç±»å±å¼ã
3. RedisTemplateç使ç¨
è¿ä¸ªç±»ä½ä¸ºä¸ä¸ªæ¨¡çç±»ï¼æä¾äºå¾å¤å¿«é使ç¨redisçapiï¼èä¸éè¦èªå·±æ¥ç»´æ¤è¿æ¥ï¼äºå¡ã
æåçæ¶åï¼æå建çBaseRedisDaoæ¯ç»§æ¿èªè¿ä¸ªç±»çã继æ¿ç好å¤æ¯æçæ¯ä¸ªDaoä¸ï¼é½å¯ä»¥èªç±çæ§å¶åºååå¨ï¼èªç±çæ§å¶èªå·±æ¯å¦éè¦äºå¡ï¼è¿ä¸ªå ä¸éè¦äºè§£ï¼è·çæç®åçè¿ç§é ç½®æ¹æ³æ¥å³å¯ã
templateæä¾äºä¸ç³»åçoperation,æ¯å¦valueOperation,HashOperation,ListOperation,SetOperationçï¼ç¨æ¥æä½ä¸åæ°æ®ç±»åçRedisã
并ä¸ï¼RedisTemplateè¿æä¾äºå¯¹åºç*OperationsEditorï¼ç¨æ¥éè¿RedisTemplateç´æ¥æ³¨å ¥å¯¹åºçOperationãæ们ææ¶ä¸è®²è¿ä¸ªã
对äºä¸é¢çtest1æ¹æ³ï¼æ们ææ¶ä¸ç¨èèï¼å äºè§£éè¿RedisTemplateæ¥ä½¿ç¨connectionæä½Redisã
Test代ç å¦ä¸ï¼
package cn.test.spjedis;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.cn.redis2.dao.IncrDao;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class TestRedis {
@Resource(name = "redisTemplate")
private RedisTemplate<String, String> template; // inject the template as ListOperations
//è³äºè¿ä¸ªä¸ºä»ä¹å¯ä»¥æ³¨å ¥ãéè¦åèAbstractBeanFactory doGetBean
//super.setValue(((RedisOperations) value).opsForValue());å°±è¿ä¸è¡ä»£ç ä¾é ä¸ä¸ªeditor
@Resource(name = "redisTemplate")
private ValueOperations<String, Object> vOps;
public void testSet(){
template.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
byte [] key = "tempkey".getBytes();
byte[] value = "tempvalue".getBytes();
connection.set(key, value);
return true;
}
});
}
public void testSet1(){
vOps.set("tempkey", "tempvalue");
}
@Autowired
private IncrDao incr;
@Test
public void addLink() {
System.out.println(incr.incr());
System.out.println(incr.get());
}
}
è¿ä¸ªæ¯å¯¹Stringç±»åæå ¥ç两个æµè¯ãtestæ¹æ³ä¸ï¼ä½¿ç¨äºæ¨¡çç±»æ交åè°(RedisCallBack)çæ¹æ³æ¥ä½¿ç¨jedis connectionæä½æ°æ®ãè¿ä¸é¨åï¼æ没æä¼¼æ¾ç¸è¯å¢ï¼
HibernateTemplateçHibernateCallbackï¼ä»¥åHibernate Sessionç±»ä¸çdoWork以ådoReturningWorkæ¹æ³ï¼é½æ¯ä½¿ç¨äºè¿æ ·çæºå¶ï¼æ¹ä¾¿å¯¹äºè¿æ¥æè sessionçç»ä¸ç®¡çã
public int excuteHqlUpdate(final String hql,final Object ...params){
return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<Integer>() {
@Override
@SuppressWarnings("unchecked")
public Integer doInHibernate(Session session) throws HibernateException {
Query queryObject = session.createQuery(hql);
if (params != null) {
for (int i = 0; i < params.length; i++) {
queryObject.setParameter(i, params[i]);
}
}
return queryObject.executeUpdate();
}
});
}
Spring-data-redis + Lettuce 如何使用 Pipeline
在探讨Spring-data-redis与Lettuce的Pipeline使用方法时,首先需要理解Spring-data-redis和Lettuce的基本原理及其如何协同工作。使用Pipeline的主要目的是减少网络传输次数,提升操作效率。具体配置和使用流程如下:
在Spring-data-redis与Lettuce的留言页源码集成中,Pipeline通过RedisCallback进行操作。正确的使用方式是:在异步专用连接上执行回调,而避免直接通过RedisTemplate调用。
要实现Pipeline生效,需确保所有操作在同一个连接上执行,并且使用回调。当使用Pipeline时,操作会首先本地缓存,然后统一发送到Redis服务端执行,最后接收并返回结果集。
Lettuce中有一个AutoFlushCommands配置,其默认为false,表示每接收到一个命令即发送到服务端。若配置为false,则会缓存所有命令,直至手动调用flushCommands方法,将所有命令一并发送至服务端,实现Pipeline功能。
自Spring-data-redis 2.3.0版本,已支持Lettuce的Pipeline配置。配置步骤为:在RedisTemplate中设置shareNativeConnection为false,源码安装djnago避免使用共享连接,以防止阻塞等待连接。同时,确保连接池的大小大于预期并发线程数。
关闭共享连接的原因在于要使用PipeliningFlushPolicy,此策略要求每次获取独占连接。Pipeline的实现逻辑在PipeliningFlushPolicy的源码中得到了体现,特别是flushOnClose和buffered两种实现方式。关闭共享连接,实际上是为了正确地执行Pipeline操作。
总之,正确配置Spring-data-redis和Lettuce的Pipeline,结合合理的连接管理和策略选择,可以显著提升Redis操作效率。
SpringBoot注册RedisTemplate乱码问题及解决方案
在开发过程中,遇到SpringBoot注册RedisTemplate后出现乱码问题。在处理此问题时,我们进行了以下几个步骤的排查和解决。
首先,我们检查了序列化过程是否正常,确认了key使用了string序列化,而value则使用了jackson进行序列化。这一步排除了序列化本身的问题。
接着,我们查看了RedisTemplate是否正确注册。结果显示,编译源码工具RedisTemplate确实已经成功注册,排除了注册问题。
进一步深入源码分析,我们发现值的序列化方式仍然使用了Java的默认序列化机制。这揭示了问题所在,因为Spring在Bean注册时默认使用了方法名来注册实例,而当我们引入spring-boot-starter-data-redis后,Spring会自动注册redisTemplate实例,该实例默认使用了Java的默认序列化方式。
为了解决乱码问题,我们采取了以下措施:
1. 修改了Bean注册的方法名,将其更改为redisTemplate。
2. 在注入时,我们明确指定了bean名称。
3. 在注册bean时,我们也指定了bean名称为redisTemplate。通过这些调整,我们成功解决了RedisTemplate在注册时使用的序列化方式与期望不符的问题,从而避免了乱码现象的出现。
Spring Data Redis切换底层Jedis 和 Lettuce实现
Spring Data Redis提供了对Redis操作的高级抽象,支持Jedis和Lettuce两种连接方式。通过简单的配置即可连接Redis并切换连接方式。具体步骤如下:
引入Redis依赖使用Spring Boot的spring-boot-starter-data-redis。
自定义配置类设置Key和Value的序列化。
修改Redis连接配置,可自由切换单节点、linux mv 源码哨兵模式和集群模式。
注入RedisTemplate后,即可操作Redis。RedisTemplate具有两个泛型。
源码分析部分,从Redis自动配置类RedisAutoConfiguration开始,它引入了两个连接Redis配置类:LettuceConnectionConfiguration和JedisConnectionConfiguration。这两个配置类通过条件注解控制是否生效,如果生效,则会使用相应的依赖生成RedisConnectionFactory的Bean。引入Lettuce依赖时,能通过io.lettuce.core.RedisClient找到类,说明默认使用Lettuce。若无Jedis相关依赖,则当前配置类无效。
切换连接方式至Jedis有两种方式:利用@ConditionalOnClass注解排除Lettuce依赖,或利用@ConditionalOnProperty注解修改配置文件中的spring.redis.client-type为jedis。第一种方式优点在于不加载多余的依赖包,推荐使用。第二种方式则可通过配置文件自由切换连接方式。
本文由OpenWrite平台发布。请按照上述步骤进行Spring Data Redis的使用和连接方式切换。
RedisTemplate:我不背锅,是你用错了
RedisTemplate的使用问题,探讨原因与解决方案。
观察问题,终极战区源码发现使用RedisTemplate获取不到值。首先怀疑RedisTemplate与StringRedisTemplate使用的Connection不同。经确认,使用的是同一Connection,问题可能出在代码逻辑或序列化方式上。
通过调试源码,发现在Redis客户端BinaryJedis的hget方法中,Redis并未返回数据。分析问题,发现是RedisTemplate默认使用了JDK序列化,与StringRedisTemplate的数据格式不匹配。
进一步对比StringRedisTemplate与RedisTemplate的关系,发现StringRedisTemplate专门用于字符串操作,而RedisTemplate用于复杂对象操作,主要区别在于序列化方式不同。
通过redis客户端查看数据格式,发现存储的是字符串格式的Hash。使用StringRedisTemplate能获取数据的原因,可能是存储时使用了StringRedisTemplate。而RedisTemplate默认使用JDK序列化,导致数据格式不匹配,无法获取。
调整RedisTemplate的序列化方式,将其从JDK序列化改为与StringRedisTemplate一致的字符串序列化方式。重新运行测试,发现仍然无法获取数据。查看RedisTemplate对象信息,发现虽然valueSerializer已更改为一致,但hash序列化仍使用了JDK序列化。
为了确保一致性,将所有序列化方式统一设置为一致的字符串序列化。修改后,成功获取到了数据。个人认为,这主要是由于使用RedisTemplate时对默认序列化方式的理解不足,导致了问题的出现。
仔细阅读源码,可以发现StringRedisTemplate与RedisTemplate在序列化方式上的区别,即StringRedisTemplate默认为所有操作设置了String序列化方式,而RedisTemplate默认使用JDK序列化。理解这些差异,有助于正确使用这两个类,并避免类似问题。
分析SpringBoot 的Redis源码
在Spring Boot 2.X版本中,官方简化了项目配置,如无需编写繁琐的web.xml和相关XML文件,只需在pom.xml中引入如spring-boot-starter-data-redis的starter包即可完成大部分工作,这极大地提高了开发效率。
深入理解其原理,我们研究了spring-boot-autoconfigure和spring-boot-starter-data-redis的源码。首先,配置项在application.properties中的设置会被自动映射到名为RedisProperties的类中,此类由RedisAutoConfiguration类负责扫描和配置。该类会检测是否存在RedisOperations接口的实现,例如官方支持的Jedis或Lettuce,以此来决定使用哪个客户端。
在RedisAutoConfiguration中,通过@Bean注解,它引入了LettuceConnectionConfiguration和JedisConnectionConfiguration,这两个配置类会创建RedisConnectionFactory实例。在注入RedisTemplate时,实际使用的会是第一个被扫描到的RedisConnectionFactory,这里通常是LettuceConnectionFactory,因为它们在@Import注解的导入顺序中位于前面。
自定义starter时,可以模仿官方starter的结构,首先引入spring-boot-autoconfigure,然后创建自己的配置类(如MyRedisProperties)和操作模板类(如JedisTemplete)。在MyRedisAutoConfiguration中,你需要编写相关配置并确保在spring.factories文件中注册,以便Spring Boot在启动时扫描到你的自定义配置。
以自定义my-redis-starter为例,项目结构包括引入的依赖,配置类的属性绑定,以及创建连接池和操作方法的实现。测试时,只需在Spring Boot项目中引入自定义starter,配置好相关参数,即可验证自定义starter的正确工作。
springboot的自动装配原理(springboot自动装配原理过程)
springboot自动装配原理面试回答是什么?
SpringBoott自动装配原理主要解决了传统spring的重量级xml配置Bean.实好猜现了自动装配;所以,我们也常在面试中被问到SpringBoot是如何实现自动装配。
springboo的介绍
SpringBoot基于Spring4.0设计,不仅继承了Spring框架原有的优秀特性,而且还通过简化配置来进一步简化了Spring应用的整个搭建和开发过程。
另外SpringBoot通过集成大量的框架使得依赖包的版本冲突,以及引用的不稳定性等问题得到了很好物绝的解决。?SpringBoot框架中还有两个非常重要的策略:开箱即用和约定优于配置,开箱即用,Outofbox,是指在开发过程中。
通过在MAVEN项目的pom文件中添加相关依赖包,然后使用对应注解来代替繁琐的XML配置文件以管理对象的生命周期。
这个特点使得开发人员摆脱了复杂的配置工作以及依赖的管理工作,更加专注于业务逻辑。约定优于配置,Conventionoverconfiguration,是一种由SpringBoot本身来配罩袜姿置目标结构。
SpringBoot自动装配原理初看@SpringBootApplication有很多的注解组成,其实归纳就是一个"三体"结构,重要的只有三隐含指个Annotation:
(1)@Configuration注解
(2)@ComponentScan
(3)@EnableAutoConfiguration
从源码中可以知道,最关键的要属@Import(EnableAutoConfigurationImportSelector.class),借助EnableAutoConfigurationImportSelector,@EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件的灶配@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器。同时借助于Spring框架原有的一个工具类:SpringFactoriesLoader,@EnableAutoConfiguration就可以实现智能的自动配置。
总结:@EnableAutoConfiguration作用就是从classpath中搜寻所有的META-INF/spring.factories配置文件,并将其中org.springframework.boot.autoconfigure.EnableutoConfiguration对应的配置项通过反射(JavaRefletion)实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到IoC容器。这些功能配置类要生效的话,会去classpath中找是否有该类的依赖类(也就是pom.xml必须有对应功能的jar包才行)并且配置类里面注入了默认属性值类,功能类可以引用并赋默认值。生成功能类的原则是自定义优先,没有自定义时才会使用自动装配类。
1、从spring-boot-autoconfigure.jar/META-INF/spring.factories中获取redis的相关配置类全限定名(有多个的配置类)RedisAutoConfiguration,一般一个功能配置类围绕该功能,负责管理创建多个相关的功能类,比如RedisAutoConfiguration负责:JedisConnectionFactory、RedisTemplate、StringRedisTemplate这3个功能类的创建
2、RedisAutoConfiguration配置类生效的一个条件是在classpath路径下有RedisOperations类存在,因此springboot的自动装配机制会会去classpath下去查找对应的class文件。
3.如果pom.xml有对应的jar包,就能匹配到对应依赖class,
4、匹配成功,这个功能配置类才会生效,同时会注入默认的属性配置类@EnableConfigurationProperties(RedisProperties.class)
5.Redis功能配置里面会根据条件生成最终的JedisConnectionFactory、RedisTemplate,并提供了默认的配置形式@ConditionalOnMissingBean(name="redisTemplate")
6.最终创建好的默认装配类,会通过功能配置类里面的@Bean注解,注入到IOC当中
7.用户使用,当用户在配置文件中自定义时候就会覆盖默认的配置@ConditionalOnMissingBean(name="redisTemplate")
1.通过各种注解实现了类与类之间的依赖关系,容器在启动的老斗时候Application.run,会调用EnableAutoConfigurationImportSelector.class的selectImports方法(其实是其父类的方法)--这里需要注意,调用这个方法之前发生了什么和是在哪里调用这个方法需要进一步的探讨
2.selectImports方法最终会调用SpringFactoriesLoader.loadFactoryNames方法来获取一个全面的常用BeanConfiguration列表
3.loadFactoryNames方法会读取FACTORIES_RESOURCE_LOCATION(也就是spring-boot-autoconfigure.jar下面的spring.factories),获取到所有的Spring相关的Bean的全限定名ClassName,大概多个
4.selectImports方法继续调用filter(configurations,autoConfigurationMetadata);这个时候会根据这些BeanConfiguration里面的条件,来一一筛选,最关键的是
@ConditionalOnClass,这个条件注解会去classpath下查找,jar包里面是否有这个条件依赖类,所以必须有了相应的jar包,才有这些依赖类,才会生成IOC环境需要的一些默认配置Bean
5.最后把符合条件的BeanConfiguration注入默认的EnableConfigurationPropertie类里面的属性值,并且注入到IOC环境当中
springboot自动装配原理@EnableAutoConfiguration
1、springboot启动会加载大量的自动配弯弊盯置类:(在下面的spring.factories文件中)
2、通过@ConditionalOnXXX判断我们是否导入了相关的功能(卜键就是pom文件中的starter),如果导入了,就会自动配置。
4、给容器中添加自动配置类的时候,埋和会从XXXProperties类中获取某些属性。我们只需要在配置文件中指定这些属性即可,如果没指定就会用默认值。
XXXA
比如:server.port
最后,通过配置:debug=true
可以查看失效和未生效的类(spring.factories文件中的)