基本上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 jar 包
描述一下Spring AOP?...Spring中有哪些不同的通知类型(advice types)? Spring AOP 代理是什么? 引介(Introduction)是什么? 连接点(Joint Point)和切入点(Point Cut)是什么? 织入(Weaving)是什么?
spring aop spring aop spring aop spring aop spring aop spring aop spring aop spring aop spring aop
spring-aop-1.1.1.jar spring-aop-1.2.6.jar spring-aop-1.2.9.jar spring-aop-2.0.2.jar spring-aop-2.0.6.jar spring-aop-2.0.7.jar spring-aop-2.0.8.jar spring-aop-2.0.jar spring-aop-2.5.1.jar spring-aop-...
AOP流程源码分析-SpringAOP中定义的类图AOP流程源码分析-SpringAOP中定义的类图AOP流程源码分析-SpringAOP中定义的类图AOP流程源码分析-SpringAOP中定义的类图AOP流程源码分析-SpringAOP中定义的类图AOP流程源码分析...
一、适合人群 1、具备一定Java编程基础,初级开发者 2、对springboot,mybatis,mysql有基本认识 3、对spring aop认识模糊的,不清楚如何实现Java 自定义注解的 ...4、spring boot,mybatis,druid,spring aop的使用
基于注解实现SpringAop基于注解实现SpringAop基于注解实现SpringAop
springAOP详解
springaop依赖的jar包,spring版本2.5.6,如果需要,可以下载使用,欢迎各位评论指出不足
Spring框架的关键组件之一是面向方面编程(AOP)框架。 面向方面的编程需要将程序逻辑分解成不同的部分。 此教程将通过简单实用的方法来学习Spring框架提供的AOP/面向方面编程。
死磕Spring之AOP篇 - Spring AOP两种代理对象的拦截处理(csdn)————程序
spring aop的demo spring aop的demo
Spring AOP 几个不同使用方法的完整例子,使用Junit4c测试, 在我的博客上有不同配置组合的说明,可以参考
spring aop切面拦截指定类和方法实现流程日志跟踪 一般情况下,在不侵入业务代码的情况下,需要做流程日志跟踪是比较合理的 采用springaop切面思想
开发工具 spring-aop-4.3.6.RELEASE开发工具 spring-aop-4.3.6.RELEASE开发工具 spring-aop-4.3.6.RELEASE开发工具 spring-aop-4.3.6.RELEASE开发工具 spring-aop-4.3.6.RELEASE开发工具 spring-aop-4.3.6.RELEASE...
AOP的意思就是面向切面编程。本文主要是通过梳理JDK中自带的反射机制,实现 AOP动态代理模式,这也是Spring AOP 的实现原理
Spring aop Spring aop
springAOP所需jar包 springAOP所需jar包 springAOP所需jar包 springAOP所需jar包 springAOP所需jar包
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器...Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中
NULL 博文链接:https://306963591.iteye.com/blog/1129837