项目开发中使用缓存是常用提高查询性能的有效手段,这里主要讲解一下Redis与Spring中几个缓存注解的整合使用。
spring:
#Redis配置
redis:
host: 127.0.0.1
#Redis服务器连接密码(默认为空)
password: xxxx
#Redis数据库索引(默认为0)
database: 5
port: 6379
#连接超时时间(毫秒)
timeout: 5000ms
#这里使用的是lettuce,如果使用Jedis,把下面的lettuce改成Jedis即可
lettuce:
pool:
#连接池中的最大空闲连接
max-idle: 8
#连接池中的最小空闲连接
min-idle: 0
#连接池最大连接数(使用负值表示没有限制)
max-active: 8
#连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
Configuration
@EnableCaching //开启缓存注解
@Slf4j
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
log.info("--init RedisTemplate--");
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 配置连接工厂
template.setConnectionFactory(factory);
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jacksonSeial.setObjectMapper(om);
// 值采用json序列化
template.setValueSerializer(jacksonSeial);
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
// 设置hash key 和value序列化模式
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(jacksonSeial);
template.afterPropertiesSet();
return template;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题),过期时间60*60秒
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(60*60))
// .entryTtl(Duration.ofSeconds(-1)) //设置过期时间:-1为永久有效
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}
/**
* 对hash类型的数据操作
*
* @param redisTemplate
* @return
*/
@Bean
public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForHash();
}
/**
* 对redis字符串类型数据操作
*
* @param redisTemplate
* @return
*/
@Bean
public ValueOperations<String, Object> valueOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForValue();
}
/**
* 对链表类型的数据操作
*
* @param redisTemplate
* @return
*/
@Bean
public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForList();
}
/**
* 对无序集合类型的数据操作
*
* @param redisTemplate
* @return
*/
@Bean
public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForSet();
}
/**
* 对有序集合类型的数据操作
*
* @param redisTemplate
* @return
*/
@Bean
public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForZSet();
}
}
代码中对@Cacheable、@CacheEvict、@CachePut几个注解有一定的说明
Slf4j
@RestController
@RequestMapping("/redis")
public class RedisController {
/**
* @Cacheable可以标记在一个方法上,也可以标记在一个类上。
* 当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。
* 对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,
* 以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。
*/
/**
* 1.value属性是必须指定的,其表示当前方法的返回值是会被缓存在哪个Cache上的,对应Cache的名称。
* 其可以是一个Cache也可以是多个Cache,当需要指定多个Cache时其是一个数组。
* 2. key属性是用来指定Spring缓存方法的返回结果时对应的key的。该属性支持SpringEL表达式。
*/
@GetMapping("/cacheTest")
@Cacheable(value = "test_user",key="#userId")
public DataResult cacheTest(String userId,String params){
SysUser sysUser=new SysUser();
sysUser.setId(userId);
sysUser.setRealName("海贼-王路飞");
sysUser.setAge(20);
sysUser.setSex(1);
sysUser.setCreateTime(new Date());
log.info("=====sysUser:{}",sysUser);
return DataResult.success(sysUser);
}
@GetMapping("/cacheTest2")
@Cacheable(value = {"test_user","test_cache"},key="#userId")
public DataResult cacheTest2(String userId,String params){
SysUser sysUser=new SysUser();
sysUser.setId(userId);
sysUser.setRealName("拜金-娜美");
sysUser.setAge(18);
sysUser.setSex(2);
sysUser.setCreateTime(new Date());
log.info("=====sysUser:{}",sysUser);
return DataResult.success(sysUser);
}
/**
* condition属性指定发生的条件
* condition属性默认为空,表示将缓存所有的调用情形。其值是通过SpringEL表达式来指定的,
* 当为true时表示进行缓存处理;当为false时表示不进行缓存处理,
* 即每次调用该方法时该方法都会执行一次
*/
@GetMapping("/cacheTest3")
@Cacheable(value = "test_user",key="#user.id",condition="#user.age%2==0")
public DataResult cacheTest3(SysUser user){
SysUser sysUser=new SysUser();
sysUser.setId(user.getId());
sysUser.setRealName("厨子-老三");
sysUser.setAge(user.getAge());
sysUser.setSex(1);
sysUser.setCreateTime(new Date());
log.info("=====sysUser:{}",sysUser);
return DataResult.success(sysUser);
}
/**
* @CachePut: 在支持Spring Cache的环境下,对于使用@Cacheable标注的方法,
* Spring在每次执行前都会检查Cache中是否存在相同key的缓存元素,
* 如果存在就不再执行该方法,而是直接从缓存中获取结果进行返回,
* 否则才会执行并将返回结果存入指定的缓存中。
* @CachePut也可以声明一个方法支持缓存功能。
* 与@Cacheable不同的是使用@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,
* 而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。
*/
@GetMapping("/cacheTest4")
@CachePut(value = "test_user",key="#userId")
public DataResult cacheTest4(String userId,Integer age){
SysUser sysUser=new SysUser();
sysUser.setId(userId);
sysUser.setRealName("剑客-绿头索大");
sysUser.setAge(23);
sysUser.setSex(1);
sysUser.setCreateTime(new Date());
log.info("=====sysUser:{}",sysUser);
return DataResult.success(sysUser);
}
/**
* @CacheEvict是用来标注在需要清除缓存元素的方法或类上的。
* 当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。
* @CacheEvict可以指定的属性有value、key、condition、allEntries和beforeInvocation。
* 其中value、key和condition的语义与@Cacheable对应的属性类似。
* 即value表示清除操作是发生在哪些Cache上的(对应Cache的名称);
* key表示需要清除的是哪个key,如未指定则会使用默认策略生成的key;
* condition表示清除操作发生的条件。
* 下面我们来介绍一下新出现的两个属性allEntries和beforeInvocation。
*/
/**
* allEntries是boolean类型,表示是否需要清除缓存中的所有元素。
* 默认为false,表示不需要。
* 当指定了allEntries为true时,Spring Cache将忽略指定的key
*/
@GetMapping("/cacheTest5")
@CacheEvict(value="test_user",key = "#userId", allEntries=true)
public DataResult cacheTest5(String userId,Integer age){
SysUser sysUser=new SysUser();
sysUser.setId(userId);
sysUser.setRealName("麋鹿-乔老大");
sysUser.setAge(6);
sysUser.setSex(1);
sysUser.setCreateTime(new Date());
log.info("=====sysUser:{}",sysUser);
return DataResult.success(sysUser);
}
/**
* 清除操作默认是在对应方法成功执行之后触发的,
* 即方法如果因为抛出异常而未能成功返回时也不会触发清除操作。
* 使用beforeInvocation可以改变触发清除操作的时间,
* 当我们指定该属性值为true时,Spring会在调用该方法之前清除缓存中的指定元素。
*/
@GetMapping("/cacheTest6")
@CacheEvict(value="test_user",key = "#userId", beforeInvocation=true)
public DataResult cacheTest6(String userId,Integer age){
SysUser sysUser=new SysUser();
sysUser.setId(userId);
sysUser.setRealName("幸运大佬-乌大神");
sysUser.setAge(18);
sysUser.setSex(1);
sysUser.setCreateTime(new Date());
log.info("=====sysUser:{}",sysUser);
return DataResult.success(sysUser);
}
}