`

源代码解读基于Spring的声明性缓存实现原理

阅读更多
前言:
本贴复制来源: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接口,对代理的对象的方法调用进行拦截,实现缓存功能。
下面是完整的类图:



  

从类图中,可以看到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!


  • 大小: 189.7 KB
分享到:
评论

相关推荐

    【面试资料】-(机构内训资料)看透Spring MVC源代码分析与实践.zip

    这份【面试资料】-(机构内训资料)看透Spring MVC源代码分析与实践.zip文件很可能是为了帮助求职者准备相关面试问题而设计的,包含了对Spring MVC工作原理、关键组件和源码解读的详尽解析。 1. **Spring MVC基本...

    电子商场源代码

    本文将深入探讨基于JAVA语言的电子商城系统,揭示其背后的技术架构和实现原理,并对提供的压缩包文件进行解读。 首先,JAVA电子商城系统是利用JAVA这门强大的面向对象编程语言构建的,它具有高度的可移植性、安全性...

    传智巴巴运动网源代码

    以上是对"传智巴巴运动网源代码"中可能涉及的技术栈和核心知识点的解读,实际的源代码可能会根据项目需求和开发团队选择采用不同的技术和实现方式。深入研究这套源代码,开发者可以学习到一套完整的网站开发流程,...

    spring核心源码详细解读

    - **基于Java代码配置**:利用`@Configuration`注解结合`@Bean`注解来声明Bean的定义,这种方式更加灵活,且避免了XML配置文件。 - **基于Java注解配置**:通过`@Component`、`@Service`、`@Repository`等注解来声明...

    ibatis-2.3.4.726-src-源代码

    本文将针对iBatis 2.3.4.726版本的源代码进行详尽解读,帮助开发者深入了解其内部机制,提升开发与调试能力。 1. **框架概述** iBatis,又称为MyBatis的前身,是一个基于Java的持久层框架,它简化了数据库操作,将...

    电子商城系统JAVA源代码

    【电子商城系统JAVA源代码】是一个基于JAVA编程语言开发的电子商务平台的核心代码库。这个系统通常包括用户界面、后台管理、订单处理、支付接口、库存管理等多个模块,旨在提供一个全面、安全、高效的在线购物环境。...

    oa(办公自动化)系统源代码

    以下是对OA系统及其源代码的详细解读: 1. **OA系统概述** - OA(Office Automation)系统是利用信息化手段,将传统的办公流程数字化,以提高办公效率,减少人力成本。 - 主要功能包括文档管理、工作流审批、会议...

    Spring 3.0 API

    SPEL)、基于Java的bean元数据、在组件内定义bean元数据、通用类型转换系统和字段格式化系统、数据层、Web层、全面的REST支持、@MVC的添加、声明性模型验证、Java EE 6的早期支持和嵌入式数据库的支持。 Spring ...

    spring 源码解析

    4. **Spring AOP**:Spring AOP是面向切面编程的实现,用于在不修改源代码的情况下插入横切关注点,如日志、缓存和事务管理。AOP代理有两种形式:JDK动态代理和CGLIB代理。前者针对实现了接口的类,后者则适用于没有...

    JAVA小区门户网站(源代码+LW).rar

    【标题】"JAVA小区门户网站(源代码+LW)"是一个基于Java编程语言开发的社区门户系统,其中"LW"可能是“轻量级”或者“逻辑层”的缩写,表明这个项目可能采用了轻量级框架或者重点在于业务逻辑的实现。这个压缩包包含...

    spring源码(1),可直接导入eclipse

    1. **spring-expression**: 这是Spring表达式语言(Spring Expression Language, SpEL)的源代码。SpEL是一个强大且灵活的内嵌表达式语言,用于在运行时查询和操作对象图。它支持属性导航、方法调用、条件和逻辑运算...

    JAVA办公自动化系统(源代码+论文+外文翻译).zip

    源代码、论文和外文翻译的组合,意味着我们可以深入学习其开发过程、设计理念以及技术实现。 源代码是理解系统运作的关键,它揭示了JAVA编程在办公自动化领域的具体应用。开发者可能使用了Java Swing或JavaFX进行...

    spring笔记

    Spring笔记中提到的技术知识点主要围绕Hibernate框架展开,Hibernate是一个开放源代码的对象关系映射(Object/Relational Mapping,简称ORM)框架,用于Java环境。它对JDBC进行了非常轻量级的对象封装,使得开发者...

    一个好的hibernate源代码有server2008数据库

    描述中说“5分不算多”,这可能是用户对这个源代码质量的评价,意味着他认为这个源代码的价值或质量非常高,至少可以得满分5分。这也暗示了该代码示例的实用性、可读性和稳定性都相当不错。 **标签关联:** 标签只...

    人力资源管理信息系统源码

    1. 代码规范:遵循统一的编码规范,提高代码可读性和可维护性。 2. 性能优化:通过缓存策略、数据库索引优化等方式提升系统响应速度。 3. 自动化测试:编写测试用例,确保新功能的正确性和老功能的稳定性。 4. 版本...

    JAVA小区物业管理系统(源代码).zip

    【JAVA小区物业管理系统(源...以上是对"JAVA小区物业管理系统(源代码)"的详细解读,这个系统不仅涵盖了物业管理的基础功能,还体现了Java在企业级应用中的应用实践,对于学习和理解Java Web开发具有较高的参考价值。

    在网上下载的spring3相关的文档

    - **Spring的事务管理**:编程式和声明式事务管理的使用,以及如何在多数据源环境中配置事务。 - **Spring与企业服务的集成**:JMS(Java Message Service)、JMX(Java Management Extensions)等。 - **Spring ...

    spring 3.1 参考手册 英文

    随着 Java 技术的发展,Spring 3.1 对 Java 5 及其后续版本提供了全面的支持,利用了 Java 5 的新特性(如泛型、枚举和可变参数)来优化代码结构和提高类型安全性。 **2.2 文档改进** 为了帮助开发者更好地理解和...

    基于FLEX的一个问卷系统

    这个系统结合了FLEX的交互性与Spring的强大功能,为用户提供了一个高效、灵活的问卷设计和数据收集平台。 FLEX是Adobe公司推出的一种用于创建富互联网应用程序(RIA)的开发工具,它基于ActionScript编程语言和Flex...

Global site tag (gtag.js) - Google Analytics