Spring自带缓存
在不使用Redis等缓存组件时,Spring默认的缓存机制,是通过一个ConcurrentMapCacheManager,来获取ConcurrentMapCache类型的缓存组件,缓存数据都存放在ConcurrentHashMap中。
主要注解:
- @EnableCaching:标注在主启动类上,表示开启缓存
- @Cacheable:标注在方法上,被标注的方法会使用缓存
- @CacheEvict
- @CachePut
@EnableCaching
@SpringBootApplication
@EnableCaching // 直接标注在主启动类上即可
public class StudySpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(StudySpringBootApplication.class, args);
}
}
@Cacheable
标注了@Cacheable注解后,当执行到标记的方法时,会先去查询Cache,按照cacheNames/value指定的名字获取到缓存(通过CacheManager,第一次获取缓存时,如果没有Cache组件,会自动创建一个对应的缓存组件),然后到缓存中查询内容,通过一个key来查询(key默认是方法的入参);如果从缓存中找到了对应的值,就直接返回缓存中存放的值,而不调用方法,否则就调用方法,并将该方法的返回值放入缓存中。
@Service
public class EmployeeService {
@Autowired
private EmployeeMapper employeeMapper;
// @Cacheable
@Cacheable(value = "getEmp")
public Employee getEmployeeById(Integer id) {
return employeeMapper.getEmpById(id);
}
public void updateEmployee(Employee employee) {
employeeMapper.updateEmployee(employee);
}
}
Cacheable各个属性解析
Cacheable源码:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable {
@AliasFor("cacheNames")
String[] value() default {}; // 同cacheNames,表示当前cache组件的名字,数组形式,可以存放多个name
@AliasFor("value")
String[] cacheNames() default {}; // 同value
String key() default ""; // 缓存数据时使用的key,默认是入参,可以修改,通过SpEL表达式来指定
String keyGenerator() default ""; // key生成器,配置它可以指定key的生成格式(与key两者选一个用)
String cacheManager() default ""; // 指定使用哪个缓存管理器
String cacheResolver() default ""; // 指定使用哪个缓存解析器
String condition() default ""; // 表示缓存在什么条件下生效,通过SpEL表达式配置
String unless() default ""; // 表示缓存在什么情况下不生效,通过SpEL表达式配置
boolean sync() default false; // 表示是否使用异步模式
}
具体使用各个属性
①key
// 这里root.methodName表示当前方法名,#id表示传参中的属性名为id的值
@Cacheable(value = {"getEmp"}, key = "#root.methodName+'['+#id+']'")
public Employee getEmployeeById(Integer id) {
return employeeMapper.getEmpById(id);
}
通过打断点可以看到,在ConcurrentMapCache中,lookup方法中的key已经称为了设置的结果:方法名+[id]
②keyGenerator
// keyGenerator中的值是注册入容器的KeyGenerator实例的id
@Cacheable(value = {"getEmp"},keyGenerator = "keyGenerator")
public Employee getEmployeeById(Integer id) {
return employeeMapper.getEmpById(id);
}
注册的KeyGenerator:
@Configuration
public class CacheConfiguration {
@Bean("keyGenerator")
public KeyGenerator getKeyGenerator() {
return new KeyGenerator() {
/**
* method代表调用的方法,objects是入参数组
*/
@Override
public Object generate(Object o, Method method, Object... objects) {
return method.getName() + "--" + Arrays.asList(objects);
}
};
}
}
③condition与unless
condition:在表达式满足时,触发缓存机制
unless:当表达式满足时,就不会触发缓存机制
@Cacheable(value = {"getEmp"},keyGenerator = "keyGenerator",condition = "#id>1",unless = "#id==3")
public Employee getEmployeeById(Integer id) {
return employeeMapper.getEmpById(id);
}
使用上面的代码后,可以发现当浏览器中发送的id<=1或id ==3时,不会触发缓存,每一次请求都会查询数据库;而满足id>1且id!=3时,就会触发缓存,不会重复查询数据库。
@CachePut
@CachePut :既调用方法,又更新了缓存
通常用于修改了数据库的数据后,更新缓存中的数据
执行顺序:
1、调用方法
2、将方法的返回值放入缓存中
想要更新数据库时,让标注了@Cacheable的查询方法的缓存也更新,需要:
1、让@CachePut和 @Cacheable的value值相同,
2、key也要相同:这里可以用#employee.id 或 #result.id
(result就代表方法的返回值,但是在@Cacheable中不能使用result,
因为Cacheable要先查询缓存,而此时还没有返回值,这样就无法查询)
@CachePut(value = "emp",key = "#result.id")
public Employee updateEmployee(Employee employee) {
employeeMapper.updateEmployee(employee);
return employee;
}
在value和key值和前面的标注了@Cacheable注解的查询方法一致的时候,可以发现每一次更新数据的时候,查询中的缓存也被更新了,查询方法不需要执行就可以根据更新后的缓存得到正确的数据。
@CacheEvict
@CacheEvict 每一次调用时删除value缓存中对应key的缓存内容
注解中的特殊属性:
1、allEntries:
默认false:表示每次触发清理时清理key对应的缓存数据;
设置为true:表示每一次清理缓存都会清理全部的缓存,也就删除了其他key的缓存数据
2、beforeInvocation:
默认false:表示在方法体执行完后,才会进行缓存清理的操作
设置为true:表示在方法执行前执行缓存清理操作
作用:设置为true时,如果方法内部出现了异常,也会完成缓存清理的操作,否则不会进行
@CacheEvict(value = "emp",key = "#id",allEntries = true,beforeInvocation = true)
public void deleteEmployee(Integer id) {
employeeMapper.deleteEmployee(id);
}
@Caching
@Caching注解标注在方法上,可以用来进行相对复杂的缓存操作,如下面,将同一个数据缓存入多种格式的key中
@Caching( // 可以使用@Caching来进行复杂的缓存操作,如下
cacheable = { // 指定一个或多个Cacheable规则
@Cacheable(value = "emp", key = "'lastName:'+#lastName")
},
put = { // 指定一个或多个CachePut规则
@CachePut(value = "emp", key = "#result.id"),
@CachePut(value = "emp", key = "'email:'+#result.email")
}
)
public Employee getEmployeeByLastName(String lastName) {
return employeeMapper.getEmployeeByLastName(lastName);
}
@Caching源码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Caching {
Cacheable[] cacheable() default {};
CachePut[] put() default {};
CacheEvict[] evict() default {};
}
可以看到除了指定cacheable和put之外,还可以指定evict来进行缓存清理的操作。
@CacheConfig
@CacheConfig标注在类上,用来抽取该类中缓存的公共配置(如标注了cacheNames后,下面的方法上就会默认采用这个值作为cacheName【同value】,可以减少冗余代码)
@Service
@CacheConfig(cacheNames = "emp")
public class EmployeeService {
// ... ...
}
@CacheConfig源码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CacheConfig {
String[] cacheNames() default {};
String keyGenerator() default "";
String cacheManager() default "";
String cacheResolver() default "";
}
可以看到可以设置以上的公共信息