AOP+Ehcache 缓存框架

AOP+Ehcache 实现缓存功能

设计思路:查询数据,通过AOP拦截需要缓存的方法,在方法进入之前进入拦截器,通过包、类、方法名称作为key获取当前缓存对象结果,如果为空,则执行真正的方法,如果有缓存对象,则直接返回。删除和修改数据,每次都在此之前调用拦截器方法清除相关缓存对象。

1.需要引入AOP和Ehcache相关Jar文件

2.编写切面类里面写上拦截时的业务逻辑

import java.io.Serializable;
import java.util.List;

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;

/**
 * 
 * 类功能描述:AOP+Ehcache 拦截需要缓存的方法,插入点方法执行前后
 *
 * @author <a href="mailto:[email protected]">mengqingyu </a>
 * @version $Id: codetemplates.xml,v 1.1 2009/03/06 01:13:01 mengqingyu Exp  $
 * Create:  2011-12-12 下午04:27:44
 */
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  
     */
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable { 
        Object result; 
        String cacheKey = getCacheKey(invocation.getThis().getClass().getName()
        		, invocation.getMethod().getName(), invocation.getArguments()); 
        Element element = cache.get(cacheKey); 
        logger.warn("检查缓存:" + cacheKey); 
        if (element == null) { 
            logger.warn("创建缓存......");
            result = invocation.proceed(); 
            element = new Element(cacheKey, (Serializable) result); 
            cache.put(element); 
        } 
        List list = cache.getKeys();
        return element.getValue(); 
    } 

    /**
     * 
     * @function:获得cache key的方法,cache key是Cache中一个Element的唯一标识 
     * 			 cache key包括 包名+类名+方法名,如com.co.cache.service.UserServiceImpl.getAllUser 
     * @param targetName
     * @param methodName
     * @param arguments
     * @return
     * @author: mengqingyu    2011-12-12 下午04:35:21
     */
    private String getCacheKey(String targetName, String methodName, Object[] arguments) {
    	StringBuilder sb = new StringBuilder(); 
        sb.append(targetName).append(".").append(methodName); 
        if (arguments != null) { 
            for(Object o:arguments){
            	sb.append(".").append(o); 
            }
        } 
        return sb.toString(); 
    } 
     
    /**
     * implement InitializingBean,检查cache是否为空 
     */
    public void afterPropertiesSet() throws Exception { 
        Assert.notNull(cache, "缓存未初始化");
     }
}

package com.berheley.bi.aop;

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;

/**
 * 
 * 类功能描述:AOP+Ehcache 拦截需要缓存的方法,插入点方法执行之后
 *
 * @author <a href="mailto:[email protected]">mengqingyu </a>
 * @version $Id: codetemplates.xml,v 1.1 2009/03/06 01:13:01 mengqingyu Exp  $
 * Create:  2011-12-12 下午04:27:44
 */
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(); 
    } 

    /**
     * 拦截Service/DAO的方法,并查找该结果是否存在,如果存在则删除结果, 
     * 否则忽略,此方法为了保证更新数据库同时同步缓存  
     */    
	@Override
	public void afterReturning(Object arg, Method method, Object[] arguments,
			Object obj) throws Throwable {
		String className = obj.getClass().getName();
		List<String> list = cache.getKeys();
		for(String cacheKey:list){
			if(cacheKey.startsWith(className)){
				cache.remove(cacheKey);
				logger.debug("删除缓存:" + cacheKey);
			}
		}
	} 
	
    /**
     * implement InitializingBean,检查cache是否为空 
     */
    public void afterPropertiesSet() throws Exception { 
        Assert.notNull(cache, "缓存未初始化");
     }
}


3.配置文件中配置AOP和类的实例化

<?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:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
		http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd ">
	
	<!-- AOP+Ehcache by mengqingyu-->
	
	<!-- 引用ehCache的配置 -->
	<bean id="defaultCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
	  <property name="configLocation">
		<value>classpath:ehcache.xml</value>
	  </property>
	</bean>
	
	<!-- 定义ehCache的工厂,并设置所使用的Cache name 如找不到则使用默认缓存配置defaultCache-->
	<bean id="ehCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
	  <property name="cacheManager">
		<ref local="defaultCacheManager"/>
	  </property>
	  <property name="cacheName">
		  <value>systemCache</value>
	  </property>
	</bean>
	
	<!-- find/create cache拦截器 -->
	<bean id="methodCacheInterceptor" class="com.berheley.bi.aop.MethodCacheInterceptor">
	  <property name="cache">
		<ref local="ehCache" />
	  </property>
	</bean>
	
	<!-- find/create cache拦截器 -->
	<bean id="methodCacheAfterAdvice" class="com.berheley.bi.aop.MethodCacheAfterAdvice">
	  <property name="cache">
		<ref local="ehCache" />
	  </property>
	</bean>
	
	<!-- 自动代理 -->
	<aop:config>
		<!-- 这里可添加多个切入点 -->
		<aop:advisor pointcut="execution(* com.berheley.bi.report.service.impl.SummaryReportServiceImpl.getSource*(..))"   advice-ref="methodCacheInterceptor" />
		<aop:advisor pointcut="execution(* com.berheley.bi.report.service.impl.SummaryReportServiceImpl.getPrefixAndSuffix*(..))"   advice-ref="methodCacheAfterAdvice" />
	</aop:config>
</beans>


4.引入ehcache.xml文件

<!-- AOP+Ehcache by mengqingyu-->

<!-- 
	diskStore 当内存缓存数量超过设置数量时,将数据写到硬盘中,path为硬盘相对路径 
	maxElementsInMemory 缓存中最大允许创建的对象数
	eternal 缓存中对象是否为永久,如果true过期设置将无效 
	timeToIdleSeconds 自最后读取开始有效时间 
	timeToLiveSeconds 自创建开始有效时间
	overflowToDisk 内存不足时是否启用磁盘缓存 
-->
<ehcache>
	<diskStore path="java.io.tmpdir"/> 
	
	<defaultCache 
		maxElementsInMemory="10000" 
		eternal="false" 
		overflowToDisk="true" 
		timeToIdleSeconds="120" 
		timeToLiveSeconds="120" 
		diskPersistent="false" 
		diskExpiryThreadIntervalSeconds="120"/>
		
  	<cache name="systemCache"
        maxElementsInMemory="10000"  
        eternal="false" 		
        timeToIdleSeconds="259200000" 
        timeToLiveSeconds="259200000"  
        overflowToDisk="true"/>    

</ehcache>

猜你喜欢

转载自mengqingyu.iteye.com/blog/1310480