`
jinnianshilongnian
  • 浏览: 21433941 次
  • 性别: Icon_minigender_1
博客专栏
5c8dac6a-21dc-3466-8abb-057664ab39c7
跟我学spring3
浏览量:2404932
D659df3e-4ad7-3b12-8b9a-1e94abd75ac3
Spring杂谈
浏览量:2997590
43989fe4-8b6b-3109-aaec-379d27dd4090
跟开涛学SpringMVC...
浏览量:5631404
1df97887-a9e1-3328-b6da-091f51f886a1
Servlet3.1规范翻...
浏览量:257542
4f347843-a078-36c1-977f-797c7fc123fc
springmvc杂谈
浏览量:1593136
22722232-95c1-34f2-b8e1-d059493d3d98
hibernate杂谈
浏览量:248958
45b32b6f-7468-3077-be40-00a5853c9a48
跟我学Shiro
浏览量:5847466
Group-logo
跟我学Nginx+Lua开...
浏览量:698110
5041f67a-12b2-30ba-814d-b55f466529d5
亿级流量网站架构核心技术
浏览量:780420
社区版块
存档分类
最新评论

在spring中获取代理对象代理的目标对象工具类

 
阅读更多

 

昨天晚上一哥们需要获取代理对象的目标对象,查找了文档发现没有相应的工具类,因此自己写了一个分享给大家。能获取JDK动态代理/CGLIB代理对象代理的目标对象。

 

 

问题描述::

 

我现在遇到个棘手的问题,要通过spring托管的service类保存对象,这个类是通过反射拿到的,经过实验发现这个类只能反射取得sservice实现了接口的方法,而extends类的方法一律不出现,debug后发现这个servie实例被spring替换成jdkdynmicproxy类,而不是原始对象了,,它里面只有service继承的接口方法,而没有extends 过的super class方法,怎么调用原生对象的方法!!!!!

 

用托管的spring service类调用getClass().getName()方法,发现输出都是$proxy43这类东西!!

 

 

通过此种方式获取目标对象是不可靠的,或者说任何获取目标对象的方式都是不可靠的,因为TargetSource,TargetSource中存放了目标对象,但TargetSource有很多种实现,默认我们使用的是SingletonTargetSource ,但还有其他的比如ThreadLocalTargetSourceCommonsPoolTargetSource 等等。

 

这也是为什么spring没有提供获取目标对象的API。

 

 

import java.lang.reflect.Field;

import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.framework.AopProxy;
import org.springframework.aop.support.AopUtils;

public class AopTargetUtils {

	
	/**
	 * 获取 目标对象
	 * @param proxy 代理对象
	 * @return 
	 * @throws Exception
	 */
	public static Object getTarget(Object proxy) throws Exception {
        
		if(!AopUtils.isAopProxy(proxy)) {
			return proxy;//不是代理对象
		}
		
		if(AopUtils.isJdkDynamicProxy(proxy)) {
			return getJdkDynamicProxyTargetObject(proxy);
		} else { //cglib
			return getCglibProxyTargetObject(proxy);
		}
		
		
        
	}


	private static Object getCglibProxyTargetObject(Object proxy) throws Exception {
		Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
        h.setAccessible(true);
        Object dynamicAdvisedInterceptor = h.get(proxy);
        
        Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
        advised.setAccessible(true);
        
        Object target = ((AdvisedSupport)advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
        
        return target;
	}


	private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {
		Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
        h.setAccessible(true);
        AopProxy aopProxy = (AopProxy) h.get(proxy);
        
        Field advised = aopProxy.getClass().getDeclaredField("advised");
        advised.setAccessible(true);
        
        Object target = ((AdvisedSupport)advised.get(aopProxy)).getTargetSource().getTarget();
        
        return target;
	}
	
}

 

 

19
23
分享到:
评论
38 楼 g_man1990 2018-04-18  
1、org.springframework.aop.support.AopUtils#isAopProxy(Object),看源码,会先判断object instanceof SpringProxy
2、这是一个JdkDynamicProxy ,并木有实现SpringProxy,<bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
3、如果绕过1,然后执行到
AopProxy aopProxy = (AopProxy) h.get(proxy); 就报错了。Proxy.getInvocationHandler会得到ManagedEntityManagerFactoryInvocationHandler
37 楼 huangjf 2017-05-22  
LZ 想问个CGLIB代理的问题。
问题描述:我写了一个类,在类的方法上面使用了注解,注解实现了原生的Inherited ,然后用cglib实现代理,结果代理出来的对象在这个方法上面这个注解丢失了,想请问下,CGLIB是有这个问题吗,还是我使用的不对,下面贴身用CGLIB实现代理代码
public Object getInstance(Object target) {
       
        targetObject=target;
        Enhancer enhancer = new Enhancer();
        //this.target = target;
        enhancer.setSuperclass(target.getClass());
        //enhancer.setStrategy();
        // 回调方法
        enhancer.setCallback(this);
        return enhancer.create();
    }
36 楼 lzc_java 2015-06-24  
陈老师好~
35 楼 qiyangyang09 2014-12-08  
我也遇到反射获取方法问题。用了你这个工具类,可以从代理中获取相应的原始对象,学习了,谢谢你。
34 楼 oqyneil 2014-09-10  
Spring的aop代理对象都实现了Advised这个接口,可以直接将代理对象转成Advised类型后通过该接口提供的方法获取目标对象,我的项目使用这个方式来获取目标对象,貌似没什么问题
33 楼 huang_yong 2014-08-03  
我也遇到了这个问题,感谢开涛的解决方案!

在 Spring 中已经提供了该特性,可调用 org.springframework.aop.support.AopUtils 类的 isCglibProxy 方法来判断实例是否被 CGLib 动态代理了。

同样地,可以调用该类的 isJdkDynamicProxy 方法来判断实例是否被 JDK 动态代理。

若不知道是动态代理方式(CGLib 或 JDK),则可以使用 isAopProxy 方法来判断。

最后,也可使用 AopUtils 的 getTargetClass 方法获取被动态代理的目标类。
32 楼 jinnianshilongnian 2013-09-20  
qingse998 写道
lz你好我最近在看spring和动态代理,发现代理的类都不是原来的对象了,但是spring中可以通过ApplicationContext ct = new ClassPathXmlApplicationContext("spring.xml");
Student st = (Student)ct.getBean("student");得到目标对象,不知道和你说的是不是同一回事


目标对象和代理对象 类型是相同的(比如相同的接口/相同的类),所以赋值是成功

类代理: 生成目标对象的子类
接口代理:生成目标对象接口的实现
31 楼 qingse998 2013-09-20  
lz你好我最近在看spring和动态代理,发现代理的类都不是原来的对象了,但是spring中可以通过ApplicationContext ct = new ClassPathXmlApplicationContext("spring.xml");
Student st = (Student)ct.getBean("student");得到目标对象,不知道和你说的是不是同一回事
30 楼 zj05409 2013-04-27  
org.springframework.aop.support.AopUtils
29 楼 jinnianshilongnian 2012-11-14  
yangpeihai 写道
jinnianshilongnian 写道
yangpeihai 写道
jinnianshilongnian 写道
yangpeihai 写道
楼主牛人啊
请教个问题,cglib是否可以访问被代理类的private方法,多谢

我记得是不能代理私有方法的  因为私有方法只有自己能调用

通常我们一个方法如果业务逻辑比较复杂,都会拆成若干个private方法,如果我想通过cglib动态代理写日志,这些private方法就捕获不到啦,你是怎么处理这种日志记录private方法问题的,给点建议哈


这个是spring aop的限制,没什么好的方法,只能开放出去(如public)。
或者使用原生的aspectj。

什么是“原生的aspectj”,能不能搞个简单例子演示一下,多谢。。。

aspectj in action
28 楼 yangpeihai 2012-11-14  
jinnianshilongnian 写道
yangpeihai 写道
jinnianshilongnian 写道
yangpeihai 写道
楼主牛人啊
请教个问题,cglib是否可以访问被代理类的private方法,多谢

我记得是不能代理私有方法的  因为私有方法只有自己能调用

通常我们一个方法如果业务逻辑比较复杂,都会拆成若干个private方法,如果我想通过cglib动态代理写日志,这些private方法就捕获不到啦,你是怎么处理这种日志记录private方法问题的,给点建议哈


这个是spring aop的限制,没什么好的方法,只能开放出去(如public)。
或者使用原生的aspectj。

什么是“原生的aspectj”,能不能搞个简单例子演示一下,多谢。。。
27 楼 jinnianshilongnian 2012-11-14  
yangpeihai 写道
jinnianshilongnian 写道
yangpeihai 写道
楼主牛人啊
请教个问题,cglib是否可以访问被代理类的private方法,多谢

我记得是不能代理私有方法的  因为私有方法只有自己能调用

通常我们一个方法如果业务逻辑比较复杂,都会拆成若干个private方法,如果我想通过cglib动态代理写日志,这些private方法就捕获不到啦,你是怎么处理这种日志记录private方法问题的,给点建议哈


这个是spring aop的限制,没什么好的方法,只能开放出去(如public)。
或者使用原生的aspectj。
26 楼 yangpeihai 2012-11-14  
jinnianshilongnian 写道
yangpeihai 写道
楼主牛人啊
请教个问题,cglib是否可以访问被代理类的private方法,多谢

我记得是不能代理私有方法的  因为私有方法只有自己能调用

通常我们一个方法如果业务逻辑比较复杂,都会拆成若干个private方法,如果我想通过cglib动态代理写日志,这些private方法就捕获不到啦,你是怎么处理这种日志记录private方法问题的,给点建议哈
25 楼 jinnianshilongnian 2012-11-14  
yangpeihai 写道
楼主牛人啊
请教个问题,cglib是否可以访问被代理类的private方法,多谢

我记得是不能代理私有方法的  因为私有方法只有自己能调用
24 楼 yangpeihai 2012-11-14  
楼主牛人啊
请教个问题,cglib是否可以访问被代理类的private方法,多谢
23 楼 gigi_112 2012-09-26  
不错 楼主如果用个递归可能会更准确
22 楼 m635674608 2012-09-12  
当为cglib代理时,为什么不取CGLIB$CALLBACK1. target也也一样啊,,,
21 楼 jinnianshilongnian 2012-08-16  
zys0523 写道
受教了,楼主很牛
提个小意见.
Field h = proxy.getClass().getSuperclass().getDeclaredField("h"); 
        h.setAccessible(true); 
        AopProxy aopProxy = (AopProxy) h.get(proxy); 

这三行可以换成JDK原生自带的
java.lang.reflect.Proxy.getInvocationHandler
    public static InvocationHandler getInvocationHandler(Object proxy)


JDK这种方式只能获取 InvocationHandler 这个呀,是获取不到目标对象的,目标对象实际存储在((AdvisedSupport)advised.get(aopProxy)).getTargetSource().getTarget(); 
20 楼 zys0523 2012-08-15  
受教了,楼主很牛
提个小意见.
Field h = proxy.getClass().getSuperclass().getDeclaredField("h"); 
        h.setAccessible(true); 
        AopProxy aopProxy = (AopProxy) h.get(proxy); 

这三行可以换成JDK原生自带的
java.lang.reflect.Proxy.getInvocationHandler
    public static InvocationHandler getInvocationHandler(Object proxy)
19 楼 jinnianshilongnian 2012-08-01  
Pigwen 写道
jinnianshilongnian 写道
Pigwen 写道
晕,jdk的代理本身就只能搞接口,你让spring用cglib做代理不就好了啊。。。


嗯,如果是私有的呢

那就要反思下为什么会需要去反射这样的方法了。
你现在这个方案搞得了某个版本的cglib,其他版本的cglib万一又改变动态类名字的规则了喃,更何况还有javassist这些其他的动态代理框架存在?

没有完美的解决方案,现在他的需求就是这个样子。
设计目前就是这个样子。设计的问题除非他修改源代码 

相关推荐

    spring杂谈 作者zhang KaiTao

    1.11 在spring中获取代理对象代理的目标对象工具类 1.12 如何为spring代理类设置属性值 1.13 我对SpringDAO层支持的总结 1.14 我对SpringDAO层支持的总结 1.15 我对SpringDAO层支持的总结 1.16 我对Spring 容器管理...

    Spring中文帮助文档

    6.8.1. 在Spring中使用AspectJ进行domain object的依赖注入 6.8.2. Spring中其他的AspectJ切面 6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用中使用AspectJ加载时织入(LTW) 6.9. 更多资源 7...

    Spring-Reference_zh_CN(Spring中文参考手册)

    6.8.1. 在Spring中使用AspectJ来为domain object进行依赖注入 6.8.1.1. @Configurable object的单元测试 6.8.1.2. 多application context情况下的处理 6.8.2. Spring中其他的AspectJ切面 6.8.3. 使用Spring IoC来...

    Spring 2.0 开发参考手册

    6.8.1. 在Spring中使用AspectJ来为domain object进行依赖注入 6.8.2. Spring中其他的AspectJ切面 6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用中使用AspectJ Load-time weaving(LTW) 6.9. ...

    Spring API

    6.8.1. 在Spring中使用AspectJ进行domain object的依赖注入 6.8.2. Spring中其他的AspectJ切面 6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用中使用AspectJ加载时织入(LTW) 6.9. 更多资源 7...

    spring chm文档

    6.8.4. 在Spring应用中使用AspectJ Load-time weaving(LTW) 6.9. 其它资源 7. Spring AOP APIs 7.1. 简介 7.2. Spring中的切入点API 7.2.1. 概念 7.2.2. 切入点实施 7.2.3. AspectJ切入点表达式 7.2.4. ...

    ssh(structs,spring,hibernate)框架中的上传下载

     文件数据存储在Blob类型的FILE_CONTENT表字段上,在Spring中采用OracleLobHandler来处理Lob字段(包括Clob和Blob),由于在程序中不需要引用到oracle数据驱动程序的具体类且屏蔽了不同数据库处理Lob字段方法上的...

    java开源包4

    PortGroper 是一款java写的开源拒绝服务测试工具,它不是僵尸网络类的ddos,而是使用大量的代理作为bots发起DDOS。Port Groper可以与用测试防火墙,干扰web 统计脚本的跟踪,为网站增加流量..往好了用什么都能干,就是...

    JAVA上百实例源码以及开源项目

    在有状态SessionBean中,用累加器,以对话状态存储起来,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用完毕,从内存中清除…… Java Socket 聊天...

    JAVA上百实例源码以及开源项目源代码

    6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用...

    java开源包1

    PortGroper 是一款java写的开源拒绝服务测试工具,它不是僵尸网络类的ddos,而是使用大量的代理作为bots发起DDOS。Port Groper可以与用测试防火墙,干扰web 统计脚本的跟踪,为网站增加流量..往好了用什么都能干,就是...

    java开源包11

    PortGroper 是一款java写的开源拒绝服务测试工具,它不是僵尸网络类的ddos,而是使用大量的代理作为bots发起DDOS。Port Groper可以与用测试防火墙,干扰web 统计脚本的跟踪,为网站增加流量..往好了用什么都能干,就是...

    java开源包2

    PortGroper 是一款java写的开源拒绝服务测试工具,它不是僵尸网络类的ddos,而是使用大量的代理作为bots发起DDOS。Port Groper可以与用测试防火墙,干扰web 统计脚本的跟踪,为网站增加流量..往好了用什么都能干,就是...

    java开源包3

    PortGroper 是一款java写的开源拒绝服务测试工具,它不是僵尸网络类的ddos,而是使用大量的代理作为bots发起DDOS。Port Groper可以与用测试防火墙,干扰web 统计脚本的跟踪,为网站增加流量..往好了用什么都能干,就是...

    java开源包6

    PortGroper 是一款java写的开源拒绝服务测试工具,它不是僵尸网络类的ddos,而是使用大量的代理作为bots发起DDOS。Port Groper可以与用测试防火墙,干扰web 统计脚本的跟踪,为网站增加流量..往好了用什么都能干,就是...

    java开源包5

    PortGroper 是一款java写的开源拒绝服务测试工具,它不是僵尸网络类的ddos,而是使用大量的代理作为bots发起DDOS。Port Groper可以与用测试防火墙,干扰web 统计脚本的跟踪,为网站增加流量..往好了用什么都能干,就是...

    java开源包10

    PortGroper 是一款java写的开源拒绝服务测试工具,它不是僵尸网络类的ddos,而是使用大量的代理作为bots发起DDOS。Port Groper可以与用测试防火墙,干扰web 统计脚本的跟踪,为网站增加流量..往好了用什么都能干,就是...

Global site tag (gtag.js) - Google Analytics