`
Norther
  • 浏览: 25751 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

拿到spring proxy的target class

阅读更多
前两天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方法实现的完美一点,可以让我们直接使用。

所有代码在附件中,谢谢。
分享到:
评论
4 楼 redhat 2014-08-27  
stupidly wrong! java proxy has many targeted interfaces, it has no concrete implementation, which means it may have no single target!
3 楼 sorphi 2008-11-19  
哦了
spring 2.5加入aspectj的支持后,ProceedingJoinPoint更方便拿到target了
2 楼 Norther 2008-11-19  
sorphi 写道
根本在于,武断的将classname、methodname、parametervalue来做cache的key要素,是很“猥琐”的方案,以至于又出现这些“猥琐”的补丁。

那帖我也建议了,完全可以在method上指定@Cache(key="pattern{0}{1}")这样的方式,可惜没人听啊


你说的有道理,cache那个地方可以有很多种处理方式,但这个不仅限于method cache的补丁,在不少的情况下,需要拿到proxy的target class,都可以用这样的方式,在这里只是从method cache那篇文章引出来而已。
1 楼 sorphi 2008-11-19  
根本在于,武断的将classname、methodname、parametervalue来做cache的key要素,是很“猥琐”的方案,以至于又出现这些“猥琐”的补丁。

那帖我也建议了,完全可以在method上指定@Cache(key="pattern{0}{1}")这样的方式,可惜没人听啊

相关推荐

    08spring4_dynamicproxy.rar

    public class Client { public static void main(String[] args) { // UserService userService=new UserServiceImpl(); // ProxyInovationHandler pih =new ProxyInovationHandler(); // pih.setTarget...

    spring框架手动提交事务,jdbctample

    <aop:aspectj-autoproxy proxy-target-class="true"> xml中要有这句,可以解决子类类报错的问题。 或者去掉这句话,同时去掉public class AccountServiceImpl implements AccountService {继承关系,也可以解决

    spring_aop4.rar_Home Home_jar 转换_spring AOP jar

    aop:aspectj-autoproxy proxy-target-class="true"/> JDK动态代理和CGLIB字节码生成的区别? * JDK动态代理只能对实现了接口的类生成代理,而不能针对类 * CGLIB是针对类实现代理,主要是对指定的类生成一个...

    SpringCodeReadingNotes:Spring源码阅读笔记-spring源码阅读

    xml扫描过后,发现把所有Bean定义打印出来,发现有一个bean叫做“ proxyTargetClass = false”; 优化=假; opaque = false; 暴露代理=假; Frozen = false”,他实际上是一个InfrastructureAdvisorAutoProxyCreator。...

    Spring.html

    修改默认代理方法:<aop:aspectj-autoproxy proxy-target-class="true"/> 增强种类 前置通知 后置通知 异常通知 最终通知 环绕通知 注意:使用注解的方式,最终通知和后置通知顺序换了,建议使用环绕通知...

    Spring.net框架

    在Step3到Step5的例子中,我们将利用Spring.net提供的Ioc框架,轻松完 成解耦以及系统改造等工作。 一、类之间的依赖 我们的第一个例子主要用于说明程序的基本构造,并且作为一个反面典型,引出为什么要解耦,以及...

    Spring3中配置DBCP,C3P0,Proxool,Bonecp数据源

    <aop:config proxy-target-class="true"/> <!-- 采用单数据源事务控制方式,通过注解来定义事务--> class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> ...

    shiro入门学习demo源码

    <property name="proxyTargetClass" value="true" /> </bean> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> ...

    cglib-nodep

    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的问题...

    spring-framework-reference-4.1.2

    3. New Features and Enhancements in Spring Framework 4.0 ............................................ 17 3.1. Improved Getting Started Experience .........................................................

    基于Spring的HttpInvoker实现改写egova_invoker.zip

    ProxyFactory.proxy 方法,生成接口的代理对象,直接调用方法客户端和服务器端的接口和实体类要一致,实体类必须是可以序列化的比如:定义Bean: public class DemoBean implements java.io.Serializable{ ...

    维生药业小项目 SSH简单学习项目

    维生药业小项目 SSH简单学习项目 <beans xmlns="http://www.springframework.... proxy-target-class="true" /> <!-- 使用annotation 自动注册bean,并检查@Required,@Autowired的属性已被注入 --> </beans>

    spring-async-type-erasure:在Spring 4.1.6.RELEASE中演示类型擦除问题

    弹簧异步型擦除这是我描述的问题的证明。 有解决办法! 当使用@EnableAsync进行通用bean限定时,需要启用CGLib代理。 这意味着当将类转换为后端的异步代理时,将保留...解决方案@EnableAsync(proxyTargetClass=true)

    Jdk动态代理 底层

    当spring没有手动开启Cglib动态代理,即:或@EnableAspectJAutoProxy(proxyTargetClass = true),默认使用的就是Jdk动态代理。动态代理的应用范围很广,例如:日志、事务管理、缓存等。本文将模拟@Cacheable,即缓...

    编程式创建Aspect代理源码

    编程式创建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.

    spring-framework-reference4.1.4

    3. New Features and Enhancements in Spring Framework 4.0 ............................................ 17 3.1. Improved Getting Started Experience .........................................................

    tomcat8 + nginx + memcached + cas 实现负载均衡的配置包

    <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权威指南 很精典的学习教程,比ANT更好用

    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 ...

    asp.net知识库

    SQL过程自动C#封装,支持从表到基本存储过程生成 使用SQLDMO控制 SQL Server 使用SQL-DMO实现定制SQL Scripts Create Tables and Build inserts from Tables by using Mygeneration Templates(Sql Server) C# 获取...

Global site tag (gtag.js) - Google Analytics