浏览 5453 次
该帖已经被评为良好帖
|
|
---|---|
作者 | 正文 |
发表时间:2008-11-19
最后修改:2008-11-19
aop cache再讨论,讲述了利用AOP来实现method cache,写的很好,但是有一个遗憾,就是那个代码不能对代理对象实现cache,下面是ahuaxuan的代码
前两天ahuaxuan同学的帖子
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(); 上述代码中,如果invocation.getThis()得到的对象是一个spring利用JDK dynamic proxy创建的代理,那么其getClass().getName()方法返回的是一种类似$Proxy21之类的名字,就无法利用其class name配置相应的cache了,所以ahuaxuan才写了上面的代码,碰到是dynamic proxy,则跳过cache处理,造成了遗憾,我们就试着弥补一下ahuaxuan的遗憾,经过观察spring代码,找到了类org.springframework.aop.support.AopUtils的方法getTargetClass,其java doc也说明了该方法可以根据proxy获得其target class,代码如下, public static Class getTargetClass(Object candidate) { Assert.notNull(candidate, "Candidate object must not be null"); if (candidate instanceof TargetClassAware) { return ((TargetClassAware) candidate).getTargetClass(); } if (isCglibProxyClass(candidate.getClass())) { return candidate.getClass().getSuperclass(); } return candidate.getClass(); } 短短几行代码,我们发现,这个方法对dynamic proxy根本不起作用,它只适用于实现了TargetClassAware接口的对象,或者由cglib产生的代理对象,但是jdk提供的dynamic proxy是我们最常用的创建代理的方式,是spring的默认行为,难道就不行了吗?当然不是了!(好老套的句型。。。),我们知道在spring中,创建proxy主要有两种方式,一种是cglib,其核心类为org.springframework.aop.framework.Cglib2AopProxy,另一种是最常用的jdk提供的dynamic proxy机制,核心类是org.springframework.aop.framework.JdkDynamicAopProxy,而这个JdkDynamicAopProxy类也是创建dynamic proxy的核心,既它实现了java.lang.reflect.InvocationHandler接口,而通过java.lang.reflect.Proxy的静态方法getInvocationHandler可以从一个proxy得到其对应的InvocationHandler,这就是问题关键了,拿到了InvocationHandler的实例,也就是JdkDynamicAopProxy,就可以通过反射拿到它携带的AdvisedSupport对象,从而拿到target class! JdkDynamicAopProxy的主要代码如下: class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable { /** Config used to configure this proxy */ private final AdvisedSupport advised; 而我们实现的代码如下 private static final String ADVISED_FIELD_NAME = "advised"; private static final String CLASS_JDK_DYNAMIC_AOP_PROXY = "org.springframework.aop.framework.JdkDynamicAopProxy"; public static Class getTargetClass(Object candidate) { if (!org.springframework.aop.support.AopUtils.isJdkDynamicProxy(candidate)) { return org.springframework.aop.support.AopUtils.getTargetClass(candidate); } return getTargetClassFromJdkDynamicAopProxy(candidate); } private static Class getTargetClassFromJdkDynamicAopProxy(Object candidate) { try { InvocationHandler invocationHandler = Proxy.getInvocationHandler(candidate); if (!invocationHandler.getClass().getName().equals(CLASS_JDK_DYNAMIC_AOP_PROXY)) { //在目前的spring版本,这处永远不会执行,除非以后spring的dynamic proxy实现变掉 log.warn("the invocationHandler of JdkDynamicProxy isn`t the instance of " + CLASS_JDK_DYNAMIC_AOP_PROXY); return candidate.getClass(); } AdvisedSupport advised = (AdvisedSupport) new DirectFieldAccessor(invocationHandler).getPropertyValue(ADVISED_FIELD_NAME); Class targetClass = advised.getTargetClass(); if (Proxy.isProxyClass(targetClass)) { // 目标类还是代理,递归 Object target = advised.getTargetSource().getTarget(); return getTargetClassFromJdkDynamicAopProxy(target); } return targetClass; } catch (Exception e) { log.error("get target class from " + CLASS_JDK_DYNAMIC_AOP_PROXY + " error", e); return candidate.getClass(); } } 这样就OK了,扩展了AopUtils.getTargetClass方法,从而支持获得dynamic proxy的target class,上述代码里的JdkDynamicAopProxy不是public的,是我们通过反射来强行拿到其里面的代理信息,这种方式属于很猥琐的方式,如果以后spring升级了,那个JdkDynamicAopProxy内部的代码改了,这段代码就行不通了,呵呵,这个原理很简单,雕虫小技而已,个人认为spring完全可以把那个getTargetClass方法实现的完美一点,可以让我们直接使用。 所有代码在附件中,谢谢。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2008-11-19
最后修改:2008-11-19
根本在于,武断的将classname、methodname、parametervalue来做cache的key要素,是很“猥琐”的方案,以至于又出现这些“猥琐”的补丁。
那帖我也建议了,完全可以在method上指定@Cache(key="pattern{0}{1}")这样的方式,可惜没人听啊 |
|
返回顶楼 | |
发表时间:2008-11-19
最后修改:2008-11-19
sorphi 写道 根本在于,武断的将classname、methodname、parametervalue来做cache的key要素,是很“猥琐”的方案,以至于又出现这些“猥琐”的补丁。
那帖我也建议了,完全可以在method上指定@Cache(key="pattern{0}{1}")这样的方式,可惜没人听啊 你说的有道理,cache那个地方可以有很多种处理方式,但这个不仅限于method cache的补丁,在不少的情况下,需要拿到proxy的target class,都可以用这样的方式,在这里只是从method cache那篇文章引出来而已。 |
|
返回顶楼 | |
发表时间:2008-11-19
哦了
spring 2.5加入aspectj的支持后,ProceedingJoinPoint更方便拿到target了 |
|
返回顶楼 | |