Ehcache学习ehcache与springAOP拦截器实例
Posted 李晓娜
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Ehcache学习ehcache与springAOP拦截器实例相关的知识,希望对你有一定的参考价值。
本次我们使用springAOP+ehcache结合来实现数据的缓存,我们可以 Cache 系统中 Service 或则 DAO 层的 get/find 等方法返回结果,如果数据更新( 使用Create/update/delete 方法), 则刷新 cache 中相应的内容。
Aop中最常见的就是拦截器,那么我们首先需要创建的就是一个拦截器:
Ø MethodCacheInterceptor
<span style="font-family:Microsoft YaHei;font-size:14px;">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();
/**
* public Object invoke(MethodInvocation invocation) 中,
完成了搜索 Cache/新建 cache 的功能。
* @param invocation
* @return
* @throws Throwable
*/
@Override
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);
// 搜索cache功能,如果存在直接返回,否则的话将创建之后进行存储
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();
// return null;
/**
* implement InitializingBean, 检查 cache 是否为空
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception
Assert.notNull(cache, "Need a cache. Please use setCache(Cache) create it.");
/**
* 获得 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();
</span>
Ø MethodCacheAfterAdvice
<span style="font-family:Microsoft YaHei;font-size:14px;">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();
@Override
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);
@Override
public void afterPropertiesSet() throws Exception
Assert.notNull(cache, "Need a cache. Please use setCache(Cache) create it.");
</span>
随后,再建立一个拦截器MethodCacheAfterAdvice,作用是在用户进行create/update/delete操作时来刷新/remove 相关 cache 内容, 这个拦截器实现了AfterReturningAdvice 接口,将会在所拦截的方法执行后执行在
public void afterReturning(Object arg0, Method arg1,Object[] arg2, Object arg3)方法中所预定的操作
Ø 配置文件:
Application_spring_cache.xml
<span style="font-family:Microsoft YaHei;font-size:14px;"><?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:cache="http://www.springframework.org/schema/cache" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--支持缓存注解-->
<cache:annotation-driven cache-manager="cacheManager"/>
<context:component-scan base-package="ehcache.*" />
<!-- 一些@RequestMapping 请求和一些转换 -->
<mvc:annotation-driven />
<!-- 前后缀 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 静态资源访问 的两种方式 -->
<!-- <mvc:default-servlet-handler/> -->
<!--<mvc:resources location="/*" mapping="/**" />-->
<mvc:view-controller path="/" view-name="forward:/index.jsp"/>
<!-- Spring自己的基于java.util.concurrent.ConcurrentHashMap实现的缓存管理器(该功能是从Spring3.1开始提供的) -->
<!--
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean name="myCache" class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"/>
</set>
</property>
</bean>
-->
<!-- 若只想使用Spring自身提供的缓存器,则注释掉下面的两个关于Ehcache配置的bean,并启用上面的SimpleCacheManager即可 -->
<!-- Spring提供的基于的Ehcache实现的缓存管理器 -->
<!-- 缓存 属性-->
<bean id="cacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:/ehcache.xml" />
</bean>
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="cacheManagerFactory"/>
</bean>
<!--为了测试拦截器缓存-->
<!-- 定义 ehCache 的工厂, 并设置所使用的 Cache name -->
<bean id="ehCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<ref local="cacheManagerFactory"/>
</property>
<!--如果 cacheName 属性内设置的 name 在 ehCache.xml 中无法找到, 那么将使用默认的-->
<!--cache(defaultCache 标签定义)-->
<property name="cacheName">
<value>DEFAULT_CACHE</value>
</property>
</bean>
<!-- find/create cache 拦截器 -->
<bean id="methodCacheInterceptor"
class="ehcache.CacheInterceptor.MethodCacheInterceptor">
<property name="cache">
<ref local="ehCache" />
</property>
</bean>
<!-- flush cache 拦截器 -->
<bean id="methodCacheAfterAdvice"
class="ehcache.CacheInterceptor.MethodCacheAfterAdvice">
<property name="cache">
<ref local="ehCache" />
</property>
</bean>
<!--上面的代码最终创建了两个"切入点", methodCachePointCut 和-->
<!--methodCachePointCutAdvice, 分别用于拦截不同方法名的方法, 可以根据需要任意增加-->
<!--所需要拦截方法的名称。-->
<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>
<!--方法测试-->
<!--如果缓存的配置与spring拦截在一个方法中就可以直接使用不用引入,否则的话需要引入-->
<!--<import resource="cacheContext.xml"/>-->
<bean id="testServiceTarget" class="ehcache.Service.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></span>
Ehcache.xml
<span style="font-family:Microsoft YaHei;font-size:14px;"><ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!--当内存缓存中对象数量超过maxElementsInMemory时,将缓存对象写到磁盘缓存中(需对象实现序列化接口)。-->
<diskStore path="java.io.tmpdir"/>
<!--:用来配置磁盘缓存使用的物理路径,Ehcache磁盘缓存使用的文件后缀名是*.data和*.index。-->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</defaultCache>
<cache name="DEFAULT_CACHE"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="300000"
timeToLiveSeconds="600000"
overflowToDisk="true"
/>
</ehcache></span>
Ø 测试用例
接口TestService
<span style="font-family:Microsoft YaHei;font-size:14px;">package ehcache.Iservice;
import java.util.List;
/**
* Created by xiaona on 2016/6/14.
*/
public interface TestService
public List getAllObject();
public void updateObject(Object Object);
</span>
实现TestServiceImpl
<span style="font-family:Microsoft YaHei;font-size:14px;">package ehcache.Service;
import ehcache.Iservice.TestService;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* Created by xiaona on 2016/6/14.
*/
@Service
public class TestServiceImpl implements TestService
@Override
public List getAllObject()
System.out.println("---TestService: Cache 内不存在该 element, 查找并放入 Cache! ");
return null;
@Override
public void updateObject( Object Object )
System.out.println("---TestService:更新了对象,这个 Class 产生的 cache 都将被 remove");
</span>
方法调用
两种方式:getBean方式和使用注解读取
第一种方式:
<span style="font-family:Microsoft YaHei;font-size:14px;">package ehcache.controller;
import ehcache.Iservice.TestService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Created by xiaona on 2016/6/14.
*/
public class TestMain
public static void main( String[] args )
ApplicationContext context = new ClassPathXmlApplicationContext("application_spring_cache.xml");
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();
</span>
第二种方式:
<span style="font-family:Microsoft YaHei;font-size:14px;">@Resource
private TestService testService;
@RequestMapping(value = "/testInterceptor", method = RequestMethod.GET)
public String testInterceptor()
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();
return root + "/" + "testInterceptor";
</span>
前提是TestService的实现TestServiceImpl必须加上@Service注解,这样可以把其交给容器进行管理。什么样的结果表示我们的是成功的从缓存中获取到的呢?可以看到,
第一步执行getAllObject(),执行 TestServiceImpl 内的方法,并创建了 cache, 在第二次执行getAllObject()方法时,由于 cache 有该方法的缓存,直接从cache 中 get 出方法的结果, 所以没有打印出 TestServiceImpl中的内容, 而第三步, 调用了 updateObject 方法,和TestServiceImpl 相关的 cache 被 remove,所以在第四步执行时,又执行TestServiceImpl 中的方法, 创建 Cache。
Ø 小结
我们看出结合SpringAop的实例,可以实现我们对于请求方法的拦截,其实很多时候我们用到的页面都是一定时间内不会发生变化的,此时我们需要的是拦截经常被调用的方法从而进行缓存,这样的话可以提高用户对于页面请求的响应速度。
以上是关于Ehcache学习ehcache与springAOP拦截器实例的主要内容,如果未能解决你的问题,请参考以下文章
Ehcache学习ehcache与springAOP拦截器实例
Ehcache学习总结二: Ehcache+Spring+Mybaits整合