一、需求分析
调查问卷中或许每一个单击动作都会引发大量的数据库访问,特别是在参与调查的过程中,只是单击“上一页”或者“下一页”的按钮就会引发大量的查询,必须对这种问题进行优化才行。使用缓存策略进行查询缓存是降低数据库压力非常理想的方法,这里最起码能够有两种缓存方式:
1.使用hibernate的二级缓存。
2.使用spring自带的缓存模块进行查询缓存。使用spring自带的缓存模块功能必须要满足一下条件:
(1)spring版本必须至少在3.1或以上,这里使用了spring3.1
(2)必须要使用第三方缓存厂商的缓存支持,这里使用ehcache
这里使用spring自带的缓存模块进行查询缓存。
二、集成spring的缓存模块实现对Service缓存代理,降低数据库压力
1.引入类库
[spring功能模块]
spring-modules-cache.jar
[第三方缓存供应商]
backport-util-concurrent.jar
com.springsource.org.apache.commons.logging-1.1.1.jar
ehcache-1.5.0.jar
jsr107cache-1.1.jar
2.配置ehcache.xml配置文件,指定缓存的过期策略
1 <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
2 <diskStore path="java.io.tmpdir"/>
3 <defaultCache
4 maxElementsInMemory="10000"
5 eternal="false"
6 timeToIdleSeconds="120"
7 timeToLiveSeconds="120"
8 overflowToDisk="true"
9 maxElementsOnDisk="10000000"
10 diskPersistent="false"
11 diskExpiryThreadIntervalSeconds="120"
12 memoryStoreEvictionPolicy="LRU"
13 />
14 <!-- 自定义一个缓存策略 -->
15 <cache name="surveyCache"
16 maxElementsInMemory="10000"
17 eternal="false"
18 timeToIdleSeconds="120"
19 timeToLiveSeconds="120"
20 overflowToDisk="true"
21 maxElementsOnDisk="10000000"
22 diskPersistent="false"
23 diskExpiryThreadIntervalSeconds="120"
24 memoryStoreEvictionPolicy="LRU"
25 />
26 </ehcache>
上面配置中的属性意思都比较简单,很容易理解,不赘述。
3.通过配置文件配置spring的缓存模块
首先,必须必须引入缓存的命名空间才行,由于eclipse中并没有自带该命名空间相关信息,所以需要外部引入xsd约束,引入方式:
Window->preferences->XML->XML catalog->Add找到spring-cache-3.1.xsd文件添加进来即可,由于对应的location已经失效,所以建议使用本地文件协议file:///替代http协议。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/aop file:///D:\程序\java\Spring\spring-framework-4.2.1\spring-framework-4.2.1.RELEASE\schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/beans file:///D:\程序\java\Spring\spring-framework-4.2.1\spring-framework-4.2.1.RELEASE\schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/cache file:///D:\程序\java\Spring\spring-framework-4.2.1\spring-framework-4.2.1.RELEASE\schema\cache/spring-cache-3.1.xsd
http://www.springframework.org/schema/tx file:///D:\程序\java\Spring\spring-framework-4.2.1\spring-framework-4.2.1.RELEASE\schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context file:///D:\程序\java\Spring\spring-framework-4.2.1\spring-framework-4.2.1.RELEASE\schema/context/spring-context-2.5.xsd">
(1)ehcache工厂配置
1 <!-- ehcacheManager工厂配置 -->
2 <bean id="ehcacheManager"
3 class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
4 <property name="configLocation" value="classpath:ehcache.xml"></property>
5 </bean>
(2)ehcache管理器配置
1 <!-- ehcache管理器 -->
2 <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
3 <property name="cacheManager" ref="ehcacheManager"></property>
4 </bean>
(3)配置key生成器,这里需要自定义一个key生成器类,先存档,之后详解
<!-- Key生成器 ,先存档 -->
<bean id="surveyKeyGenerator" class="com.kdyzm.cache.SurveykeyGenerator" />
(4)定义缓存通知,该配置的作用就是确定针对什么样的方法进行缓存,针对什么样的方法清空缓存。
1 <!-- 定义缓存通知 -->
2 <cache:advice id="cacheAdvice" cache-manager="cacheManager"
3 key-generator="surveyKeyGenerator"><!-- 这里需要配置key生成器 -->
4 <cache:caching cache="surveyCache">
5 <!-- 设置缓存的方法 -->
6 <cache:cacheable method="get*" />
7 <cache:cacheable method="load" />
8 <cache:cacheable method="find*" />
9 <cache:cacheable method="is*" />
10
11 <!-- 设置需要清空缓存的方法 -->
12 <cache:cache-evict method="save*" all-entries="true" />
13 <cache:cache-evict method="update*" all-entries="true" />
14 <cache:cache-evict method="delete*" all-entries="true" />
15 <cache:cache-evict method="batch*" all-entries="true" />
16 <cache:cache-evict method="create*" all-entries="true" />
17 <cache:cache-evict method="new*" all-entries="true" />
18 </cache:caching>
19 </cache:advice>
(5)配置缓存AOP,创建切入点表达式,确定缓存对象的范围
为了将缓存通知首先启动,将其order属性配置为0(并不是因为0才首先启动,只是相对于日志通知和事务通知来说其order属性值最小)。
1 <aop:config>
2 <!-- 日志切入点 -->
3 <aop:pointcut
4 expression="(execution(* *..*Service.save*(..))
5 or execution(* *..*Service.update*(..))
6 or execution(* *..*Service.delete*(..))
7 or execution(* *..*Service.batch*(..))
8 or execution(* *..*Service.create*(..))
9 or execution(* *..*Service.new*(..))) and !bean(logService)"
10 id="loggerPointcut" />
11 <aop:pointcut expression="execution(* *..*Service.*(..))"
12 id="txPointcut" />
13 <!-- 必须配置order属性,使用该属性可以改变配置的通知的加载顺序,order值越大,优先级越高 必须让事务的通知放到后面,让日志的通知先执行,这样才能在执行完成日志的通知后事务确保能够结束。
14 order值越小,优先级越高 为了解决事务没有结束的问题,必须同时修改解除绑定的时间 -->
15 <aop:advisor advice-ref="cacheAdvice"
16 pointcut="execution(* com.kdyzm.service.SurveyService.*(..)) or
17 execution(* com.kdyzm.service.PageService.*(..)) or
18 execution(* com.kdyzm.service.QuestionService.*(..)) or
19 execution(* com.kdyzm.service.AnswerService.*(..))" order="0" />
20 <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"
21 order="2" />
22 <aop:aspect id="loggerAspect" ref="logger" order="1">
23 <aop:around method="record" pointcut-ref="loggerPointcut" />
24 </aop:aspect>
25 </aop:config>
这里对调查相关的所有Service中的相关方法都进行了代理缓存。
4.如何自定义key生成器
配置文件中有一个key生成器的配置,该配置的作用就是根据当前执行的方法环境(方法名、参数列表、类名)计算出来唯一的标识符(key值),如果该标识符已经存在,则缓存管理器就会调用缓存中的数据否则就执行目标方法并将目标方法的查询结果缓存起来。
1 package com.kdyzm.cache;
2
3 import java.lang.reflect.Method;
4
5 import org.springframework.cache.interceptor.KeyGenerator;
6
7 /**
8 * 自定义key生成器
9 * @author kdyzm
10 *
11 */
12 public class SurveykeyGenerator implements KeyGenerator{
13
14 //什么对象调用的什么方法,参数列表是什么
15 @Override
16 public Object generate(Object arg0, Method arg1, Object... arg2) {
17 String targetCode=arg0.getClass().getSimpleName()+"["+arg0.hashCode()+"]"; //arg0是类
18 String methodName=arg1.getName(); //arg1是方法
19 if(arg2!=null){ //arg2是参数列表
20 StringBuffer stringBuffer=new StringBuffer();
21 stringBuffer.append("(");
22 for(int i=0;i<arg2.length;i++){
23 stringBuffer.append(arg2[i].toString());
24 stringBuffer.append(",");
25 }
26 stringBuffer.append(")");
27 targetCode= targetCode+"."+methodName+stringBuffer.toString();
28 System.out.println(targetCode);
29 return targetCode;
30 }
31 targetCode= targetCode+"."+methodName+"()";
32 System.out.println(targetCode);
33 return targetCode;
34 }
35 }
三、测试效果
1.在主页上单击参与调查,查看后台
清空控制台,重新单击一次“参与调查”超链接,再次查看后台,这时候就会发现没有SQL语句发出了(手要快,不要超过120秒,之前配置的ehcach.xml配置文件中声明了如果超过120s没用的话就会清空缓存,所以还是会发出SQL查询语句)
2.测试参与调查的过程中的缓存效果。
这里需要创建多个调查页,在翻页的过程中查看缓存的效果,这里我的调查1中一共有三页。
(1)单击调查1,依次单击下一页,直到最后一页,观察控制台打印,发现不断地有SQL查询发出
(2)单击上一页、直到第一页,还是发现不断的有查询发出。
(3)从第一页单击“下一页”直到最后一页,发现不再有查询发出,单击上一页还是没有发现有SQL语句发出,证明spring缓存配置成功。