aop cache再讨论
*作者:张荣华
*日期: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();
- }
- }
/*** @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>
<!-- 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.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodCache {}
还有一个:
- @Target(ElementType.TYPE)
- @Retention(RetentionPolicy.RUNTIME)
- public @interface ObjectCache {
- }
@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();
- }
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{
- }
@ObjectCache
public class AaService implement xxxxxx{}
如果我要拦截bbservice上的b1方法,代码如下:
- public class BbService implement xxxxxx{
- @MethodCache
- public void bb() {
- }
- }
public class BbService implement xxxxxx{@MethodCachepublic void bb() {}
}
好了,目的达到了,我们可以任意的指定需要要拦截某个类的全部,或者部分方法了. 可是心中好像还是很闷的慌,我很慌张,非常慌张.
有人问了:都到这个份上了还慌啥张啊.
答:它tmd什么时候过期啊.我在ehcache.xml配置的可是统一的过期时间啊.ok,想到了,改,于是俺们的annotation就长成下面这个样子了:
- @Target(ElementType.METHOD)
- @Retention(RetentionPolicy.RUNTIME)
- public @interface MethodCache {
- int expire() default 0;
- }
@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;
- }
@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();
- }
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联合使用不就解决问题了吗.
aop cache再讨论相关推荐
- Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论
Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论 创建用户自定义的类加载器 要创建用户自定义的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的f ...
- 再讨论下古老的include
再讨论下古老的include,做c/c++局域网聊天编程的对我们都知道#include指令都不会陌生,绝大多数也都知道如何使用,但我相信仍有人对此是一知半解, C: 我们都知道#include C++ ...
- latex中英文字母大小写变换再讨论
latex中英文字母大小写变换再讨论 在英文的习惯中,特别是标题等内容中,大小写有一些常用的习惯. 在latex中如果正常输入英文段落和语句,通常作者会注意使用这些习惯,因此通常不太会需要太多的字母大 ...
- 贫血模型与充血模型再讨论
贫血模式和充血模式 Martin Fowler很早以前就写过一篇文章,题目叫"贫血模型".文章里面批判贫血的领域模型是不够优雅.不够OO的,提倡使用充血的领域模型.在Java世界里 ...
- 《结合DDD讲清楚编写技术方案的七大维度》再讨论
欢迎大家关注公众号「JAVA前线」查看更多精彩分享文章,主要包括源码分析.实际应用.架构思维.职场分享.产品思考等等,同时欢迎大家加我个人微信「java_front」一起交流学习 1 前文回顾 我在之 ...
- 关于字符串和字符数组的再讨论
字符串指针与字符数组(转载) 选择自 happycock 的 Blog 很多刚从C转C++的人都不明白,在C中这样的代码 char *pChar="hELLO!"; //定义字符指 ...
- 再讨论下webdriver
webdriver协议 何为协议?协议就是客户端和服务器规定好的通信方式. web driver协议规定了某一个操作(比如find_element_by_id),客户端需要向服务器的哪个路由(其实是通 ...
- 一个积分不等式的再讨论
转载于:https://www.cnblogs.com/zhangwenbiao/p/4738923.html
- matlab比Excel好在哪,科学网—再讨论浮点运算精度问题(IDL与Excel和Matlab相比) - 董彦卿的博文...
论坛里面一个哥们的问题,原帖地址:http://bbs.esrichina-bj.cn/ESRI/viewthread.php?tid=90950&page=1&extra=#pid8 ...
- 美债危机再讨论--》债务危机的本质是流动性短缺
为什么80%的码农都做不了架构师?>>> 深圳-刘小兵(878202407) 10:49:44 Will 10:43:09 美债分两种,政府债与民间债.政府债只是用来做秀的 ...
最新文章
- 全球及中国潜水压力传感器行业运行态势及发展战略研究报告2022-2027年
- DataTables提示:DataTables warning: table id=queueInfo_table - Requested unknown parameter 'type' for r
- What happens when passing an non-exist text object to API
- java5.0下载_java虚拟机
- java的reentrantlock_Java并发之ReentrantLock详解
- 有规律字段拆分(2005的解决方案)
- Scrum立会报告+燃尽图(十月二十二日总第十三次)
- mybatisplus 操作另一个数据库的数据_实例分析:python操作数据库项目
- Linux查看、运行和杀死java进程
- 自托管websocket和webapi部署云服务器域名及远程访问
- 我是如何将博客转成PDF的
- 快速部署PostgreSQL
- 必须掌握的30种SQL语句优化
- 浙大PAT 1063
- Android事件机制深入探讨(一)
- 计算机作文 六年级,我和电脑600字_六年级作文_小学作文 - 265学校教育网
- 360隐私保险箱 vs misuo
- layui单据打印_layui打印表格自定义函数
- 洛谷P1125笨小猴C语言
- 《机器学习实战》支持向量机(手稿+代码)
热门文章
- 转]分享一个可以下载全球影像的网站(包括历史影像)
- 主数据文件损坏(或丢失)情况下,如何备份尾部事务日志.
- Underscore骨骼
- 文件管理器 Root Explorer v2.9.4 零售完全版
- Gaze Estimation学习笔记(1)-Appearance-Based Gaze Estimation in the Wild
- 063 模块的四种形式
- node.js如何制作命令行工具(一)
- sharepoint portal server 2003--入门资料
- 数据结构学习---有序链表的合并
- javahost:使用虚拟DNS省掉开发环境配置hosts文件