Spring Aop Cache在Jpetstore中的应用

    基本上cache是通过key-value的形式来缓存数据,通过key来获取缓存的数据。尤其开源cache既不像内存数据库,可以支持任意组合条件的查询,也不像tangosol等商业cache,可以笨重的支持按value的属性查询。

 

    cache缓存对于应用来说,如何组织key以方便的管理和命中缓存是至关重要的,现在网上流行的针对查询的key是[Class Name]+[Method Name]+{[Argument Type]+[Argument Value]}(0-n).如果Argument Value是复杂对象,继续分解等。这种缓存的数据存在一个如何保持与数据库数据一致的问题,现在网上看到的都是通过定时刷新清空cache的策略。

 

    还有一种缓存是针对单个对象的缓存,采用[Object Name]+[Object ID]的key存放方式。当对象内容改变时,只需要更新这个对象即可。

 

    所有的缓存不能做基于value维度的查询,这就导致了基于条件查询的数据因此存在重复缓存的问题,现时也没有什么好的解决方案,所以我们只能好好规划需要缓存的数据。

 

    网上已经有很多类似的AOP cache例子了,我只是参照自己动手实践一下。主要参照的是http://opensource.atlassian.com/confluence/spring/display/DISC/AOP+Cache 。下面的例子是在其上进行的简化。

 

1.如何实现

    1.1 spring配置

 

        <bean id="cacheInterceptor" 
                   class="org.springframework.aop.cache.MemoryCacheInterceptor"/>
        <bean id="jpetstoreManagerAdvisor" 
                   class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
		<property name="advice">
			<ref bean="cacheInterceptor"/>
		</property>
		<property name="patterns">
			<list>
				<value>org.springframework.samples.jpetstore.domain.logic.PetStoreImpl.getProduct</value>
			</list>
		</property>
	</bean>	
	<bean id="jpetstoreManagerCacheProxyCreator" 
                  class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
	    <property name="beanNames"><value>petStore</value></property>
	    <property name="interceptorNames">
	        <list>
	        	<value>jpetstoreManagerAdvisor</value>
	        </list>
	    </property>
	</bean>

 

 

    CacheInterceptor

 

package org.springframework.aop.cache;

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.ObjectUtils;

public abstract class CacheInterceptor implements MethodInterceptor, InitializingBean {
	private static Log log = LogFactory.getLog(CacheInterceptor.class);


	private String objectDiscriminator = DEFAULT_OBJECT_DISCRIMINATOR;
       private String argumentDiscriminator = DEFAULT_ARGUMENT_DISCRIMINATOR;
       private String argumentTypeDiscriminator = DEFAULT_ARGUMENT_TYPE_DISCRIMINATOR;


	private static final String DEFAULT_OBJECT_DISCRIMINATOR = "@";	
	private static final String DEFAULT_ARGUMENT_DISCRIMINATOR = "-";   
       private static final String DEFAULT_ARGUMENT_TYPE_DISCRIMINATOR = "#";


	public void afterPropertiesSet() throws Exception {}

	public Object invoke(MethodInvocation invocation) throws Throwable {
               String cacheName = getCacheName(invocation);
		String key = getCacheKey(invocation.getThis(), invocation.getArguments(),
                                                         invocation.getMethod().getParameterTypes());
		if (log.isDebugEnabled()) {
		    log.debug("Cache key: " + key);
		}
		Object result = getFromCache(cacheName,key);
		if (result == null) {
		    if (log.isInfoEnabled()) {
		        log.info("Invoking method " + invocation.getMethod().getDeclaringClass().getName() 
		        		+ "#" + invocation.getMethod().getName());
		    }
		    result = invocation.proceed();
		    putInCache(cacheName,key,result);
		} else {
		    if (log.isInfoEnabled()) {
		    	log.info("Returning cached data for key [" + key + "] in cache [" + cacheName + "]");
		    }
		}
		return result;
	}

    protected abstract Object getFromCache(String cacheName, String key) 
    	throws CacheInterceptorException; 


    protected abstract void putInCache(String cacheName, String key, Object result) 
    	throws CacheInterceptorException;

    protected String getCacheName(MethodInvocation invocation) {
        return invocation.getMethod().getDeclaringClass().getName() 
        		+ "@" + invocation.getMethod().getName();
    }

    protected String getCacheKey(Object target, Object[] arguments, Class[] argumentClasses) 
         throws CacheInterceptorException {   
        StringBuffer result = new StringBuffer(); 
        result.append(ObjectUtils.getIdentityHexString(target));     
        if (arguments != null) {
            result.append(this.objectDiscriminator);
	        for (int i = 0; i < arguments.length; i++) {
	           if (i > 0) {
	               result.append(this.argumentDiscriminator);
	           }
               result.append(argumentClasses[i].getName());
               result.append(this.argumentTypeDiscriminator);
	           result.append(arguments[i]);
	        }
        }
        return result.toString();
    }
}

 

    MemoryCacheInterceptor

 

package org.springframework.aop.cache;

import java.util.HashMap;
import java.util.Map;

public class MemoryCacheInterceptor extends CacheInterceptor {
	private Map cache;

    public void afterPropertiesSet() throws Exception {
        super.afterPropertiesSet();   
        this.cache = new HashMap();
    }

	protected Object getFromCache(String cacheName, String key) 
            throws CacheInterceptorException {
       return this.cache.get(key);
    }

	protected void putInCache(String cacheName, String key, Object result) 
           throws CacheInterceptorException {
        this.cache.put(key,result);
    }
}

  这样配置之后在org.springframework.samples.jpetstore.domain.logic.PetStoreImpl中第一次执行getProduct是从数据库中获取,其后就是从缓存中。如何通过定时刷新缓存,可以参照http://opensource.atlassian.com/confluence/spring/display/DISC/AOP+Cache中的实例。

 

2.如何更新数据

    还有一种通过[Object Name]+[Object ID]的简单的缓存方 式,这种缓存也仅适用通过ID获取其对象的场景。这种缓存的更新可以通过AOP的AfterReturningAdvice来实现,在执行update的时候更新缓存,在执行delete操作的时候清除,或者也可在insert的时候放入缓存。下面仅说一下利用ehcache的一个当通过ID删除对象的片段:

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();
   String cacheKey = className +"-"+arg2[0].toString();
   cache.remove(cacheKey);
   logger.debug("remove cache " + cacheKey);
}

public void afterPropertiesSet() throws Exception {}

}

 

总体上,如果要设计一个合适的AOP缓存,还需要考虑很多。上面只是想到的一点点,还有缓存的集群等等。

你可能感兴趣的:(spring,AOP,bean,cache,OpenSource)