目录
简述
使用内存等快速访问介质缓存热点数据,以此提高性能和增加吞吐量。
spring定义了CacheManager和Cache接口统一不同的缓存技术。其中CacheManager是Spring提供的各种缓存技术的抽象接口。而Cache接口包含缓存的各种操作,spring本身并没有提供缓存功能的实现(提供了list,map)。1
详见官方文档Cache Abstraction
注解
@EnableCaching
开启缓存
@Configuration
@EnableCaching
public class AppConfig {
}
Alternatively, for XML configuration you can use the cache:annotation-driven
element:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache https://www.springframework.org/schema/cache/spring-cache.xsd">
<cache:annotation-driven/>
</beans>
@Cacheable
触发缓存填充,即缓存中存在直接返回,否则执行具体实现获取数据并添加到缓存中。
- value、cacheNames:两个等同的参数,用于指定缓存存储的集合名。
- key:缓存对象的key值,非必需,缺省按照函数的所有参数组合作为key值,若自己配置需使用SpEL表达式,比如:
@Cacheable(key = “#p0”)
:使用函数第一个参数作为缓存的key值 - condition:缓存对象的条件,非必需,也需使用SpEL表达式,只有满足表达式条件的内容才会被缓存,比如:
@Cacheable(key = “#p0”, condition = “#p0.length() < 3”)
,表示只有当第一个参数的长度小于3的时候才会被缓存 - unless:另外一个缓存条件参数,非必需,需使用SpEL表达式。它不同于condition参数的地方在于它的判断时机,该条件是在函数被调用之后才做判断的,所以它可以通过对result进行判断。
- keyGenerator:用于指定key生成器,非必需。若需要指定一个自定义的key生成器,我们需要去实现
org.springframework.cache.interceptor.KeyGenerator
接口,并使用该参数来指定。需要注意的是:该参数与key是互斥的 - cacheManager:用于指定使用哪个缓存管理器,非必需。只有当有多个时才需要使用
- cacheResolver:用于指定使用那个缓存解析器,非必需。需通过
org.springframework.cache.interceptor.CacheResolver
接口来实现自己的缓存解析器,并用该参数指定。
@CacheEvict
触发缓存移除
- allEntries:非必需,默认为false。当为true时,会移除所有数据。如:@CachEvict(cacheNames=”test”,allEntries=true)
- beforeInvocation:非必需,默认为false,会在调用方法之后移除数据。当为true时,会在调用方法之前移除数据。如方法有异常则不移除缓存。
@CachePut
触发缓存更新
@Caching
灵活组合如何触发缓存
@CacheConfig
在类级别共享缓存的配置
样例
controller
@RestController
@RequestMapping("/cache")
public class CacheTestController {
@Autowired
CacheTestService cacheTestService;
@RequestMapping("get")
public SimpleResp get(int id) {
String value = cacheTestService.get(id);
return new SimpleResp(true, value + "," + System.currentTimeMillis());
}
@RequestMapping("update")
public SimpleResp update(int id, String value) {
cacheTestService.update(id, value);
return new SimpleResp(true, System.currentTimeMillis());
}
@RequestMapping("del")
public SimpleResp del(int id) {
cacheTestService.del(id);
return new SimpleResp(true, System.currentTimeMillis());
}
}
public class SimpleResp {
private final boolean success;
private final Object msg;
public SimpleResp(boolean success) {
this(success, "");
}
public SimpleResp(boolean success, Object msg) {
this.success = success;
this.msg= msg;
}
public boolean isSuccess() {
return success;
}
public Object getMsg() {
return msg;
}
}
service
@Service
public class CacheTestService {
static List<String> list = new ArrayList<>();
static {
list.add("a");
list.add("b");
list.add("c");
list.add("d");
}
@Cacheable(cacheNames = "test", key = "#p0")
public String get(int id) {
System.out.println("load from local====" + id);
return list.get(id);
}
@CachePut(cacheNames = "test", key = "#p0")
public String update(int id, String value) {
System.out.println("update local====" + id + "," + value);
list.add(id, value);
return value;
}
@CacheEvict(cacheNames = "test", key = "#p0")
public void del(int id) {
String value = get(id);
System.out.println("del local====" + id + "," + value);
list.remove(value);
}
}
main
@SpringBootApplication
@EnableCaching
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
application.yml
spring:
cache:
type: simple
测试
# 测试请求
~$ curl http://127.0.0.1:10010/cache/get?id=2
~$ curl http://127.0.0.1:10010/cache/update?id=2&value=test
~$ curl http://127.0.0.1:10010/cache/del?id=2
# 看控制台输出
注意
缓存失效
The default advice mode for processing caching annotations is proxy, which allows for interception of calls through the proxy only. Local calls within the same class cannot get intercepted that way. For a more advanced mode of interception, consider switching to aspectj mode in combination with compile-time or load-time weaving.
- <cache:annotation-driven/>有一个mode属性,可选值有proxy和aspectj。默认是使用proxy。当mode为proxy时,只有缓存方法在外部被调用的时候cache才会发生作用,如果一个缓存方法在其声明对象内部被调用时cache是不会发生作用的。而mode为aspectj时就不会有这种问题。
- 使用proxy时,只有public方法上的
@Cacheable
等标注才会起作用,如果需要非public方法上的方法也可以使用cache时把mode设置为aspectj。 - <cache:annotation-driven/>还可以指定一个proxy-target-class属性,表示是否要代理class,默认为false。我们前面提到的
@Cacheable
、@cacheEvict
等也可以标注在接口上,这对于基于接口的代理来说是没有什么问题的,但是需要注意的是当我们设置proxy-target-class为true或者mode为aspectj时,是直接基于class进行操作的,定义在接口上的@Cacheable等Cache注解不会被识别到,那对应的cache也不会起作用了。
detail you can see from cache-annotation-enable
<cache:annotation-driven/> looks for @Cacheable/@CachePut/@CacheEvict/@Caching only on beans in the same application context in which it is defined. This means that, if you put <cache:annotation-driven/> in a WebApplicationContext for a DispatcherServlet, it checks for beans only in your controllers, not your services. See the MVC section for more information.
<cache:annotation-driven/>只会去寻找定义在同一个ApplicationContext下的@Cacheable等缓存注解。
整合2
ConcurrentMap
Ehcache-based Cache
https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#cache-store-configuration-ehcache
Ehcache
Caffeine Cache
一个请求的生命周期里实现缓存
https://www.cnblogs.com/imyijie/p/11651679.html