spring boot 集成redis版本说明
spring boot 集成redis版本说明
官网文档:https://docs.spring.io/spring-boot/docs/2.0.2.RELEASE/reference/htmlsingle/
当前版本是2.0.3(目前官网的当前版本也是2.0.3 {2018-06-25})
1.5.X版本redis依赖如下:
org.springframework.boot spring-boot-starter-data-redis
2.0.X版本redis依赖如下:
引用方式一:
org.springframework.data spring-data-redis org.slf4j jcl-over-slf4j
redis.clients jedis
说明:直接使用spring-data-redis ,引用jedis客户端引用方式二:
org.springframework.boot spring-boot-starter-data-redis io.lettuce lettuce-core
redis.clients jedis
说明:默认情况下,Spring Boot starter(spring-boot-starter-data-redis)使用 Lettuce。您需要排除该依赖关系,并包含Jedis。Spring Boot管理这些依赖关系,以便尽可能简化此过程。
spring boot caching与redis集成方案
配置RedisConfig.java
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {@BeanJedisConnectionFactory jedisConnectionFactory() {return new JedisConnectionFactory();}/*** spring boot 2.0.x以上版本的使用方式* @param redisConnectionFactory* @return*/@SuppressWarnings("rawtypes")@Beanpublic CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {RedisCacheManager rcm = RedisCacheManager.builder(redisConnectionFactory).build();return rcm;}/*** 自定义生成redis-key* lambda表达式* @return*/@Overridepublic KeyGenerator keyGenerator() {return (o, method, objects)->{StringBuilder sb = new StringBuilder();sb.append(o.getClass().getName()).append(".");sb.append(method.getName()).append(".");for (Object obj : objects) {sb.append(obj.toString());}System.out.println("keyGenerator=" + sb.toString());return sb.toString();};}@Beanpublic RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate template = new RedisTemplate();template.setConnectionFactory(redisConnectionFactory);//注入redis数据源template.setKeySerializer(new StringRedisSerializer());//设置key的序列化方式template.setValueSerializer(new RedisObjectSerializer());//设置value的序列化方式return template;}
}
@EnableCaching 开启caching功能 引入CachingConfigurationSelector自动配置,导入Caching相关注解拦截功能
CacheManager 由RedisCacheManager实现:RedisCacheManager.builder(redisConnectionFactory).build();(2.0.x版本)
配置RedisObjectSerializer序列化方式
/*** redis 序列化方式***/
public class RedisObjectSerializer implements RedisSerializer
对key/value的存储进行自定义的序列化
Spring cache的注解如何使用
在spring cache与redis集成之后,我们就可以使用spring cache自带的注解功能
缓存的主要使用方式包括以下两方面
- 缓存的声明,需要根据项目需求来妥善的应用缓存
- 缓存的配置方式,选择需要的缓存支持,例如Ecache、redis、memercache等
@CacheConfig:该注解是可以将缓存分类,它是类级别的注解方式。 @CacheConfig(cacheNames = "xxx")统一声明 @Cacheable(value="xxx")的属性,简单明了. @CacheConfig is a class-level annotation that allows to share the cache names,如果你在你的方法写别的名字,那么依然以方法的名字为准。
eg:
@CacheConfig(cacheNames = "user")
@Service
public class UserCacheRedisService {}说明:
@CacheConfig(cacheNames = "user"), 使UserCacheRedisService的所有缓存注解, value值就都为user。
@Cacheable:主要针对方法配置,能够根据方法的请求参数对其结果进行缓存 1.如果key不存在,执行方法体,并将结果更新到缓存中。 2.如果key存在,直接查询缓存中的数据。
@Cacheable 作用和配置方法
@Cacheable
public List selectAllUser(){log.info("selectAllUser execute");return data;
}@Cacheable(value = "getUser", key = "#id")
public User getUser(int id){User user = new User();user.setId(id);user.setAge(20);user.setUsername("玛雅文明");log.info("getUser execute");return user;
}
@CachePut:主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会执行方法体
- 如果key存在,更新内容
- 如果key不存在,插入内容。 @CachePut 作用和配置方法
@CachePut(value="saveOfUpdate", key = "\"user_\" + #user.id")
public User saveOfUpdate(User user){log.info("saveOfUpdate execute");return user;
}
@CachEvict: 主要针对方法配置,能够根据一定的条件对缓存进行清空,执行方法体
@CacheEvict 作用和配置方法
/*** 清空指定key缓存* @param user*/
@CacheEvict(value="saveOfUpdate", key="\"user_\" + #user.getId()")
public void clearUser(User user) {log.info("clearUser execute");
}/*** allEntries:是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存*/
@CacheEvict(value="saveOfUpdate", allEntries=true)
public void flushCacle() {log.info("flushCacle execute");
}
条件缓存 下面提供一些常用的条件缓存
//@Cacheable将在执行方法之前( #result还拿不到返回值)判断condition,如果返回true,则查缓存;
@Cacheable(value = "user", key = "#id", condition = "#id lt 10")
public User conditionFindById(final Long id) //@CachePut将在执行完方法后(#result就能拿到返回值了)判断condition,如果返回true,则放入缓存;
@CachePut(value = "user", key = "#id", condition = "#result.username ne 'zhang'")
public User conditionSave(final User user) //@CachePut将在执行完方法后(#result就能拿到返回值了)判断unless,如果返回false,则放入缓存;(即跟condition相反)
@CachePut(value = "user", key = "#user.id", unless = "#result.username eq 'zhang'")
public User conditionSave2(final User user) //@CacheEvict, beforeInvocation=false表示在方法执行之后调用(#result能拿到返回值了);且判断condition,如果返回true,则移除缓存;
@CacheEvict(value = "user", key = "#user.id", beforeInvocation = false, condition = "#result.username ne 'zhang'")
public User conditionDelete(final User user)
@Caching
有时候我们可能组合多个Cache注解使用;比如用户新增成功后,我们要添加id–>user;username—>user;email—>user的缓存;此时就需要@Caching组合多个注解标签了。
@Caching(put = {
@CachePut(value = "user", key = "#user.id"),
@CachePut(value = "user", key = "#user.username"),
@CachePut(value = "user", key = "#user.email")
})
public User save(User user) {
自定义缓存注解
@Caching组合,会让方法显的较臃肿,可以通过自定义注解把这些注解组合到一个注解中,如:
@Caching(put = {
@CachePut(value = "user", key = "#user.id"),
@CachePut(value = "user", key = "#user.username"),
@CachePut(value = "user", key = "#user.email")
})
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface UserSaveCache {
}使用:
@UserSaveCache
public User save(User user){//do something
}
扩展
比如findByUsername时,不应该只放username–>user,应该连同id—>user和email—>user一起放入;这样下次如果按照id查找直接从缓存中就命中了
@Caching(cacheable = {@Cacheable(value = "user", key = "#username")},put = {@CachePut(value = "user", key = "#result.id", condition = "#result != null"),@CachePut(value = "user", key = "#result.email", condition = "#result != null")}
)
public User findByUsername(final String username) {System.out.println("cache miss, invoke find by username, username:" + username);for (User user : users) {if (user.getUsername().equals(username)) {return user;}}return null;
}
其实对于:id—>user;username—->user;email—>user;更好的方式可能是:id—>user;username—>id;email—>id;保证user只存一份;如:
@CachePut(value="cacheName", key="#user.username", cacheValue="#user.username")
public void save(User user) @Cacheable(value="cacheName", key="#user.username", cacheValue="#caches[0].get(#caches[0].get(#username).get())")
public User findByUsername(String username)
Redis 序列化方式以及相互之间的比较
当我们的数据存储到Redis的时候,我们的键(key)和值(value)都是通过Spring提供的Serializer序列化到数据库的。
RedisTemplate默认使用的是JdkSerializationRedisSerializer
StringRedisTemplate默认使用的是StringRedisSerializer
Spring Data Redis(1.4.7版本)为我们提供了下面的Serializer:
GenericToStringSerializer、GenericJackson2JsonRedisSerializer、Jackson2JsonRedisSerializer、JacksonJsonRedisSerializer、JdkSerializationRedisSerializer、StringRedisSerializer。
序列化方式对比:
* GenericToStringSerializer:可以将任何对象泛化为字符串并序列化
* GenericJackson2JsonRedisSerializer:使用Jackson库将对象序列化为JSON字符串。优点:是速度快,序列化后的字符串短小精悍,不需要实现Serializable接口,会在json中加入@class属性,类的全路径包名,方便反序列化缺点:也非常致命,那就是此类的构造函数中有一个类型参数,必须提供要序列化对象的类型信息(.class对象)。 通过查看源代码,发现其只在反序列化过程中用到了类型信息,时间消耗比JDK长。
* JdkSerializationRedisSerializer: 使用JDK提供的序列化功能。 优点:是反序列化时不需要提供类型信息(class),最高效的缺点:是需要实现Serializable接口,还有序列化后的结果非常庞大,是JSON格式的5倍左右,这样就会消耗redis服务器的大量内存。
* Jackson2JsonRedisSerializer: 使用Jackson库将对象序列化为JSON字符串。优点:是速度快,序列化后的字符串短小精悍,不需要实现Serializable接口。缺点:也非常致命,那就是此类的构造函数中有一个类型参数,必须提供要序列化对象的类型信息(.class对象)。 通过查看源代码,发现其只在反序列化过程中用到了类型信息,时间消耗比JDK长。无法指定List容器里面元素的类,所以反序列化时直接将元素反序列化成了LinkedHashMap导致返回结果的时候强制类型转化报错。
* StringRedisSerializer:简单的字符串序列化,一般如果key-value都是string的话,使用StringRedisSerializer就可以了
JedisConnectionFactory配置介绍:
一.简单配置
application.properties
# REDIS (RedisProperties)
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=192.168.127.131
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=q1w2e3r4
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=0
RedisConfig配置
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {/*** 默认,只能连接本地,连接其他网络添加* jedisConnectionFactory.setHostName(host);* jedisConnectionFactory.setPort(port);* */@BeanJedisConnectionFactory jedisConnectionFactory() {return new JedisConnectionFactory(); }@SuppressWarnings("rawtypes")@Bean@Beanpublic CacheManager cacheManager(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);redisCacheManager.setDefaultExpiration(300);return redisCacheManager;}/*** 自定义生成redis-key* lambda表达式* @return*/@Overridepublic KeyGenerator keyGenerator() {return (o, method, objects)->{StringBuilder sb = new StringBuilder();sb.append(o.getClass().getName()).append(".");sb.append(method.getName()).append(".");for (Object obj : objects) {sb.append(obj.toString());}System.out.println("keyGenerator=" + sb.toString());return sb.toString();};}@Beanpublic RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate template = new RedisTemplate();template.setConnectionFactory(redisConnectionFactory);//注入redis数据源template.setKeySerializer(new StringRedisSerializer());//设置key的序列化方式//template.setValueSerializer(new RedisObjectSerializer());//设置value的序列化方式一template.setValueSerializer(jackRedisSerializer());//设置value的序列化方式二 推荐这种方式,这种方式不会乱码return template;}public Jackson2JsonRedisSerializer jackRedisSerializer(){// 使用Jackson2JsonRedisSerialize 替换默认序列化Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper objectMapper = new ObjectMapper();objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(objectMapper);return jackson2JsonRedisSerializer;}}
二.带jedispool的配置
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {@Value("${spring.redis.host}")private String host;@Value("${spring.redis.password}")private String password;@Value("${spring.redis.port}")private int port;@Value("${spring.redis.timeout}")private int timeout;@Value("${spring.redis.database}")private int defaultDatabaseIndex;@Value("${spring.redis.pool.max-idle}")private int maxIdle;@Value("${spring.redis.pool.min-idle}")private int minIdle;@Value("${spring.redis.pool.max-active}")private int maxActive;@Value("${spring.redis.pool.max-wait}")private long maxWaitMillis;@Beanpublic RedisConnectionFactory connectionFactory() {JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();jedisConnectionFactory.setHostName(host);jedisConnectionFactory.setPort(port);jedisConnectionFactory.setTimeout(timeout);if (!StringUtils.isEmpty(password)) {jedisConnectionFactory.setPassword(password);}if (defaultDatabaseIndex != 0) {jedisConnectionFactory.setDatabase(defaultDatabaseIndex);}jedisConnectionFactory.setPoolConfig(poolConfig());// 初始化连接pooljedisConnectionFactory.afterPropertiesSet();return jedisConnectionFactory;}public JedisPoolConfig poolConfig() {JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();jedisPoolConfig.setMaxTotal(maxActive);jedisPoolConfig.setMinIdle(minIdle);jedisPoolConfig.setMaxIdle(maxIdle);jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);return jedisPoolConfig;}/*** spring boot 1.3.x~1.5.x版本* @param redisTemplate* @return*/// 定制缓存管理器的属性,默认提供的CacheManager对象可能不能满足需要// 因此建议依赖业务和技术上的需求,自行做一些扩展和定制// 这样就可以在使用Spring4中的@Cacheable、@CachePut、@CacheEvict 注解了// 使用cache注解管理redis缓存@Beanpublic CacheManager cacheManager(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);redisCacheManager.setDefaultExpiration(300);return redisCacheManager;}/*** 自定义生成redis-key* lambda表达式* @return*/@Overridepublic KeyGenerator keyGenerator() {return (o, method, objects)->{StringBuilder sb = new StringBuilder();sb.append(o.getClass().getName()).append(".");sb.append(method.getName()).append(".");for (Object obj : objects) {sb.append(obj.toString());}System.out.println("keyGenerator=" + sb.toString());return sb.toString();};}@Beanpublic RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate template = new RedisTemplate();template.setConnectionFactory(redisConnectionFactory);//注入redis数据源template.setKeySerializer(new StringRedisSerializer());//设置key的序列化方式//template.setValueSerializer(new RedisObjectSerializer());//设置value的序列化方式一template.setValueSerializer(jackRedisSerializer());//设置value的序列化方式二 推荐这种方式,这种方式不会乱码return template;}public Jackson2JsonRedisSerializer jackRedisSerializer(){// 使用Jackson2JsonRedisSerialize 替换默认序列化Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper objectMapper = new ObjectMapper();objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(objectMapper);return jackson2JsonRedisSerializer;}
}
下面就是我的订阅号的二维码,欢迎大家关注!
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
