为了统一各种缓存的接入流程,spring 设计了统一spring cache缓存模块,将所有操作进行了抽象,主要为Cache和CacheManager。
看一下spring 默认有以下几种实现。
CacheManager结构如下:
spring cache的默认实现为ConcurrentMapCache,下面我们修改默认实现,将缓存替换为redis,并详细演示了@Cacheable的使用。
首先看下该注解结构:
value、cacheNames :用来指定缓存组件的名字
key :缓存数据时使用的 key。默认是使用方法参数的值。可以使用 spEL 表达式来编写。
keyGenerator :key 的生成器。 key 和 keyGenerator 二选一使用
cacheManager :可以用来指定缓存管理器。从哪个缓存管理器里面获取缓存。
condition :可以用来指定符合条件的情况下才缓存
unless :否定缓存。当 unless 指定的条件为 true ,方法的返回值就不会被缓存。
sync :是否使用异步模式。
在采用默认配置的情况下,只需要引入redis的依赖即可,非常简单(创建spring boot项目此处不做介绍)。
在pom文件中添加以下redis依赖
org.springframework.boot
spring-boot-starter-data-redis
在配置文件中添加redis的地址等信息,根据自己的需求即可。
spring.redis.host=192.168.1.11
spring.redis.password=123456
spring.redis.port=6379
到此为止我们就把cache的默认实现换成redis了,下面我们写代码来验证以下。
@Service
public class CacheService {
@Cacheable("demo01")
public int demo01(int i){
System.out.println("=========》模拟查询数据库获取数据");
return i*i;
}
}
此处我们测试注解@Cacheable,value属性是必须指定的,其表示当前方法的返回值是会被缓存在哪个Cache上的,此处我们取名demo01。
编写好service后我们在编写一个controller,
@RestController
public class CacheController {
@Autowired
private CacheService cacheService;
@GetMapping("demo01/{number}")
public Object demo01(@PathVariable("number") int number) {
return cacheService.demo01(number);
}
}
启动类上开启缓存功能
@SpringBootApplication
@EnableCaching
public class SpringCacheApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(SpringCacheApplication.class, args);
}
}
我们来访问以下localhost:8080/demo01/2时,控制台的输出结果
当我们多次刷新界面后,会发现控制台没有任何输出,说明我们是从redis取的数据。
我们通过命令登录redis 看一下我们的数据是不是存在了redis中。
发现我们的数据已经存在redis中了。
但是我们看到的数据不是很友好,这是因为默认的序列化方式为jdk的序列化方式。
我们可以修改一下value的序列化方式。
我们加一个redis的配置文件RedisConfig.java
@Configuration
public class RedisConfig {
@Bean
public RedisCacheManagerBuilderCustomizer myRedisCacheManagerBuilderCustomizer() {
return (builder) -> builder
.withCacheConfiguration("demo01",
RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(RedisSerializationContext
.SerializationPair
.fromSerializer(RedisSerializer.json())));
}
}
重启服务后我们再来测试一下,这次我们输入4。
登录redis服务器后查看一下结果:
第二次我们输入的4,获取的结果为16,符合我们的序列化要求。
我们可以修改掉默认key的生产策略,比如我们要用 ""来作为我们自定义的key,i为我们方法的参数。
@Cacheable(value = "demo01",key = "'<'+ #i +'>'")
public int demo02(int i){
System.out.println("=========》模拟查询数据库获取数据");
return ++i;
}
我们来测试一下
登录redis 获取一下我们的key ,可以成功获取到我们的值2。
@Bean
public KeyGenerator myKeyGenerator(){
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
//方法名-第一个参数值
return method.getName()+"-"+params[0].toString();
}
};
}
在写个demo03的service和对应的controller
@Cacheable(value = "demo01",keyGenerator = "myKeyGenerator")
public int demo03(int i){
System.out.println("=========》模拟查询数据库获取数据");
return --i;
}
@GetMapping("demo03/{number}")
public Object demo03(@PathVariable("number") int number) {
return cacheService.demo03(number);
}
重启服务后我们测试下:
同样,登录redis 验证下我们的结果:
符合我们的预期,demo01 为缓存组件的名字,demo03-1 即为我们自定义的格式(方法名-第一个参数名)。
service代码
@Cacheable(value = "demo01",condition = "#i>8")
public int demo04(int i){
System.out.println("=========》模拟查询数据库获取数据");
return --i;
}
controller代码
@GetMapping("demo04/{number}")
public Object demo04(@PathVariable("number") int number) {
return cacheService.demo04(number);
}
在浏览器进行测试:
登录redis ,查看结果,正常情况下应该获取不到,因为我们的i=6,不满足我们的i>8的条件
结果符合预期,没有获取到值。
我们修改下i的值改为9,符合我们设置的大于8的条件,再来测试下
查看redis结果:
发现redis中已经缓存了我们的数据,结果符合我们设置的condition条件。
service代码:
@Cacheable(value = "demo01",unless = "#i>100")
public int demo05(int i){
System.out.println("=========》模拟查询数据库获取数据");
return --i;
}
controller代码:
打开浏览器测试:
输入88 ,不满足我们的unless条件,因此结果应该被缓存
登录redis查看:
确实,结果已经缓存了。
我们在来测试下102,正常满足unless条件,因此结果应该不会被缓存。
浏览器测试:
登录redis 查看缓存结果:
符合设置的unless条件,结果因为没有被缓存。
7. sync
是否使用异步模式。默认false,以同步的方式将方法返回的结果存在缓存中。
接下来会给大家补充上其他注解的详细用法。