简介
Spring Cache 是Spring - context-xxx.jar中提供的功能,可以结合EHCache,Redis等缓存工具使用。给用户提供非常方便的缓存处理,缓存基本判断等操作,可以直接使用注解实现。
在包含了Spring - context-xxx.jar的Spring Boot项目中,在启动类中添加@EnableCaching注解,即可开启缓存功能。默认Spring Cache是不开启。
加载缓存工具顺序
只要检测到项目中配置了下面缓存工具。(导入了依赖,在Spring容器中发现对应工具的内容),无论导入多少个缓存工具用于只用最前面的一个。
默认寻找缓存工具的顺序:(为什么redis配置上就可以用的原因)
-
Generic
-
JCache (JSR-107) (EhCache 3, Hazelcast, Infinispan, and others)
-
EhCache 2.x
-
Hazelcast
-
Infinispan
-
Couchbase
-
Redis
-
Caffeine
-
Simple
无参使用
-
pom.xml添加依赖
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.7.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> </dependencies> -
新建配置文件application.yml
spring: redis: host: 192.168.8.129 # cluster: # nodes: 集群时使用 # port: 6379 -
新建启动类
@SpringBootApplication @EnableCaching public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class,args); } } -
新建Service以及实现类
-
每个方法单独配置key前缀适用于整个类中方法的前缀不统一的情况。
-
@Cacheable表示要对方法返回值进行缓存。缓存时key的名称为cacheName::key
-
cacheNames : 配置key的前缀
-
key:配置key的后缀。里面字符串要使用单引号。
-
Spring Cache使用的RedisTemplate<StringRedisSerializer,JdkSerializationRedisSerializer>
public interface DemoService { String demo(); } @Service public class DemoServiceImpl implements DemoService { @Override // 固定字符串需要使用单引号 @Cacheable(key = "'demo'",cacheNames = "com.sxt") public String demo() { System.out.println("demo方法被执行"); return "demo-result"; } } -
统一配置类中方法前缀
public interface DemoService { String demo(); } @Service @CacheConfig(cacheNames = "com.sxt") public class DemoServiceImpl implements DemoService { @Override // 固定字符串需要使用单引号 @Cacheable(key = "'demo'") public String demo() { System.out.println("demo方法被执行"); return "demo-result"; } } -
新建控制器
@Controller public class DemoConteroller { @Autowired private DemoService demoService; @RequestMapping("/demo") @ResponseBody public String demo(){ return demoService.demo(); } }
-
带有参数的方法缓存
-
在@Cacheable的key属性中通过#参数名可以获取到方法参数。key中内容Spring EL,既然是表达式字符串要用单引号,没有被单引号包含的内容都表示变量。
-
注意:基本上当方法有参数时,设置key的时候需要添加上参数条件。因为参数不一样,方法的返回值也可以不一样了。
@Service @CacheConfig(cacheNames = "com.bjsxt") public class DemoServiceImpl implements DemoService { @Override // 固定字符串需要使用单引号 @Cacheable(key = "'demo'") public String demo() { System.out.println("demo方法被执行"); return "demo-result"; } @Override // Spring EL // 字符串使用单引号 // #+方法参数名称:可以调用方法参数 @Cacheable(key = "'selectById'+#id") public String selectById(Long id) { System.out.println("执行了selectById:"+id); return "selectById"+id; } }
返回值为对象或集合
-
这个时候需要更换Redis中Value对应的序列化器。更换序列化器后同时也会解决redis中数据前面出现乱码的问题。因为默认对Redis的value序列化器使用JdkSerializationRedisSerializer序列化器。
-
如果不更换也可以有效果, 但是实体类必须实现Serializable接口
-
新建配置类 ,注意:此配置方法影响是的Spring Cache,当Spring Cache使用Redis作为缓存工具时,key和value的序列化类型。如果项目中需要使用Spring Data Redis直接操作Redis,下面配置方法对操作时的key和value的序列化类型没有影响,还是默认的JDK序列化器,如果需要更改成GenericJackson2JsonRedisSeriablizer,还需要编写之前RedisTemplate实例配置方法
@Configuration public class RedisConfig { @Bean public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { //缓存配置对象 RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig(); redisCacheConfiguration = redisCacheConfiguration.entryTtl(Duration.ofMinutes(30L)) //设置缓存的默认超时时间:30分钟 .disableCachingNullValues() //如果是空值,不缓存 .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) //设置key序列化器 .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); //设置value序列化器 return RedisCacheManager .builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory)) .cacheDefaults(redisCacheConfiguration).build(); } } -
Spring Data Redis直接操作Redis , 默认对Redis的value序列化器使用JdkSerializationRedisSerializer序列化器。
@Configuration public class RedisConfig { @Bean public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){ RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setKeySerializer(new StringRedisSerializer()); // 一定要保证自己操作redis时value的序列化器类型和Spring Cache的value序列化器类型一样。 redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); redisTemplate.setConnectionFactory(factory); return redisTemplate; } } -
编写service
@Override @Cacheable(key = "'selectByUsername'+#id+':'+#username") public People selectByUsername(Long id,String username) { People people = new People(); people.setName(username); people.setId(id); return people; } @Override @Cacheable(key = "'selectAll'") public List<People> selectAll() { List<People> list = new ArrayList<>(); list.add(new People(1l,"张三")); list.add(new People(2l,"李四")); return list; } -
编写控制器
@RequestMapping("selectByUsername") public People selectByUsername(Long id,String username){ return demoService.selectByUsername(id,username); } @RequestMapping("/selectAll") public List<People> selectAll(){ return demoService.selectAll(); }
condition和unless属性
@Cacheable中的属性
-
condition是普通条件。如果条件成立则进行缓存。此判断为进入到方法体之前的判断,所以#result不允许用在这里。如果在condition中出现#result会导致条件恒不成立,不进行缓存。
-
unless是方法执行完成后的条件,当符合条件不被缓存。注意:#result只能写在这个属性中。多用在返回结果为null时不缓存效果。
-
演示为参数不等于5时才被缓存
@Override @Cacheable(key = "'selectById2'+#id",condition = "#id!=5") public People selectById2(Long id) { System.out.println("执行selectById2"); return null; } -
演示当方法返回值为null时不被缓存。
@Cacheable(key = "'selectById2'+#id",unless= "#result == null ") public String selectById2(Long id){ System.out.println("执行selectById2"); return null; }
删除缓存
使用@CacheEvict进行删除
-
可以删除固定的key的缓存数据
@CacheEvict(key = "'demo'") @Override public void delete() { System.out.println("执行delete"); } -
可以通过参数实现通过删除方法
方法参数key值表示要删除Redis中com.sxt::key这样的数据
@CacheEvict(key = "#key") @Override public int delete(String key) { System.out.println("执行了delete方法"); return 0; }
修改缓存
@CachePut和@Cacheable的区别
-
@Cacheable 如果发现有缓存数据,直接获取缓存数据。如果没有缓存数据,执行方法,把方法返回值进行缓存。
-
@CachePut 无论是否已经缓存,恒执行方法体,每次都会把方法返回值数据缓存到缓存工具中。根据@CachePut的特性,多用这个注解实现缓存修改。
// Cache 每次都走方法体,无论redis中是否存在指定key. // 恒执行redis的新增操作 @CachePut(key = "'update'") @Override public String update() { System.out.println("执行修改"); return "update2"; }
Spring Cache 的执行原理
-
Spring Cache扫描容器中包含了RedisCacheManager实例。将会使用Redis作为缓存工具。
-
在执行业务方法时,如果方法上包含@Cacheable将会判断是否存在key,如果存在key直接获取Redis中数据作为方法返回结果。如果不存在key则执行方法后,把方法返回值缓存到Redis中,最后返回返回值。
-
如果执行业务方法时,方法上面包含@CacheEvit将会在执行完成方法后删除缓存数据。
-
如果执业务方法时,方法上面包含@CachePut,每次都会执行方法体,并且把方法返回值缓存到Redis中。