前两天ahuaxuan同学的帖子
aop cache再讨论,讲述了利用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方法实现的完美一点,可以让我们直接使用。
所有代码在附件中,谢谢。
分享到:
相关推荐
public class Client { public static void main(String[] args) { // UserService userService=new UserServiceImpl(); // ProxyInovationHandler pih =new ProxyInovationHandler(); // pih.setTarget...
<aop:aspectj-autoproxy proxy-target-class="true"> xml中要有这句,可以解决子类类报错的问题。 或者去掉这句话,同时去掉public class AccountServiceImpl implements AccountService {继承关系,也可以解决
aop:aspectj-autoproxy proxy-target-class="true"/> JDK动态代理和CGLIB字节码生成的区别? * JDK动态代理只能对实现了接口的类生成代理,而不能针对类 * CGLIB是针对类实现代理,主要是对指定的类生成一个...
xml扫描过后,发现把所有Bean定义打印出来,发现有一个bean叫做“ proxyTargetClass = false”; 优化=假; opaque = false; 暴露代理=假; Frozen = false”,他实际上是一个InfrastructureAdvisorAutoProxyCreator。...
修改默认代理方法:<aop:aspectj-autoproxy proxy-target-class="true"/> 增强种类 前置通知 后置通知 异常通知 最终通知 环绕通知 注意:使用注解的方式,最终通知和后置通知顺序换了,建议使用环绕通知...
在Step3到Step5的例子中,我们将利用Spring.net提供的Ioc框架,轻松完 成解耦以及系统改造等工作。 一、类之间的依赖 我们的第一个例子主要用于说明程序的基本构造,并且作为一个反面典型,引出为什么要解耦,以及...
<aop:config proxy-target-class="true"/> <!-- 采用单数据源事务控制方式,通过注解来定义事务--> class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> ...
<property name="proxyTargetClass" value="true" /> </bean> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> ...
org.springframework.aop.framework.AopConfigException: Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces 从异常信息可以看出是cglib的问题...
3. New Features and Enhancements in Spring Framework 4.0 ............................................ 17 3.1. Improved Getting Started Experience .........................................................
ProxyFactory.proxy 方法,生成接口的代理对象,直接调用方法客户端和服务器端的接口和实体类要一致,实体类必须是可以序列化的比如:定义Bean: public class DemoBean implements java.io.Serializable{ ...
维生药业小项目 SSH简单学习项目 <beans xmlns="http://www.springframework.... proxy-target-class="true" /> <!-- 使用annotation 自动注册bean,并检查@Required,@Autowired的属性已被注入 --> </beans>
弹簧异步型擦除这是我描述的问题的证明。 有解决办法! 当使用@EnableAsync进行通用bean限定时,需要启用CGLib代理。 这意味着当将类转换为后端的异步代理时,将保留...解决方案@EnableAsync(proxyTargetClass=true)
当spring没有手动开启Cglib动态代理,即:或@EnableAspectJAutoProxy(proxyTargetClass = true),默认使用的就是Jdk动态代理。动态代理的应用范围很广,例如:日志、事务管理、缓存等。本文将模拟@Cacheable,即缓...
编程式创建Aspect代理源码 You can use the org.springframework.aop.aspectj.annotation.AspectJProxyFactory class to create a proxy for a target object that is advised by one or more @AspectJ aspects.
3. New Features and Enhancements in Spring Framework 4.0 ............................................ 17 3.1. Improved Getting Started Experience .........................................................
<bean class="net.spy.memcached.spring.MemcachedClientFactoryBean" p:servers="127.0.0.1:11211" p:protocol="BINARY" p:locatorType="ARRAY_MOD" p:failureMode="Redistribute" p:transcoder-ref=...
Maven权威指南 Authors Tim O'Brien (Sonatype, Inc.) , John Casey (Sonatype, Inc.) , Brian Fox (Sonatype, Inc.) , Bruce Snyder () , Jason Van Zyl (Sonatype, Inc.) , Juven Xu () Abstract ...
SQL过程自动C#封装,支持从表到基本存储过程生成 使用SQLDMO控制 SQL Server 使用SQL-DMO实现定制SQL Scripts Create Tables and Build inserts from Tables by using Mygeneration Templates(Sql Server) C# 获取...