拿到spring proxy的target class

前两天ahuaxuan同学的帖子[url="http://www.iteye.com/topic/263895"]aop cache再讨论[/url],讲述了利用AOP来实现method cache,写的很好,但是有一个遗憾,就是那个代码不能对代理对象实现cache,下面是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方法实现的完美一点,可以让我们直接使用。

所有代码在附件中,谢谢。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值