- 浏览: 103083 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
luochen512:
分析的很到位,对我的帮助很大,希望多多交流
Jeecms源码分析(二) -
king114963349:
谢谢,分享
JPA例子 -
莫小鸣:
不错啊!!
Jeecms源码分析(三)
5. 在 Spring 中运用 EHCache
需要使用 Spring 来实现一个 Cache 简单的解决方案,具体需求如下:使用任意一个现有开源 Cache Framework,要求使用 Cache 系统中 Service 或则 DAO 层的 get/find 等方法返回结果,如果数据更新(使用 Create/update/delete 方法),则刷新 cache 中相应的内容。根据需求,计划使用 Spring AOP + ehCache 来实现这个功能,采用 ehCache 原因之一是Spring 提供了 ehCache 的支持,至于为何仅仅支持 ehCache 而不支持 osCache 和JBossCache 无从得知(Hibernate???),但毕竟 Spring 提供了支持,可以减少一部分工作量:)。二是后来实现了 OSCache 和 JBoss Cache 的方式后,经过简单测试发现几个 Cache 在效率上没有太大的区别(不考虑集群),决定采用 ehCahce。AOP 嘛,少不了拦截器,先创建一个实现了 MethodInterceptor 接口的拦截器,用来拦截Service/DAO 的方法调用,拦截到方法后,搜索该方法的结果在 cache 中是否存在,如果存在,返回 cache 中的缓存结果,如果不存在,返回查询数据库的结果,并将结果缓存到 cache 中。
MethodCacheInterceptor.java
Java 代码
package com.co.cache.ehcache;
import java.io.Serializable;
import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
public class MethodCacheInterceptor implements MethodInterceptor, InitializingBean
{
private static final Log logger = LogFactory.getLog(MethodCacheInterceptor.class);
private Cache cache;
public void setCache(Cache cache) {
this.cache = cache;
}
public MethodCacheInterceptor() {
super();
}
/**
* 拦截 Service/DAO 的方法,并查找该结果是否存在,如果存在就返回 cache 中的值,
* 否则,返回数据库查询结果,并将查询结果放入 cache
*/
public Object invoke(MethodInvocation invocation) throws Throwable {
String targetName = invocation.getThis().getClass().getName();
String methodName = invocation.getMethod().getName();
Object[] arguments = invocation.getArguments();
Object result;
logger.debug("Find object from cache is " + cache.getName());
String cacheKey = getCacheKey(targetName, methodName, arguments);
Element element = cache.get(cacheKey);
if (element == null) {
logger.debug("Hold up method , Get method result and create cache........!");
result = invocation.proceed();
element = new Element(cacheKey, (Serializable) result);
cache.put(element);
}
return element.getValue();
}
/**
* 获得 cache key 的方法, cache key 是 Cache 中一个 Element 的唯一标识
* cache key 包括 包名 + 类名 + 方法名,如 com.co.cache.service.UserServiceImpl.getAllUser
*/
private String getCacheKey(String targetName, String methodName, Object[] arguments) {
StringBuffer sb = new StringBuffer();
sb.append(targetName).append(".").append(methodName);
if ((arguments != null) && (arguments.length != 0)) {
for (int i = 0; i < arguments.length; i++) {
sb.append(".").append(arguments[i]);
}
}
return sb.toString();
}
/**
* implement InitializingBean ,检查 cache 是否为空
*/
public void afterPropertiesSet() throws Exception {
Assert.notNull(cache, "Need a cache. Please use setCache(Cache) create it.");
}
}
上面的代码中可以看到,在方法 public Object invoke(MethodInvocation invocation) 中,完成了搜索 Cache/ 新建 cache 的功能。
Element element = cache.get(cacheKey);
这句代码的作用是获取 cache 中的 element ,如果 cacheKey 所对应的 element 不存在,将会返回一个 null 值。
Java 代码
result = invocation.proceed();
这句代码的作用是获取所拦截方法的返回值,详细请查阅 AOP 相关文档。随后,再建立一个拦截器 MethodCacheAfterAdvice ,作用是在用户进行 create/update/delete 操作时来刷新 /remove 相关 cache 内容,这个拦截器实现了 AfterReturningAdvice 接口,将会在所拦截的方法执行后执行在 public void afterReturning(Object arg0, Method arg1,Object[] arg2, Object arg3) 方法中所预定的操作
Java 代码
package com.co.cache.ehcache;
import java.lang.reflect.Method;
import java.util.List;
import net.sf.ehcache.Cache;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
public class MethodCacheAfterAdvice implements AfterReturningAdvice, InitializingBean
{
private static final Log logger = LogFactory.getLog(MethodCacheAfterAdvice.class);
private Cache cache;
public void setCache(Cache cache) {
this.cache = cache;
}
public MethodCacheAfterAdvice() {
super();
}
public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws
Throwable {
String className = arg3.getClass().getName();
List list = cache.getKeys();
for(int i = 0;i<list.size();i++){
String cacheKey = String.valueOf(list.get(i));
if(cacheKey.startsWith(className)){
cache.remove(cacheKey);
logger.debug("remove cache " + cacheKey);
}
}
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(cache, "Need a cache. Please use setCache(Cache) create it.");
}
}
上面的代码很简单,实现了 afterReturning 方法实现自 AfterReturningAdvice 接口,方法中所定义的内容将会在目标方法执行后执行,在该方法中的作用是获取目标 class 的全名,如: com.co.cache.test.TestServiceImpl ,然后循环 cache 的 key list , remove cache 中所有和该 class 相关的 element 。
Java 代码
String className = arg3.getClass().getName();
随后,开始配置 ehCache 的属性, ehCache 需要一个 xml 文件来设置 ehCache 相关的一些属性,如最大缓存数量、 cache 刷新的时间等等 .
ehcache.xml
<ehcache>
<diskStore path="c:\\myapp\\cache"/>
<defaultCache
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
/>
<cache name="DEFAULT_CACHE"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="300000"
timeToLiveSeconds="600000"
overflowToDisk="true"
/>
</ehcache>
配置每一项的详细作用不再详细解释,有兴趣的请 google 下 ,这里需要注意一点 defaultCache 标签定义了一个默认的 Cache ,这个 Cache 是不能删除的,否则会抛出 No default cache is configured 异常。另外,由于使用拦截器来刷新 Cache 内容,因此在定义 cache 生命周期时可以定义较大的数值, timeToIdleSeconds="300000" , timeToLiveSeconds="600000" ,好像还不够大?然后,在将 Cache 和两个拦截器配置到 Spring ,这里没有使用 2.0 里面 AOP 的标签。
cacheContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd ">
<beans>
<!-- 引用 ehCache 的配置 -->
<bean id="defaultCacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation">
<value>ehcache.xml</value>
</property>
</bean>
<!-- 定义 ehCache 的工厂,并设置所使用的 Cache name -->
<bean id="ehCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<ref local="defaultCacheManager"/>
</property>
<property name="cacheName">
<value>DEFAULT_CACHE</value>
</property>
</bean>
<!-- find/create cache 拦截器 -->
<bean id="methodCacheInterceptor"
class="com.co.cache.ehcache.MethodCacheInterceptor">
<property name="cache">
<ref local="ehCache" />
</property>
</bean>
<!-- flush cache 拦截器 -->
<bean id="methodCacheAfterAdvice"
class="com.co.cache.ehcache.MethodCacheAfterAdvice">
<property name="cache">
<ref local="ehCache" />
</property>
</bean>
<bean id="methodCachePointCut"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="methodCacheInterceptor"/>
</property>
<property name="patterns">
<list>
<value>.*find.*</value>
<value>.*get.*</value>
</list>
</property>
</bean>
<bean id="methodCachePointCutAdvice"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="methodCacheAfterAdvice"/>
</property>
<property name="patterns">
<list>
<value>.*create.*</value>
<value>.*update.*</value>
<value>.*delete.*</value>
</list>
</property>
</bean>
</beans>
上面的代码最终创建了两个 " 切入点 " , methodCachePointCut 和 methodCachePointCutAdvice ,分别用于拦截不同方法名的方法,可以根据需要任意增加所需要拦截方法的名称。需要注意的是 Java 代码
<bean id="ehCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<ref local="defaultCacheManager"/>
</property>
<property name="cacheName">
<value>DEFAULT_CACHE</value>
</property>
</bean>
如果 cacheName 属性内设置的 name 在 ehCache.xml 中无法找到,那么将使用默认的
cache(defaultCache 标签定义 ) ,事实上到了这里,一个简单的 Spring + ehCache Framework 基本完成了,为了测试效果,举一个实际应用的例子,定义一个 TestService 和它的实现类 TestServiceImpl ,里面包含两个方法 getAllObject() 和 updateObject(Object Object) ,具体代码如下:
TestService.java
Java 代码
package com.co.cache.test;
import java.util.List;
public interface TestService {
public List getAllObject();
public void updateObject(Object Object);
}
TestServiceImpl.java
Java 代码
package com.co.cache.test;
import java.util.List;
public class TestServiceImpl implements TestService
{
public List getAllObject() {
System.out.println("---TestService : Cache 内不存在该 element ,查找并放入 Cache ! ");
return null;
}
public void updateObject(Object Object) {
System.out.println("---TestService :更新了对象,这个 Class 产生的 cache 都将被 remove !
");
}
}
使用 Spring 提供的 AOP 进行配置
applicationContext.xml
XML/HTML 代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd ">
<beans>
<import resource="cacheContext.xml"/>
<bean id="testServiceTarget" class="com.co.cache.test.TestServiceImpl"/>
<bean id="testService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref local="testServiceTarget"/>
</property>
<property name="interceptorNames">
<list>
<value>methodCachePointCut</value>
<value>methodCachePointCutAdvice</value>
</list>
</property>
</bean>
</beans>
这里一定不能忘记 import cacheContext.xml 文件,不然定义的两个拦截器就没办法使用
了。
最后,写一个测试的代码
MainTest.java
Java 代码
(DEFAULT_CONTEXT_FILE);
TestService testService = (TestService)context.getBean("testService");
System.out.println("1-- 第一次查找并创建 cache");
testService.getAllObject();
System.out.println("2-- 在 cache 中查找 ");
testService.getAllObject();
System.out.println("3--remove cache");
testService.updateObject(null);
System.out.println("4-- 需要重新查找并创建 cache");
testService.getAllObject();
}
}
运行,结果如下
Java 代码
1-- 第一次查找并创建 cache
---TestService : Cache 内不存在该 element ,查找并放入 Cache !
2-- 在 cache 中查找
3--remove cache
---TestService :更新了对象,这个 Class 产生的 cache 都将被 remove !
4-- 需要重新查找并创建 cache
---TestService : Cache 内不存在该 element ,查找并放入 Cache !
大功告成 . 可以看到,第一步执行 getAllObject() ,执行 TestServiceImpl 内的方法,并创建了 cache ,在第二次执行 getAllObject() 方法时,由于 cache 有该方法的缓存,直接从 cache 中 get 出方法的结果,所以没有打印出 TestServiceImpl 中的内容,而第三步,调用了 updateObject 方法,和 TestServiceImpl 相关的 cache 被 remove ,所以在第四步执行时,又执行 TestServiceImpl 中的方法,创建 Cache 。网上也有不少类似的例子,但是很多都不是很完备,自己参考了一些例子的代码,其实在 spring-modules 中也提供了对几种 cache 的支持, ehCache , OSCache , JBossCache 这些,看了一下,基本上都是采用类似的方式,只不过封装的更完善一些,主要思路也还是 Spring 的 AOP ,有兴趣的可以研究一下。
发表评论
-
Hibernate4 + Ehcache 配置问题
2012-11-02 08:21 164471、遇到的问题: Caused by: org ... -
Ehcache例子
2012-11-01 09:57 1231Spring3.1后对ehcache提供了原生的支持,本文章提 ... -
Ehcache学习(4)
2012-11-01 09:48 17496. 分布式缓存集群环境配置 6 ... -
Ehcache学习(2)
2012-11-01 09:48 16124. Ehcache ... -
Ehcache学习(1)
2012-11-01 09:41 6526参考文档: 部分都是网络上查询的资料,很多,不列 ...
相关推荐
**缓存框架 Ehcache 学习笔记** Ehcache 是一个广泛使用的开源 Java 缓存框架,它在处理大量数据的高性能应用中扮演着重要角色。Ehcache 提供了本地内存缓存、磁盘存储以及分布式缓存的能力,使得应用程序能够快速...
### Ehcache学习文档知识点梳理 #### 一、文档概述与目的 - **文档目的**:旨在帮助读者深入了解和掌握Ehcache缓存技术,适用于所有对缓存技术感兴趣的技术人员。 - **文档范围**:覆盖了Ehcache的基础概念、配置...
通过这些示例,我们可以学习如何设置Ehcache的分布式特性,例如使用Terracotta服务器进行集群缓存,以及如何处理分布式环境下的缓存一致性问题。 总结来说,Ehcache的监控涉及了多个方面,包括但不限于使用JMX、Web...
【Spring3 Hibernate4 Ehcache整合实例详解】 在Java企业级应用开发中,Spring、Hibernate和Ehcache是三个非常重要的框架和技术。Spring作为轻量级的IoC(Inversion of Control)和AOP(Aspect Oriented ...
标题"ehcache-1.2.3 ejb3-persistence"指出这是一个关于ehcache的特定版本(1.2.3)与EJB3持久化相关的资源包。Ehcache是一个广泛使用的Java缓存系统,而EJB3(Enterprise JavaBeans 3.0)是Java EE平台的一部分,...
学习Ehcache时,开发者需要理解其配置文件(通常为`ehcache.xml`),其中包含了缓存管理器的配置、缓存的设置等信息。此外,Ehcache与Spring框架的集成也是常见的应用场景,通过Spring的缓存抽象,可以方便地将...
### ehcache基础知识与实践 #### 一、ehcache简介 ...通过以上内容的学习,相信初学者已经对ehcache有了初步的了解。接下来可以通过更多的实践来深入掌握ehcache的高级特性及其在复杂场景下的应用技巧。
**Ehcache简介** Ehcache是一款开源的Java缓存框架,它被广泛应用于提高应用程序性能,通过存储数据副本以减少对数据库的...通过`Ehcache_Hello`案例,开发者可以逐步学习和实践,从而熟练运用Ehcache进行缓存管理。
ehcache-2.10.6.jar ehcache jar包供各位开发人员 学习、交流,切勿用于商业用途。
通过分析这些源代码,你可以学习如何集成Ehcache到你的应用程序中,以及如何使用反射进行测试和调试。 总结起来,这个例子展示了如何将Ehcache的强大缓存能力与Java反射机制相结合,以提高测试的灵活性和覆盖率。...
EHCache 是一个开源的、高性能的缓存解决方案,广泛应用于Java应用程序中,以提高数据访问的速度和...通过学习这份技术文档和实际操作,开发者可以深入了解EHCache的工作原理,并有效地利用它来优化应用程序的性能。
3. **缓存策略**:Ehcache提供了多种缓存替换策略,如LRU(Least Recently Used)、LFU(Least Frequently Used)等。源码分析可以揭示这些策略的实现细节和选择哪种策略的依据。 4. **缓存同步**:在多线程环境中...
【Spring + Ehcache 整合应用详解】 ...通过学习和实践这个示例,开发者可以深入了解Spring与Ehcache的集成,以及如何利用缓存提升应用程序的性能。同时,了解如何管理和配置数据库连接,也是Java开发中的基础技能。
通过对 Mybatis-ehcache 1.2.1 的源码学习,开发者可以更好地理解缓存如何与 ORM 框架协同工作,优化数据库交互,同时也能为自定义缓存解决方案提供参考。在实际开发中,结合缓存策略和监控,可以进一步提升系统的...
** Ehcache 学习笔记** Ehcache 是一个开源的 Java 缓存系统,它提供了在应用程序中存储和检索数据的高效方式,特别是在高并发环境下。这个笔记将深入探讨 Ehcache 的核心概念、配置和使用场景,以及如何通过源码...
通过深入学习Ehcache的源码,我们可以理解其内部的工作机制,包括缓存的创建、存储、检索、更新和清除过程,以及如何优化和调整缓存配置以适应不同应用场景。这有助于我们更好地利用Ehcache提升应用程序的性能,并...
Ehcache是一个开源的Java缓存库,广泛用于提高应用程序性能,通过缓存数据来减少对...尽管Ehcache已经发展到了更高级的版本,但学习老版本可以帮助我们更好地理解缓存机制的发展历程,也能为理解和使用新版本打下基础。
3. **Element(元素)**:`net.sf.ehcache.Element`是缓存中的基本单元,包含键值对。每个Element都有一个唯一的键和对应的值,还有过期时间和状态信息。 4. **缓存策略**:Ehcache支持多种缓存策略,包括LRU(最近...
3. 页面缓存的应用 在Web开发中,Ehcache可以用于缓存静态或半静态的页面内容,如首页、分类列表页等。这样,当用户请求这些页面时,服务器可以直接从缓存中获取数据,避免了每次请求都执行昂贵的数据库查询。这极大...
Spring 和 Ehcache 是两个在Java开发中非常重要的框架。Spring 是一个全面的后端开发框架,提供了依赖注入、AOP(面向切面编程)、MVC(模型-视图-...这个示例Demo是学习和理解Spring与Ehcache集成的一个很好的起点。