前言:
本贴复制来源:http://www.blogjava.net/xmatthew/archive/2010/05/archive/2010/04/22/319146.html
本文档将讲解一下,如何借助Spring Module项目,实现配置声明性缓存功能。

声明式缓存配置有效地将缓存功能从应用程序的核心需求中分离出来,解决了上文提及的众多问题,还带来了以下优点:
更明晰的责任分离。系统中的模块仅负责其核心需求,不再负责缓存功能。这将带来更出色的可追溯性。
更高的模块化程度。将缓存功能从核心模块中分离出来减少了重复代码(支持“一次且仅一次”原则),并有助于避免代码混乱。
设计决策的后期绑定。使用声明式缓存,开发人员可将与缓存实现和调优有关的决策制订延后。开发人员可将精力集中在应用程序的当前核心需求上。声明式配置支持 YAGNI(“you aren't gonna need it”的缩写,意为“您将不会需要它)原则,仅在开发人员确实需要的时候才允许他们为应用程序添加缓存,而且不需要进行系统级的更改。

说明:
本档的配置经过本人测试,都能正确运行。
运行环境: Jdk5.0, Spring-2.5, Spring-modules-0.9, ehcache-1.6.0-beta4.jar

首先创建一个StudentService服务类,本文将对其所有的以get* 方式命令的方法,进行缓存处理。当调用set* 命令时,需要其删除缓存
以更做数据的更新。
源代码如下:
public class StudentService {

private String name = "matthew";

public String getName() {
return name;
}

public String getName(String salution) {
return salution + " " + name;
}
public void setName(String name) {
this.name = name;
}

public void changeNameAndNotTellCache(String name) {
this.name = name;
}
}


接下来,就是编写Spring配置文件 context.xml,以实现根据上面的要求,进行声明性缓存功能的配置

Spring的配置内容如下:(已加上注释)
 1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
5
6 <!-- Using a EHCache cache manager -->
7 <bean id="cacheManager"
8 class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
9 <!--<property name="cacheManagerName" value="mainCache"/>-->
10 <property name="configLocation" value="classpath:ehcache.xml" />
11 </bean>
12
13 <!-- 使用Spring Modules对 EhCache的封装 -->
14 <bean id="cacheProviderFacade" class="org.springmodules.cache.provider.ehcache.EhCacheFacade">
15 <property name="cacheManager" ref="cacheManager" />
16 </bean>
17
18 <!-- 配置 方法 拦截器 -->
19 <!-- 缓存拦截器 -->
20 <bean id="cachingInterceptor"
21 class="org.springmodules.cache.interceptor.caching.MethodMapCachingInterceptor">
22 <property name="cacheProviderFacade" ref="cacheProviderFacade" />
23 <property name="cachingModels"> <!-- 进行cache缓存 -->
24 <props> <!-- 所有StudentService对象中,以get开头的方法都将进行缓存 -->
25 <prop key="StudentService.get*">cacheName=testCache</prop>
26 </props>
27 </property>
28 </bean>
29
30 <!-- 缓存刷新拦截器 -->
31 <bean id="flushingInterceptor"
32 class="org.springmodules.cache.interceptor.flush.MethodMapFlushingInterceptor">
33 <property name="cacheProviderFacade" ref="cacheProviderFacade" />
34 <property name="flushingModels"><!-- 进行cache刷新(清除) -->
35 <props>
36 <prop key="StudentService.set*">cacheNames=testCache</prop>
37 </props>
38 </property>
39 </bean>
40
41 <!-- 配置 基于BeanName规则的动态代理封装 -->
42 <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
43 <property name="beanNames">
44 <list>
45 <value>studentService</value>
46 </list>
47 </property>
48 <property name="interceptorNames">
49 <list>
50 <value>cachingInterceptor</value>
51 <value>flushingInterceptor</value>
52 </list>
53 </property>
54 </bean>
55
56 <bean id="studentService" class="StudentService"></bean>
57 </beans>

接下来,为能让EhCache能正常工作,还得编写EhCache配置文件 ehcache.xml, 内容如下:

 1 <ehcache>
2 <diskStore path="java.io.tmpdir" />
3 <defaultCache maxElementsInMemory="10000" eternal="false"
4 timeToIdleSeconds="2" timeToLiveSeconds="5" overflowToDisk="true"
5 maxElementsOnDisk="10000000" diskPersistent="false"
6 diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU" />
7 <cache name="testCache" maxElementsInMemory="10000"
8 maxElementsOnDisk="1000" eternal="false" overflowToDisk="false"
9 diskSpoolBufferSizeMB="20" timeToIdleSeconds="60" timeToLiveSeconds="3600"
10 memoryStoreEvictionPolicy="LFU" />
11 </ehcache>


下面将要测试配置是否能正确工作,编写一个测试类 Test.java内容如下:
 1 public class Test {
2
3 /**
4 * @param args
5 */
6 public static void main(String[] args) {
7 AbstractApplicationContext context;
8 context = new ClassPathXmlApplicationContext("classpath*:context.xml");
9 context.start();
10
11 StudentService ss = (StudentService) context.getBean("studentService");
12
13 String name;
14 System.out.println("第一次访问,没有缓存");
15 name = ss.getName();
16 System.out.println(name);
17 name = ss.getName("Mr");
18 System.out.println(name);
19
20 //use change not changed value
21 System.out.println("第二次访问,使用缓存");
22 ss.changeNameAndNotTellCache("Michael");
23 name = ss.getName();
24 System.out.println(name);
25
26 name = ss.getName("Mr");
27 System.out.println(name);
28
29 //update cache
30 System.out.println("清除缓存后,再次访问 ");
31 ss.setName("Michael");
32 name = ss.getName();
33 System.out.println(name);
34
35 name = ss.getName("Mr");
36 System.out.println(name);
37
38 context.close();
39 }
40
41 }


运行后的输出结果:
第一次访问,没有缓存
matthew
Mr matthew
第二次访问,使用缓存
matthew
Mr matthew
清除缓存后,再次访问
Michael
Mr Michael
从输出的结果上来看,缓存的功能已经正确启效。
本文只是一个简单的示例,希望对大家有借鉴作用。
更多的资料,详见官方文档 https://springmodules.dev.java.net/.

Good Luck!
Yours Matthew!
源代码解读基于Spring的声明性缓存实现原理

在上篇博客中,介绍了如何借助Spring Module项目,配置声明式缓存功能实现,文中只针对Ehcahce的实现进行了讲解,其实Spring Module项目把这块的功能做了一个很好的抽取,使其能更方便的对其它的缓存框架的支持和扩展。笔者正好利用该代码框架实现了与Memcached服务的集成,本文将得点通过源代码解讲一下抽取这层的实现,希望对大家有所帮助。注:本文只讲缓存部分的实现,刷新部分功能相同,请大家自己源读代码即可。

先看一下Spring的配置内容
1     <!-- 缓存拦截器 -->
2 <bean id="cachingInterceptor"
3 class="org.springmodules.cache.interceptor.caching.MethodMapCachingInterceptor">
4 <property name="cacheProviderFacade" ref="cacheProviderFacade" />
5 <property name="cachingModels"> <!-- 进行cache缓存 -->
6 <props> <!-- 所有StudentService对象中,以get开头的方法都将进行缓存 -->
7 <prop key="StudentService.get*">cacheName=testCache</prop>
8 </props>
9 </property>
10 </bean>
11
12
13 <!-- 配置 基于BeanName规则的动态代理封装 -->
14 <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
15 <property name="beanNames">
16 <list>
17 <value>studentService</value>
18 </list>
19 </property>
20 <property name="interceptorNames">
21 <list>
22 <value>cachingInterceptor</value>
23 <value>flushingInterceptor</value>
24 </list>
25 </property>
26 </bean>

通过Spring提供的BeanNameAutoProxyCreator类,提供对Bean对象的统一自动代理实现。从上面的配置中,实现缓存拦截的实现类就是org.springmodules.cache.interceptor.caching.MethodMapCachingInterceptor。实现了MethodInterceptor接口,对代理的对象的方法调用进行拦截,实现缓存功能。
下面是完整的类图:

[img]http://dl.iteye.com/upload/attachment/305229/5243d44d-7323-3f1b-ab4b-03011ec4bbe8.png[/img]



从类图中,可以看到AbstractCachingInterceptor抽象实现了MethodInterceptor接口的invoke方法。这也是整个缓存处理的入口。
看代码之前,我先来补充一下框架是如果实现方法拦截后的匹配过程。
首先是构建匹配规则:
由 MethodMapCachingInterceptor类的onAfterPropertiesSet方法实现。
实现思路如下:
取得 cachingModels属性,遍历每一个 key
例如上例中 StudentService.get* , 解析出 class 类名(StudentService),和方法的匹配字符串(get*)
然后通过 Class.forName 方法,装载该类,取出该类的所有方法,一一与指定的方法匹配字符串进行 正则匹配TextMatcher.isMatch,匹配通过的则放入到一个Map中, key=Method对象, value=CacheModel对象当 拦截器 对调用的方法进行拦截时,通过 map.get返回值来确认是否对方法进行缓存处理

实现代码如下:已经添加注释
 1     public final Object invoke(MethodInvocation mi) throws Throwable {
2 Method method = mi.getMethod();//取得拦截的方法
3 if (!CachingUtils.isCacheable(method))
4 return methodNotCacheable(mi, method); //如果是void返回值,则不需要缓存支持
5
6 CachingModel model = model(mi); //根据method,则定是否要进行缓存
7 if (model == null) return noModelFound(mi, method);
8
9 Serializable key = keyGenerator.generateKey(mi);//根据方法对象,生成key
10 Object cached = cache.getFromCache(key, model);//如果有缓存
11
12 if (null == cached) return cachedValueFromSource(mi, key, model); //如果没有缓存,把返回保存到缓存
13 return unmaskNull(cached);
14 }


到些基本的实现流程已经讲解完了,其它的大家可以通过阅读源代码进行理解。

最后补充一下如何根据这个框架集成其它的缓存服务,需要实现的接口和继承的抽象类如下:
AbstractCacheProviderFacade 缓存保存,取得,更新的实现
AbstractCacheModelValidator 检测缓存模型合法性
CachingModel 保存缓存的模型接口
AbstractFlushingModel 刷新缓存的模型抽象类


Good Luck!
Yours Matthew!
Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐