Spring自身并没有实现缓存解决方案,但是对缓存管理功能提供了声明式的支持,能够与多种流行的缓存实现进行集成。
Spring Cache是作用在方法上的(不能理解为只注解在方法上),其核心思想是:当我们在调用一个缓存方法时会把该方法参数和返回结果作为一个键值存放在缓存中,等到下次利用同样的参数调用该方法时将不再执行该方法,而是直接从缓存中获取结果进行返回。所以在使用Spring Cache的时候我们要保证我们的缓存的方法对于相同的方法参数要有相同的返回结果。
①适合
[1]经常被查询,很少或几乎不修改的[2]不是特别重要,允许出现偶尔的并发问题[3]不会被其他应用程序修改的
②不适合
[1]经常被修改[2]特别重要,不允许出现任何的并发问题,例如:财务数据[3]有可能被其他应用程序修改
Res getResByServletPath(String servletPath);
EngageService.PageInfo<Survey> getSurveyPage(Integer userId, boolean completed, Integer pageNum);EngageService.Survey getSurveyDeeply(Integer surveyId);
try {//1.尝试从缓存中获取数据Object value = cacheMap.get(key);//2.判断value是否为nullif(value == null){//3.实际执行目标方法value = 目标对象.目标方法();//4.将目标方法执行结果存入缓存cacheMap.put(key,value);}//5.返回valuereturn value;}catch(Exceptin e){}
int count = userService.getRegistUserCount(boolean active);
Cache Abstraction
①创建键生成器类,实现org.springframework.cache.interceptor.KeyGenerator接口
public class UserKeyGenerator implements KeyGenerator { //命名方式:类名.方法名.参数名。。。 @Override public Object generate(Object target, Method method, Object... params) { //target:目标对象 //method:目标方法 //params:实参数组 StringBuilder bulider = new StringBuilder(); //获取目标对象名字 String name = target.getClass().getName(); bulider.append(".").append(name); //获取方法的名字 String methodName = method.getName(); bulider.append(".").append(methodName); //获取所有的参数 if(params!=null && params.length!=0){ for (int i = 0; i < params.length; i++) { // Object object = params[i]; bulider.append(".").append(params[i]); } } String key = bulider.substring(1); System.out.println(key); return key; } }
2)需要在Spring配置文件中配置对应的bean
<bean id="userKeyGenerator" class="com.lamsey.survey.Ehcache.UserKeyGenerator"/>
[1]加入jar包[2]引入EHCache自身的配置文件,同时创建一个具名的缓存区域
<!-- Ehcache依赖 -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.3</version>
</dependency>
</dependencies>
<!-- spring 整合ehcache --> <!-- 自定义key生成器 --> <bean id="userKeyGenerator" class="com.lamsey.survey.Ehcache.UserKeyGenerator"/> <!-- 配置 EhCacheManagerFactoryBean工厂--> <bean id="ehCacheManagerFactoryBean" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" > <property name="configLocation" value="classpath:ehcache.xml"></property> </bean> <!-- 配置EhCacheCacheManager --> <bean id="ehCacheCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" > <property name="cacheManager" ref="ehCacheManagerFactoryBean"></property> </bean> <!--切面及切面表达式配置 --> <aop:config> <!-- 利用切面表达式找到切面切入点,进行切面编程 --> <aop:pointcut expression="execution(* *..ResService.getResByServletPath(String)) or execution(* *..AnswerService.getSurveyPage(Integer, boolean, Integer)) or execution(* *..AnswerService.getSurveyDeeply(Integer)) or execution(* *..SurveyService.completedSurvey(Integer))" id="cachePointCut" /> <!-- 承上启下,得到切入点,同时连接处理的方法。对切入点进行处理(cache) -->
<!-- 缓存切面优先级高于数据库事务切面优先级 -->
<aop:advisor advice-ref="cacheAdvice" pointcut-ref="cachePointCut" order="1"/> </aop:config> <!-- 对切入点进行处理,这里表现为缓存 --> <!-- 这里的自定义key【className.method.param1..paramn】 --> <cache:advice id="cacheAdvice" cache-manager="ehCacheCacheManager" key-generator="userKeyGenerator"> <!-- 在cache属性中指定缓存区域的名称 --> <!-- 指定要使用缓存的具体方法,要求必须是缓存切入点覆盖范围内的方法 --> <cache:caching cache="surveyCache"> <cache:cacheable method=" getResByServletPath" /> <cache:cacheable method="getSurveyDeeply"/> </cache:caching> <!-- 使用另外一个有可能被清空数据的缓存区域 --> <cache:caching cache="surveyCacheEvicable"> <cache:cacheable method="getSurveyPage" /> <!-- 执行updateSurveyCompleted方法时清空当前缓存区域 --> <!-- 因为调查有可能更新,当更新后就需要进行重新获取参与调查 ,所以清空该缓存--> <cache:cache-evict method="completedSurvey" all-entries="true" /> </cache:caching> </cache:advice>
ehcache.xml(从hibernate复制出来)
<ehcache> <!-- Sets the path to the directory where cache .data files are created. If the path is a Java System Property it is replaced by its value in the running VM. The following properties are translated: user.home - User's home directory user.dir - User's current working directory java.io.tmpdir - Default temp file path --> <!-- 默认以内存作为缓存,但如果不够,在这里设置一个电脑目录存储 --> <diskStore path="java.io.tmpdir"/> <!--Default Cache configuration. These will applied to caches programmatically created through the CacheManager. The following attributes are required for defaultCache: maxInMemory - Sets the maximum number of objects that will be created in memory eternal - Sets whether elements are eternal. If eternal, timeouts are ignored and the element is never expired. timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used if the element is not eternal. Idle time is now - last accessed time timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used if the element is not eternal. TTL is now - creation time overflowToDisk - Sets whether elements can overflow to disk when the in-memory cache has reached the maxInMemory limit. --> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" /> <!--Predefined caches. Add your cache configuration settings here. If you do not have a configuration for your cache a WARNING will be issued when the CacheManager starts The following attributes are required for defaultCache: name - Sets the name of the cache. This is used to identify the cache. It must be unique. maxInMemory - Sets the maximum number of objects that will be created in memory eternal - Sets whether elements are eternal. If eternal, timeouts are ignored and the element is never expired. timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used if the element is not eternal. Idle time is now - last accessed time timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used if the element is not eternal. TTL is now - creation time overflowToDisk - Sets whether elements can overflow to disk when the in-memory cache has reached the maxInMemory limit. --> <!-- Sample cache named sampleCache1 This cache contains a maximum in memory of 10000 elements, and will expire an element if it is idle for more than 5 minutes and lives for more than 10 minutes. If there are more than 10000 elements it will overflow to the disk cache, which in this configuration will go to wherever java.io.tmp is defined on your system. On a standard Linux system this will be /tmp" --> <cache name="surveyCache" maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="300" timeToLiveSeconds="600" overflowToDisk="true" /> <!-- Sample cache named sampleCache2 This cache contains 1000 elements. Elements will always be held in memory. They are not expired. --> <cache name="surveyCacheEvicable" maxElementsInMemory="1000" eternal="true" timeToIdleSeconds="0" timeToLiveSeconds="0" overflowToDisk="false" /> --> <!-- Place configuration for your caches following --> </ehcache>
<!-- 缓存切面优先级高于数据库事务切面优先级 -->
缓存切面在外层,事务切面在内层
结论:为了减少不必要的事务操作让缓存切面的优先级高于事务切面的优先级。
7.配置二级缓存
①创建键生成器,实现KeyGenerator接口
②引入EHCache环境
[1]引入EHCache依赖
[2]引入EHCache自身配置文件
[3]在EHCache中创建两个具名的缓存区域
(1)不清空的
(2)可清空的
③在Spring配置文件中配置缓存抽象和EHCache的整合
[1]配置缓存管理器工厂
[2]配置缓存管理器
[3]配置缓存切面的切入点表达式
execution(* *..ResService.getResByServletPath(String)) or
execution(* *..EngageService.getSurveyPage(..)) or
execution(* *..EngageService.getSurveyDeeply(..)) or
execution(* *..SurveyService.updateSurveyCompleted(..))
[4]配置缓存切面的通知:装配缓存管理器、装配键生成器、指定id
引用具体的缓存区域
用cache:cacheable标签指定要缓存数据的方法
用cache:cache-evict标签指定导致缓存清空的方法
[5]用aop:advisor将切入点表达式和缓存通知关联起来
④设置切面优先级让缓存切面优先级高于事务切面