Spring中獲取被代理的對象spring
### 獲取Spring被代理對象的JAVA工具類工具
Spring採用CGLIB或者JDK動態代理來實現AOP,那如何獲取 被代理對象?經過ApplicationContext.getBean()
獲取到的對象都是 利用字節碼動態生成的 加強對象,那假如咱們有場景獲取 被代理的對象,方式以下: (封裝到工具類形式裏面,直接經過getTrueTargetFrom0
便可調用,須要強轉一下類型.)debug
import org.springframework.aop.TargetSource; import org.springframework.aop.framework.AdvisedSupport; import org.springframework.cglib.proxy.MethodInterceptor; import java.lang.reflect.Field; public class SpringUtils { public static Object getTrueTargetFrom0(Object obj){ try { //獲取第一個攔截器 Field field = obj.getClass().getDeclaredField("CGLIB$CALLBACK_0"); field.setAccessible(true); MethodInterceptor interceptor = (MethodInterceptor) field.get(obj); //獲取攔截器的屬性advised Field advised = interceptor.getClass().getDeclaredField("advised"); advised.setAccessible(true); AdvisedSupport advisedSupport = (AdvisedSupport) advised.get(interceptor); TargetSource targetSource=null; if (advisedSupport!=null) { targetSource = advisedSupport.getTargetSource(); } return targetSource!=null?targetSource.getTarget():null; } catch (Exception e) { e.printStackTrace(); } return null; } public static Object getTrueTargetFrom1(Object obj){ try { //獲取第二個攔截器 Field field = obj.getClass().getDeclaredField("CGLIB$CALLBACK_1"); field.setAccessible(true); MethodInterceptor interceptor = (MethodInterceptor) field.get(obj); //獲取攔截器的屬性advised Field advised = interceptor.getClass().getDeclaredField("target"); advised.setAccessible(true); Object result = advised.get(interceptor); return result; } catch (Exception e) { e.printStackTrace(); } return null; } public static Object getTrueTargetFrom3(Object obj){ try { //獲取第四個攔截器 Field field = obj.getClass().getDeclaredField("CGLIB$CALLBACK_3"); field.setAccessible(true); MethodInterceptor interceptor = (MethodInterceptor) field.get(obj); //獲取攔截器的屬性advised Field advised = interceptor.getClass().getDeclaredField("target"); advised.setAccessible(true); Object result = advised.get(interceptor); return result; } catch (Exception e) { e.printStackTrace(); } return null; } }
效果截圖代理
效果說明: 三個方法效果同樣.code
System.setProperty("cglib.debugLocation","E:\\data\\spring")
用來指定 代理類class文件生成位置,在CGLIB中也能夠這麼用。getTrueTargetFrom0
等方法是如何獲取被代理對象UserService
的,有必定CGLIB基礎以後,代理類中存在回調類,屬性CGLIB$CALLBACK_x
(x 爲數字),Spring生成 代理類時候會將 被代理的UserService
保存起來在某些CGLIB$CALLBACK_x
中. 這些須要查看源碼纔能有個輪廓的瞭解。 CGLIB代理是基於繼承或者實現接口的方式,咱們可能只須要知道 class 屬性就能生成代理類,這樣作帶來的問題:
父類(被代理類)的屬性可能咱們 只能經過 方法 來獲取,好比有個dao
屬性,不是private
修飾類型的,咱們不想經過getDao
來獲取,想直接調用 屬性 ,那可能就是空的. 下面看下例子,對象
@Service public class UserService { @Autowired public UserDao dao; public void addUser(){ System.out.println("添加用戶"); } public UserDao getDao() { return dao; } }
這樣一個類可能不符合代碼編寫,可是用來介紹實驗效果夠了。 好比咱們 ApplicationContext.getBean(UserService.class).getDao
是可以獲取到注入的DAO,可是ApplicationContext.getBean(UserService.class).dao
輸出的就是null. 緣由很簡單,CGLIB加強的是方法,dao確定就是空的,getDao被代理了以後就進入到了真正的UserService
的 getDao 方法.blog
提示:代碼不規範,同事兩行淚,這種寫法不太規範僅供出現問題時定位.繼承