/**
*作者:张荣华
*日期:2008-11-07
**/
开门见山,一刀见血,让我们说说烦人的aop cache.
aop cache解释使用aop技术的cache,可以cache被代理对象的方法返回结果,还可以通过方法的参数值来控制缓存的粒度,看上去很美,用的人估计也颇多,好东西啊,面试的时候经常有人告诉我"我用过aop cache",看来是居家必备啊.不过居家必备的东西也得升个级什么滴啊,就想汽车一样,每年拉一次皮,照卖,还自夸是新一袋.aop cache要升级得先看看它烦人得地方.看看它烦人得地方先得知道它得用法,那么就先简单介绍一下它得用法:
常见步骤,2步
1,建立一个拦截器类,环绕增强或者后增强都可以,代码如下:
/**
* @author ahuaxuan(aaron zhang) 代码原主是一个老外,不是我
* @since 2008-5-13
* @version $Id: MethodCacheInterceptor.java 814 2008-05-13 06:52:54Z aaron $
*/
@Component("methodCacheInterceptor")
@GlobalAutowired//这个是俺写的globalautowired,大家可以忽略
public class MethodCacheInterceptor implements MethodInterceptor {
private Cache methodCache;
public void setMethodCache(Cache methodCache) {
this.methodCache = methodCache;
}
public Object invoke(MethodInvocation invocation) throws Throwable {
String targetName = invocation.getThis().getClass().getName();
String methodName = invocation.getMethod().getName();
Object[] arguments = invocation.getArguments();
Object result;
String cacheKey = getCacheKey(targetName, methodName, arguments);
Element element = methodCache.get(cacheKey);
if (element == null) {
result = invocation.proceed();
element = new Element(cacheKey, (Serializable) result);
methodCache.put(element);
}
return element.getValue();
}
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();
}
}
这段代码很简单,就是缓存某个方法的返回结果,使用的缓存组件是ehcache,ehcache的比较详细的用法ahuaxuan在
http://www.iteye.com/topic/128458这篇文章中已经有了说明.
而配置自动代理:
<!-- method cache auto proxy, add by ahuaxuan -->
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>aaComponent</value>
<value>bbComponent</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>methodCacheInterceptor</value>
</list>
</property>
</bean>
Over,最简单的aop cache.使用了该aop cache之后,可以缓存方法返回结果于无形,又可以根据方法参数来控制缓存粒度, 实乃居家旅行,杀人越货的必备良药
那么接下来看看这个用法有没有什么问题,相信熟悉一点的童子一眼就看出来了:”糟了,aaComponent和bbComponent所有的方法都被拦截了”.这个代码着实让我焦虑,我很焦虑.
Ok,我改,我改正则表达式还不行吗,我可以通过正则表达式让某些特定方法名的方法才被拦截处理.好啊,正统的spring用法,于是advice变成了advisor,增强变成了增强器, 但是我怎么看着就这么扭呢,难道我要缓存一个方法的结果还非得把这个方法的名字按照某个固定的格式来取, 再着,两个get方法,一个getxxx(),一个getyyy,两个之中一个需要缓存,另外一个不需要缓存(靠,真是变态),怎么办呢?正则的方式让我很烦躁,非常烦躁.
第一种方法让我焦虑,而第二种方法让我烦躁,我应该去寻找解决焦虑和烦躁的方案.
写代码需要有灵感,也需要有很强的分析能力,我们来看看我的问题是什么:
问题重新描述:不能精确的控制某个对象的某个方法需要被缓存.
思考:如何固定这个方法的标示-------------------
hardcode方法名到methodinterceptor中
hardcode缓存标示到方法上(如果该类所有方法都需要被缓存,那么hardcode缓存标示到类上)
我选第二种.那么看看实现步骤:
1.annotation类,两个:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodCache {
}
还有一个:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ObjectCache {
}
看上去是多么无聊的两个annotation.
2修改methodinterceptor,加上判断逻辑
如果被代理的类加了ObjectCache,那么拦截这个对象所有的方法,如果没有类上没有加ObjectCache,那么判断method上有没有加methodcache,如果加了,拦截该方法,如果没有加,直接调用目标类的方法.
于是代码变成:
public Object invoke(MethodInvocation invocation) throws Throwable {
String targetName = invocation.getThis().getClass().getInterfaces()[0].getName();
String methodName = invocation.getMethod().getName();
Object[] arguments = invocation.getArguments();
if (invocation.getThis().getClass().isAnnotationPresent(ObjectCache.class)) {
return getResult(targetName, methodName, arguments, invocation);
} else {
if (invocation.getMethod().isAnnotationPresent(MethodCache.class)) {
return getResult(targetName, methodName, arguments, invocation);
} else {
return invocation.proceed();
}
}
}
private Object getResult(String targetName, String methodName, Object[] arguments, MethodInvocation invocation) throws Throwable {
Object result;
String cacheKey = getCacheKey(targetName, methodName, arguments);
Element element = methodCache.get(cacheKey);
if (element == null) {
result = invocation.proceed();
element = new Element(cacheKey, (Serializable) result);
methodCache.put(element);
}
return element.getValue();
}
Ok,试试把,现在我要拦截aaservice上所有的方法,那么我的代码如下:
@ObjectCache
public class AaService implement xxxxxx{
}
如果我要拦截bbservice上的b1方法,代码如下:
public class BbService implement xxxxxx{
@MethodCache
public void bb() {
}
}
好了,目的达到了,我们可以任意的指定需要要拦截某个类的全部,或者部分方法了. 可是心中好像还是很闷的慌,我很慌张,非常慌张.
有人问了:都到这个份上了还慌啥张啊.
答:它tmd什么时候过期啊.我在ehcache.xml配置的可是统一的过期时间啊.ok,想到了,改,于是俺们的annotation就长成下面这个样子了:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodCache {
int expire() default 0;
}
和
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ObjectCache {
int expire() default 0;
}
再看看我们的进化过的methodInterceptor吧,大家可以详细比较一下下面这段和上面两端代码的异同之处
public Object invoke(MethodInvocation invocation) throws Throwable {
String targetName = invocation.getThis().getClass().getInterfaces()[0].getName();
String methodName = invocation.getMethod().getName();
Object[] arguments = invocation.getArguments();
Class[] cs = new Class[arguments.length];
for (int k = 0; k < arguments.length; k++) {
cs[k] = arguments[k].getClass();
}
if (invocation.getThis().getClass().getCanonicalName().contains("$Proxy")) {
if (logger.isWarnEnabled()) {
logger.warn("----- The object has been proxyed and method " +
"cache interceptor can't get the target, " +
"so the method result can't be cached which name is ------" + methodName);
}
return invocation.proceed();
} else {
if (invocation.getThis().getClass().isAnnotationPresent(ObjectCache.class)) {
ObjectCache oc = invocation.getThis().getClass().getAnnotation(ObjectCache.class);
return getResult(targetName, methodName, arguments, invocation, oc.expire());
} else {
Method[] mss = invocation.getThis().getClass().getMethods();
Method ms = null;
for (Method m : mss) {
if (m.getName().equals(methodName)) {
boolean argMatch = true;
Class[] tmpCs = m.getParameterTypes();
if (tmpCs.length != cs.length) {
argMatch = false;
continue;
}
for (int k = 0; k < cs.length; k++) {
if (!cs[k].equals(tmpCs[k])) {
argMatch = false;
break;
}
}
if (argMatch) {
ms = m;
break;
}
}
}
if (ms != null && ms.isAnnotationPresent(MethodCache.class)) {
MethodCache mc = ms.getAnnotation(MethodCache.class);
return getResult(targetName, methodName, arguments, invocation, mc.expire());
} else {
return invocation.proceed();
}
}
}
}
private Object getResult(String targetName, String methodName, Object[] arguments,
MethodInvocation invocation, int expire) throws Throwable {
Object result;
String cacheKey = getCacheKey(targetName, methodName, arguments);
Element element = methodCache.get(cacheKey);
if (element == null) {
synchronized (this) {
element = methodCache.get(cacheKey);
if (element == null) {
result = invocation.proceed();
element = new Element(cacheKey, (Serializable) result);
//annotation没有设expire值则使用ehcache.xml中自定义值
if (expire > 0) {
element.setTimeToIdle(expire);
element.setTimeToLive(expire);
}
methodCache.put(element);
}
}
}
return element.getValue();
}
童子们可以看到invoke方法加了一些判断(比如说类名中是否含有$Proxy),主要是防止越来越多的代理层次,如果被methodcacheinterceptor拦截到的类是一个代理类,那么ahuaxuan暂时还没有找到可以得到该代理类的目标类的方法(望知情者告之,不甚感激).
好了,好像可以告一段落了,因为现在既可以指定缓存某个类所有方法的返回结果,也可以只缓存某个类的某些方法的结果,而且还可以指定某个方法的结果被缓存多长的时间.嗯.
有童子说了:”等等,还有一个需求,我一个类中只有一个方法不需要缓存结果,其他都要缓存结果,怎么办?”
答:别烦了好吗,你就不能自己写一个@MethodNoCache吗,和@ObjectCache联合使用不就解决问题了吗.
文章最后,附上ahuaxuan的源代码,让各位见笑了.
分享到:
相关推荐
public Object cache(ProceedingJoinPoint pjp, Cacheable cacheable) throws Throwable { // 缓存逻辑... } } ``` 最后,我们讨论一下鉴权。在SpringBoot中,权限验证可以通过AOP来实现,例如使用Spring ...
接下来,我们将讨论如何整合Spring AOP和EhCache来创建一个简单的缓存实例: 1. **引入依赖**:首先,你需要在项目的pom.xml文件中添加Spring AOP和EhCache的相关依赖。确保引入最新版本的Spring Framework和...
下面我们将详细讨论这个整合过程中的关键知识点。 首先,让我们了解Spring Cache。Spring Cache提供了一个统一的API,用于在应用中添加缓存功能,无论你选择哪种缓存提供商。它通过AOP(面向切面编程)来实现,可以...
在本文中,我们将讨论如何给DAL层加上Cache,以提高系统的性能和减少数据库访问次数。我们将通过一个实例来说明如何实现缓存机制,并讨论使用AOP(Aspect-Oriented Programming)框架来解耦合数据库访问和缓存逻辑。...
接下来,我们讨论如何将 Redis 集成到 AutoFac 中。为了实现缓存功能,我们需要一个支持 Redis 的缓存提供者。可以使用 StackExchange.Redis 这个库,它是官方推荐的 .NET 客户端,提供了丰富的 API 来操作 Redis ...
在实现这些功能时,开发者会运用到Spring Boot的各种特性,如AOP(面向切面编程)进行日志记录,Spring Data JPA或MyBatis进行数据操作,Spring Cache实现缓存,以及Spring Boot Actuator监控应用健康状态等。...
对于Spring Cache,我们需要添加`spring-context`、`spring-aop`和`spring-expression`等核心模块,以及`spring-data-redis`模块来支持Redis。同时,还需要引入Redis客户端Jedis或Lettuce的依赖。 ```xml <!-- ...
- **事务处理**:讨论 PojoCache 如何支持事务,包括事务的提交与回滚机制。 #### 3. 配置 为了使用 JBoss Cache,首先需要下载 JBoss Cache 3.x 发行版。可以从官方下载页面 ...
TreeCache模块提供树形结构的缓存,而TreeCacheAOP则利用面向切面编程(AOP)对POJO进行动态管理。 OSCache是由OpenSymphony开发的高性能J2EE缓存框架,它允许开发者在JSP页面内直接实现内存缓冲。OSCache的特点包括...
【描述】"使用spring对缓存ehcache进行管理"表明我们将讨论如何通过Spring框架来配置和控制Ehcache的使用。Spring 提供了 Cache Abstraction,使得集成各种缓存系统变得简单,其中包括Ehcache。 首先,要使用Spring...
我们将讨论以下几个核心知识点: 1. **Spring框架**:Spring是一个开源的Java平台,它简化了Java企业级应用程序的开发。它提供了依赖注入(DI)、面向切面编程(AOP)以及一系列的模块,如数据访问、Web MVC、测试...
10. **优化**:性能优化包括使用缓存技术(如Spring Cache或Redis),调整数据库连接池配置,以及利用Spring AOP进行事务管理优化等。 总之,SSM是一个强大的Java Web开发组合,它提供了完整的功能,从控制层到持久...
例如,利用Spring的AOP特性实现全局事务管理,或者通过MyBatis的Cache接口自定义缓存机制。此外,对于性能优化,如批处理SQL、减少数据库连接开销等方面也会有所涉及。 最后,可能还会介绍如何将Spring-MYBatis应用...
本内容将围绕Spring 5的关键特性和性能优化策略展开讨论。 1. **Spring 5新特性** - **WebFlux**: 引入了响应式编程模型,支持非阻塞I/O,提高服务器处理高并发的能力。 - **WebSocket支持增强**: 提供更好的实时...
24. **Cache技术**:Ehcache和Memcached的原理及使用。 25. **SQL优化**:涉及查询优化、索引的原理和使用。 26. **Oracle数据库**:理解rownum和rowid的区别,以及分页查询的方法。 27. **执行计划分析**:学习...
10. 扩展与高级主题:涉及Hibernate的事件监听、拦截器、动态模型、批量操作和CGLIB/AOP集成等内容,帮助读者掌握更高级的Hibernate技巧。 11. 实战案例:书中可能会提供一些实际项目案例,让读者将所学知识应用于...
1. hutool-aop:封装了JDK动态代理,提供了非依赖于IOC容器的面向切面编程(AOP)功能。 2. hutool-bloomFilter:实现了布隆过滤器,用于快速地判断元素是否存在集合中。 3. hutool-cache:提供了简单易用的缓存功能...
2. 论坛板块:设计不同的讨论区,用户可以根据兴趣选择参与讨论。可以使用Spring Data JPA来操作数据库,方便查询和更新板块信息。 3. 主题与帖子:用户可以发布新主题或在已有的主题下发表回复。需要考虑帖子的...
它提供了一个AOP(面向切面编程)框架,允许在不修改代码的情况下进行功能增强。此外,Spring还支持JDBC抽象层和事务管理,降低了数据库操作的复杂性。 2. **Struts框架**:Struts作为MVC(模型-视图-控制器)架构...
学术论坛是一个在线平台,旨在促进学术交流和知识分享,用户可以发布、讨论学术文章,提出问题,进行答疑解惑。随着互联网技术的发展,学术论坛管理系统的需求日益增长,因此,采用现代化的技术框架如SSM来构建这样...