Redis+SpringBoot整合注解版
- 一、前言
- 二、配置文件
- 1.导入redis依赖
- 2.application.properties的配置
- 3.创建RedisConfig
- 三、使用注解开发
- 四、测试
- 五、其他
一、前言
我的上一篇博客是无注解版,围绕着RedisTemplate方法进行展开。
我的观点是注解简单但是不适合业务复杂的项目。
链接地址
二、配置文件
1.导入redis依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.application.properties的配置
# REDIS (RedisProperties)
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=5000
3.创建RedisConfig
注:RedisConfig类用于Redis数据缓存。
/*** redis配置类**/
@Configuration
@EnableCaching//开启注解式缓存
//继承CachingConfigurerSupport,为了自定义生成KEY的策略。可以不继承。
public class RedisConfig extends CachingConfigurerSupport {/*** 生成key的策略 根据类名+方法名+所有参数的值生成唯一的一个key** @return*/@Bean@Overridepublic KeyGenerator keyGenerator() {return new KeyGenerator() {@Overridepublic Object generate(Object target, Method method, Object... params) {StringBuilder sb = new StringBuilder();sb.append(target.getClass().getName());sb.append(method.getName());for (Object obj : params) {sb.append(obj.toString());}return sb.toString();}};}/*** 管理缓存** @param redisConnectionFactory* @return*/@Beanpublic CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {//通过Spring提供的RedisCacheConfiguration类,构造一个自己的redis配置类,从该配置类中可以设置一些初始化的缓存命名空间// 及对应的默认过期时间等属性,再利用RedisCacheManager中的builder.build()的方式生成cacheManager:RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); // 生成一个默认配置,通过config对象即可对缓存进行自定义配置config = config.entryTtl(Duration.ofMinutes(1)) // 设置缓存的默认过期时间,也是使用Duration设置.disableCachingNullValues(); // 不缓存空值// 设置一个初始化的缓存空间set集合Set<String> cacheNames = new HashSet<>();cacheNames.add("my-redis-cache1");cacheNames.add("my-redis-cache2");// 对每个缓存空间应用不同的配置Map<String, RedisCacheConfiguration> configMap = new HashMap<>();configMap.put("my-redis-cache1", config);configMap.put("my-redis-cache2", config.entryTtl(Duration.ofSeconds(120)));RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory) // 使用自定义的缓存配置初始化一个cacheManager.initialCacheNames(cacheNames) // 注意这两句的调用顺序,一定要先调用该方法设置初始化的缓存名,再初始化相关的配置.withInitialCacheConfigurations(configMap).build();return cacheManager;}@Beanpublic RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(connectionFactory);//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper mapper = new ObjectMapper();mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);serializer.setObjectMapper(mapper);template.setValueSerializer(serializer);//使用StringRedisSerializer来序列化和反序列化redis的key值template.setKeySerializer(new StringRedisSerializer());template.afterPropertiesSet();return template;}@Beanpublic StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();stringRedisTemplate.setConnectionFactory(factory);return stringRedisTemplate;}}
注意:
继承CachingConfigurerSupport,为了自定义生成KEY的策略。可以不继承。
RedisConfig entends CachingConfigurerSupport
在CacheManager中我在这里添加了两个缓存槽:my-redis-cache1和my-redis-cache2。
三、使用注解开发
@Cacheable
作用是主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
//添加到缓存@Override@Cacheable(value="my-redis-cache1",key = "'getUser'+#name",condition="#name=='yuange'")public User getUser(String name) {User user = new User();user.setName(name);return user;}
参数说明:
value :
缓存的名称,在 spring 配置文件中定义,必须指定至少一个,
key :缓存的 key,可以为空,
如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合,
例如:@Cacheable(value=”my-redis-cache1”,key=”#name”)。
condition :缓存的条件,可以为空,
使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存,
例如: @Cacheable(value=“my-redis-cache1”,key = “‘getUser’+#name”,condition=“#name==‘yuange’”)
@CachePut
只存不读 每次都真实查询数据库,把数据带到内存来;
作用是主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实 方法的调用
//修改缓存值@Override@CachePut(value = "my-redis-cache1", key = "'getUser'+#name")public User updateUser(String name) {User user = new User();user.setName(name);System.out.println("修改了名字");return user;}
主要参数说明:
@Cacheable一样。
@CacheEvict:
作用是主要针对方法配置,能够根据一定的条件对缓存进行清空
//删除缓存值@Override@CacheEvict(value = "my-redis-cache1", key = "'getUser'+#name")public void delUser(String name) {System.out.println("清除完成");}
参数说明:
allEntries :
是否清空所有缓存内容,缺省为 false,
如果指定为 true,则方法调用后将立即清空所有缓存,
例如:@CacheEvict(value=”testcache”,allEntries=true)。
beforeInvocation :
是否在方法执行前就清空,缺省为 false,
如果指定为 true,则在方法还没有执行的时候就清空缓存,
缺省情况下,如果方法执行抛出异常,则不会清空缓存,
例如@CacheEvict(value=”testcache”,beforeInvocation=true)。
四、测试
实体类User
/*** @author lichangyuan* @create 2021-02-22 22:20*/
public class User implements Serializable{private String name;public User() {System.out.println("创建了对象");}public User(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +'}';}
}
UserServiceImpl
/*** @author lichangyuan* @create 2021-02-22 23:43*/
@Service
public class UserServiceImpl implements UserService {//添加到缓存@Override@Cacheable(value="my-redis-cache1",key = "'getUser'+#name",condition="#name>10")
// @Cacheable(value = "my-redis-cache1", key = "'getUser'+#name")public User getUser(String name) {User user = new User();user.setName(name);return user;}//修改缓存值@Override@CachePut(value = "my-redis-cache1", key = "'getUser'+#name")public User updateUser(String name) {User user = new User();user.setName(name);System.out.println("修改了名字");return user;}//删除缓存值@Override@CacheEvict(value = "my-redis-cache1", key = "'getUser'+#name")public void delUser(String name) {System.out.println("清除完成");}@Override@CacheEvict(value = "my-redis-cache1", allEntries = true)public void clear() {System.out.println("my-redis-cache1清除成功");}@Override@CacheEvict(value = "my-redis-cache1", allEntries = true)public void clear2() {System.out.println("my-redis-cache1清除成功");}}
UserController
/*** @author lichangyuan* @create 2021-02-22 23:41*/
@RestController
public class UserController {@AutowiredUserService userService;@RequestMapping("get_name")public void getName(@RequestParam("name") String name) {//第一次获取假设是从数据库获取,并写入缓存User user = userService.getUser(name);//第二次获取由于缓存中已存在所以从缓存中获取User user2 = userService.getUser(name);System.out.println("===============================================");//更改缓存内容,因此不会读缓存会调用无参构造器创建一次对象。User user3 = userService.updateUser(name);System.out.println("===============================================");//删除指定缓存nameuserService.delUser(name);//因为缓存中的值被我们删掉了,再获取时又会创建一次对象而不是从缓存中拿User user4 = userService.getUser(name);}
}
使用postman测试
测试结果
五、其他
@Cacheable注解不生效原因
一个方法A调同一个类里的另一个有缓存注解的方法B,这样是不走缓存的。
例如在同一个service里面两个方法的调用,缓存是不生效的;
解决方案:
1.不使用注解的方式,直接取 Ehcache 的 CacheManger 对象,把需要缓存的数据放到里面,类似于使用 Map,缓存的逻辑自己控制;或者可以使用redis的缓存方式去添加缓存;
2.把方法A和方法B放到两个不同的类里面,例如:如果两个方法都在同一个service接口里,把方法B放到另一个service里面,这样在A方法里调B方法,就可以使用B方法的缓存。
为什么缓存没有被正常创建?
因为@Cacheable 是使用AOP 代理实现的 ,通过创建内部类来代理缓存方法,这样就会导致一个问题,类内部的方法调用类内部的缓存方法不会走代理,不会走代理,就不能正常创建缓存,所以每次都需要去调用数据库。
@Cacheable是想缓存中添加数据,
@CachePut 更新缓存中的数据,
@CacheEvict是删除缓存中的数据
- 希望2022变得好—点,再好—点。冲鸭!
- 希望我俩能走出困境,家庭美满幸福。
- 我有两个愿望,你在身边,在你身边。
- 晴天、雨天、阴天,愿你快乐每一天。
- 愿望不是许出来的,是自己努力换来的!
- 点亮一—盏心灯,点亮爱,烛照光明前路!
- 万家灯火,千里祈福,愿一切越来越好。
- 你的离别已是蓄谋已久,又何必找借口。
- 愿你遇良人,予你欢喜城,长歌暖浮生。
- 愿您:大财、小财、意外财,财源滚滚。