Spring中獲取被代理的對象
Spring中獲取被代理的對象
獲取Spring被代理對象的JAVA工具類
Spring采用CGLIB或者JDK動態代理來實現AOP,那如何獲取 被代理對象?通過ApplicationContext.getBean()
獲取到的對象都是 利用字節碼動態生成的 增強對象,那假如我們有場景獲取 被代理的對象,方式如下: (封裝到工具類形式里面,直接通過getTrueTargetFrom0
即可調用,需要強轉一下類型.)
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;
}
}
效果截圖
效果說明: 三個方法效果一樣.
System.setProperty("cglib.debugLocation","E:\\data\\spring")
用來指定 代理類class文件生成位置,在CGLIB中也可以這么用。getTrueTargetFrom0
等方法是如何獲取被代理對象UserService
的,有一定CGLIB基礎之后,代理類中存在回調類,屬性CGLIB$CALLBACK_x
(x 為數字),Spring生成 代理類時候會將 被代理的UserService
保存起來在某些CGLIB$CALLBACK_x
中. 這些需要查看源碼才能有個輪廓的了解。
獲取Spring被代理對象什么時候可能會用到?
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 方法.
提示:代碼不規范,同事兩行淚,這種寫法不太規范僅供出現問題時定位.