基本上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广泛应用于事务管理。例如,我们可以定义一个切面来处理所有数据库操作的事务,这样无需在每个业务方法中显式调用开始和提交事务,只需在切面中配置事务规则即可。 `aop-jar`这个压缩包...
在OOP中,这些关注点可能会分散在各个对象中,而在AOP中,它们被集中处理,称为横切关注点,横切关注点是那些影响整个应用的共同关注点,如安全、日志等。 Spring AOP提供了在不修改源代码的情况下,动态插入这些横...
Spring AOP(面向切面编程)是Spring框架的重要组成部分,它提供了一种模块化和声明式的方式来处理系统中的交叉关注点问题,如日志、...通过实践这些示例,可以更好地理解Spring AOP的精髓,从而在实际项目中灵活运用。
在Spring中,我们通常使用表达式或者注解来定义切点。例如,我们可以使用`@Before`、`@After`、`@Around`、`@AfterReturning`和`@AfterThrowing`等注解来声明前置、后置、环绕、返回后和异常后通知,这些通知在切点...
在Spring AOP中,我们可以通过定义切面(Aspect)来封装横切关注点,从而实现代码的解耦和模块化。切面通常包含通知(Advice)、切点(Pointcut)和织入(Weaving)等概念。 首先,让我们详细了解一下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-...
Spring AOP 是一种面向切面编程的技术,它允许我们在不修改源代码的情况下,对应用程序的特定部分(如方法调用)进行增强。在 Spring 中,AOP 的实现主要依赖于代理模式,有两种代理方式:JDK 动态代理和 CGLIB 动态...
在Spring AOP中,我们无需深入到每个方法的实现细节,而是可以定义“切面”,在合适的时机(如方法调用前、后、异常发生时等)执行特定的逻辑。这样,业务代码和关注点(如日志、事务管理等)得以分离,提高了代码的...
在Spring AOP中,这个对象通常是业务逻辑实现类。 7. **AOP代理(AOP Proxy)**:由AOP框架创建的对象,用于包含通知逻辑。Spring提供了两种类型的代理:JDK动态代理和CGLIB代理。 8. **编织(Weaving)**:是指将...
自定义类加载器允许我们在特定条件下加载类,例如,当类需要被CGLIB增强时,自定义类加载器可以在加载过程中应用AOP增强。 ### 4. Spring AOP配置 Spring AOP的配置可以通过XML或注解方式进行: - **XML配置**: ...
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的使用
在实际应用中,这三个jar包的协同工作使得Spring AOP能够实现以下功能: - **切点定义**:使用AspectJ的切点表达式(例如`execution(* com.example.service.*.*(..))`),定义需要增强的方法或类。 - **通知类型**...
在Java应用中,AOP通过代理模式实现了切面编程,使得我们可以将业务逻辑与横切关注点分离,提高代码的可复用性和可维护性。 在描述中提到的"spring aop 五个依赖jar"是实现Spring AOP功能必不可少的库文件,让我们...
Spring AOP(面向切面编程)允许开发者在不修改源代码的情况下,通过“切面”来插入新的行为或增强已有功能,而 Spring IOC(控制反转)则负责管理对象的生命周期和依赖关系,极大地简化了应用的架构。 **Spring ...
标题中的“在自定义Spring AOP中使用EL获取拦截方法的变量值”指的是在Spring的面向切面编程(AOP)中,通过Expression Language(EL,表达式语言)来访问被拦截方法的局部变量值。这通常涉及到Spring的代理机制、...
在本文中,我们将深入探讨Spring AOP的运用,并结合源码分析其工作原理。 首先,了解AOP的基本概念: 1. 切面(Aspect):切面是关注点的模块化,这些关注点通常是跨越多个对象的横切关注点,例如事务管理、日志...
在Spring AOP中,我们通常使用@Aspect注解来定义切面类。切面类中可以包含多个通知(Advice),包括前置通知(Before)、后置通知(After)、返回通知(After-returning)、异常通知(After-throwing)和环绕通知...
在`springAop1`这个压缩包中,可能包含了一个简单的应用示例,展示了如何定义一个切面类,以及如何在该类中定义通知方法。例如,我们可能会看到一个名为`LoggingAspect`的类,其中包含了`@Before`注解的方法,用于在...
基于注解实现SpringAop基于注解实现SpringAop基于注解实现SpringAop