前两天解决了一个Spring缓存的问题,因此就想到记录一下spring-boot缓存的使用。
SpringBoot开启缓存也容易,在启动类上添加 @ EnableCaching 注解就可以了,不需要过多的配置。此时开启的缓存是比较简单的缓存,即基于ConcurrentHashMap实现的缓存。虽然简单,但对于负载不高的应用也足够用了。
SpringBoot缓存两个关键类是: CacheAutoConfiguration 和 CacheAspectSupport 。这两个类分别位于spring-boot-autoconfigure和spring-context包。也就是说不使用 spring-boot-starter-cache 包也能在spring-boot中使用简单的缓存。
在 CacheType 类中可以看到SpringBoot支持的缓存类型。懒得一个一个介绍了,直接看下代码好了:
public enum CacheType { /** * Generic caching using 'Cache' beans from the context. */ GENERIC, /** * JCache (JSR-107) backed caching. */ JCACHE, /** * EhCache backed caching. */ EHCACHE, /** * Hazelcast backed caching. */ HAZELCAST, /** * Infinispan backed caching. */ INFINISPAN, /** * Couchbase backed caching. */ COUCHBASE, /** * Redis backed caching. */ REDIS, /** * Caffeine backed caching. */ CAFFEINE, /** * Simple in-memory caching. */ SIMPLE, /** * No caching. */ NONE }
可以看到,支持了差不多所有的主流缓存。
spring boot的缓存主要是由注解支持实现的。下面是几个常见的注解:
@ CacheEvict 注解的属性大致同 @ Cacheable 一致,不过 @ CacheEvict 还有两个独有的属性:
@ CacheConfig 注解的属性 @ Cacheable 都有。因为 @ CacheConfig 注解的作用本来就是统一管理相同类下所有方法的缓存配置。
需要提一下的就是在spring4.1之前是没有 @ CacheConfig 注解的,那时候需要在每个方法的 @ Cacheable 注解中设置 cacheNames 属性。
下面简单介绍下SpEL表达式的语法——看下表的,摘自Spring官方文档:
名称 | 位置 | 描述 | 示例 |
---|---|---|---|
methodName | root对象 | 当前被调用的方法名 | #root.methodname |
method | root对象 | 当前被调用的方法 | # root.method.name |
target | root对象 | 当前被调用的目标对象实例 | #root.target |
targetClass | root对象 | 当前被调用的目标对象的类 | #root.targetClass |
args | root对象 | 当前被调用的方法的参数列表 | #root.args[0] |
caches | root对象 | 当前方法调用使用的缓存列表 | #root.caches[0].name |
Argument Name | 执行上下文 | 当前被调用的方法的参数,如findArtisan(Artisan artisan),可以通过#artsian.id获得参数 | # artsian.id |
result | 执行上下文 | 方法执行后的返回值(仅当方法执行后的判断有效,如 unless cacheEvict的beforeInvocation=false) | #result |
注意事项:
类型 | 运算符 |
---|---|
关系 | <,>,<=,>=,==,!=,lt,gt,le,ge,eq,ne |
算术 | +,- ,* ,/,%,^ |
逻辑 | &&,||,!,and,or,not,between,instanceof |
条件 | ?: (ternary),?: (elvis) |
正则表达式 | matches |
其他类型 | ?.,?[…],![…],^[…],$[…] |
前面的配置,即只在在启动类上添加 @ EnableCaching 注解的情形下,我们只能使用最基础的 SIMPLE 缓存。现在我们需要尝试使用Caffeine缓存,就需要再多做些配置了。
在做这个配置的时候遇到了些问题,所以在这里也简单记录下解决问题的过程。
首先,需要在配置文件中指明使用Caffine缓存并添加Caffeine缓存的配置:
spring: cache: type: CAFFEINE caffeine: spec: initialCapacity=1048576,maximumSize=1073741824,expireAfterAccess=10m
然后加入Caffeine缓存依赖:
<dependency> <groupId>com.github.ben-manes.caffeine</groupId> <artifactId>caffeine</artifactId> </dependency>
然而此时调用方法会发现启动失败,报错如下:
Caused by: java.lang.IllegalArgumentException: No cache manager could be auto-configured, check your configuration (caching type is 'CAFFEINE') at org.springframework.util.Assert.notNull(Assert.java:215) at org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration$CacheManagerValidator.afterPropertiesSet(CacheAutoConfiguration.java:107) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1837) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1774) ... 42 common frames omitted
提示缺少CacheManager,说实话一开始看到这个错误提示我是不知咋回事儿的。
将spring-boot的debug配置设置为true,再重新执行,会有如下提示:
CaffeineCacheConfiguration: Did not match: - @ConditionalOnClass did not find required classes 'com.github.benmanes.caffeine.cache.Caffeine', 'org.springframework.cache.caffeine.CaffeineCacheManager' (OnClassCondition)
提示Caffeine缓存的自动配置需要两个类 com . github . benmanes . caffeine . cache . Caffeine 和 org . springframework . cache . caffeine . CaffeineCacheManager 。前者在引入caffeine的jar以后已经不是问题,后者则需要引入新的依赖。
CaffeineCacheManager 这个类位于spring-context-support中,我们可以直接引入这个依赖,不过更推荐通过spring-boot-starter-cache来间接引入,这样指向性会更明确一些。
spring-boot-starter-cache依赖如下:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency>
添加spring-boot-starter-cache依赖后再次测试,应用启动正常,缓存正常。
如需引入其他类型的缓存,也可以按类似的步骤来进行配置。
就这样。写了个示例应用,放在github了,有需要可以看一下: spring-boot-cache
噢,对了,Caffeine缓存在spring-boot下有个坑,以前踩到过,记录在这里了: 《spring caffeine缓存慎用weakKeys》