前几天在spring中整合ehcache,整和很简单,就是遇到了一些问题,记录一下以免再次犯错。
这假设已经配置好了spring项目。以下是spring引入cache的主要配置。
spring cache 配置
spring boot 配置
如果使用 spring boot
, 开启缓存很方便
pom 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- ehcache -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
在启动类增加启用缓存注解@EnableCaching
@EnableCaching //启用缓存
配置
#配置ehcache缓存
spring.cache.type=ehcache
#指定ehcache配置文件
spring.cache.ehcache.config=classpath:ehcache/ehcache.xml
spring mvc 配置
我使用的是spring mvc
,配置如下
pom引入依赖
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.6</version>
<type>pom</type>
</dependency>
<!--Spring对于缓存功能的抽象封装接口-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.15.RELEASE</version>
</dependency>
在配置文件开启缓存,例如在 spring-context.xml中 添加以下内容
<beans xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<!-- 启用缓存注解功能 -->
<cache:annotation-driven cache-manager="ehcacheManager"/>
<!-- cacheManager工厂类,指定ehcache.xml的位置 -->
<bean id="ehcacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache/ehcache.xml" />
</bean>
<!-- 声明cacheManager -->
<bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="ehcacheManagerFactory" />
<property name="transactionAware" value="true"/>
</bean>
ehcache 配置文件
接下来,我们需要在resources目录下增加ehcache的配置文件,新建文件ehcache/ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd"
updateCheck="true" monitoring="autodetect"
dynamicConfig="true">
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxEntriesLocalHeap="10000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskSpoolBufferSizeMB="30"
maxEntriesLocalDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</defaultCache>
<cache name="MY_CACHE"
maxEntriesLocalHeap="50000"
maxEntriesLocalDisk="10000000"
eternal="false"
diskSpoolBufferSizeMB="60"
timeToIdleSeconds="3600"
timeToLiveSeconds="3600"
memoryStoreEvictionPolicy="LRU"
transactionalMode="off">
<persistence strategy="localTempSwap"/>
</cache>
</ehcache>
缓存测试
新建一个service,并在方法中使用Cacheable
注解,指定key就可以了。
@Service
public class CacheService {
@Cacheable(value = "MY_CACHE" , key= "#name")
public String testCache(String name) {
System.out.println("缓存方法执行!");
return "Hello " + name;
}
}
测试缓存方法
@Test
public void test () {
cacheService.testCache("jam");
cacheService.testCache("jam");
}
结果发现,使用了缓存,方法只输出一次,如果把@Cacheable注解注释掉,发现方法调用了两次。
要注意的问题
缓存service注入不要与mvc的Controller配置文件一起注入
由于 spring缓存 是使用aop 实现的,所有要确保cache的bean的aop是生效的,我遇到的问题是打包成jar包好,在另一个项目中调用缓存没启用。
排查后发现 新的项目中beans注入了两次,第一次是在注入mcv的Controller中注入,没有aop,然后才是另个一配置文件的beans注入,但是单例模式第二次不生效。
项目中有 spring-mvc.xml 和 spring-context.xml 配置文件,mvc配置如下
<context:component-scan base-package="com.sf.pass">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
查找资料后发现 include-fliter 这个是白名单,默认会加载service等一些其他的beans。spring配置中的use-default-filters用来指示是否自动扫描带有@Component、@Repository、@Service和@Controller的类。默认为true,即默认扫描
<context:component-scan base-package="com.sf.pass" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
参考 https://blog.51cto.com/9855084/1961155
缓存返回的对象是共享变量,注意并发问题,修改要慎重
调用缓存方法返回的数据是共享的,请尽量不要进行修改。例如缓存方法返回了一个集合,如果对进行进行增删改,那么会直接影响了缓存的内容,这样会导致很多问题。
第一,如果是并发环境,可能会有并发修改异常。
第二,对缓存对象进行修改,会直接影响缓存,导致下次相同参数获取的结果不一致。
第三,修改了缓存的数据,如果缓存过期了,那么重新获取的结果就会不一样了。
例如,我们把之前的方法修改一下,返回一个list
@Cacheable(value = CacheConstants.CACHE_NANE_CVY_CACHE , key= "#name")
public List testCache(String name) {
return new ArrayList(10);
}
然后使用测试方法,在返回的list进行修改
@Test
public void test () {
for (int i = 0; i < 10; i++) {
List list = CacheService.testCache("jam");
list.add(1);
System.out.println(list.size());
}
}
发现缓存返回的list改变了,size不断增加。
缓存的key拼接
通常我们需要在缓存加上前缀,以便是不同类型的缓存,例如用户和产品都有id,如果想放在同一个缓存里,需要在缓存增加前缀等标识,例如用户key可能是 user_id, 产品key是prod_id,我们可以这样增加前缀 ,其中id是参数
String CACHE_KEY_USER= "'USER_'+#id"
当然key的组合还有很多中方式。可以参考下面的地址:
https://blog.csdn.net/syani/article/details/52239967
https://segmentfault.com/a/1190000016961342
缓存相关的注解
- @
Cacheable
triggers cache population - @
CacheEvict
triggers cache eviction - @
CachePut
updates the cache without interfering with the method execution - @
Caching
regroups multiple cache operations to be applied on a method - @
CacheConfig
shares some common cache-related settings at class-level
相关资料
Spring cache 相关的官方文档,有问题建议之间看官方文档
https://docs.spring.io/spring/docs/4.3.x/spring-framework-reference/html/cache.html
欢迎关注我的公众号