在 Java 裡動態代理,主要分:介面動態代理 和 類動態代理。因為它的代理類都是動態建立的,所以名字裡會帶上“動態”。
官網的有些地方叫“代理”,也有些地方叫“動態代理”。都是一個意思。
1、介面動態代理
這是 jdk 直接支援的能力。內在的原理是:框架會動態生成目標介面的一個代理類(即介面的實現類)並返回,使用者在呼叫介面的函式時,實際上呼叫的是這個代理類的函式,而代理類又把資料轉給了呼叫處理器介面。
而整個過程的感受是呼叫目標介面,最終到了 InvocationHandler 的實現類上:
//1. 定義目標介面
public interface UserService{
void addUser(int userId, String userName);
}
//=>
//2. 透過JDK介面,獲得一個代理例項
UserService userService = Proxy.getProxy(UserService.class, new InvocationHandlerImpl());
//生成的 UserService 代理類,差不多是這個樣子:
public class UserService$Proxy implements UserService{
final InvocationHandler handler;
final Method addUser2; //示意一下,別太計較它哪來的
public UserService$Proxy(InvocationHandler handler){
this.handler = handler;
}
@Override
public void void addUser(int userId, String userName){
handler.invoke(this, addUser2, new Object[](userId, userName));
}
}
//在呼叫 userService 時,本質是呼叫 UserService$Proxy 的函式,最終又是轉發到 InvocationHandler 的實現類上。
//=>
//3. 實現呼叫處理器介面
public class InvocationHandlerImpl implements InvocationHandler{
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
//...
}
}
一般,介面動態代理是為了:轉發處理。
2、類動態代理
類的動態代理,略麻煩些,且要藉助字元碼工具框架(Solon 用的是 ASM)。內在的原理倒是相差不大:框架會動態生成目標類的一個代理類(一個重寫了所有函式的子類)並返回,使用者在呼叫目標類的函式時,實際上呼叫的是這個代理類的函式,而代理類又把資料轉給了呼叫處理器介面。呼叫處理器在處理時,會附加上別的處理。
而整個過程的感受是呼叫目標類,可以附加上很多攔截處理:
//1. 定義目標類
public class UserService{
public void addUser(int userId, String userName){
//..
}
}
//=>
//2. 透過框架介面,獲得一個代理例項(::注意這裡的區別!)
UserService userService = new UserService();
userService = AsmProxy.getProxy(UserService.class, new AsmInvocationHandlerImpl(userService));
//生成的 UserService 代理類,差不多是這個樣子:
public class UserService$AsmProxy extends UserService{
final AsmInvocationHandler handler;
final Method addUser2; //示意一下,別太計較它哪來的
public UserService$Proxy(AsmInvocationHandler handler){
this.handler = handler;
}
@Override
public void void addUser(int userId, String userName){
handler.invoke(this, addUser2, new Object[](userId, userName));
}
}
//本質還是呼叫 UserService$AsmProxy 的函式,最終也是轉發到 AsmInvocationHandler 的實現類上。
//=>
//3. 實現呼叫處理器介面
public class AsmInvocationHandlerImpl implements AsmInvocationHandler{
//::注意這裡的區別
final Object target;
public AsmInvocationHandlerImpl(Object target){
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
//::注意這裡的區別
MethodWrap methodWrap = MethodWrap.get(method);
//MethodWrap 內部對各種攔截器做了封裝處理
methodWrap.invoke(target, args);
}
}
一般,類動態代理是為了:攔截並附加處理。
3、關於 Solon 的類代理情況與“函式環繞攔截”
對 Solon 來講,只有一個函式反射後再經 MethodWrap 執行的,就是被代理了。所有的“函式環繞攔截”處理就封裝在 MethodWrap 裡面。
- @Controller、@Remoting 註解的類
這兩個註解類,沒有 ASM 的類程式碼,但是它們的 Method 會轉為 MethodWrap ,幷包裝成 Action 註冊到路由器。即它們是經 MethodWrap 再呼叫的。所以它們有代理能力,支援“函式環繞攔截”。
- @Service、@Dao、@Repository 註解的類
這三個註解,來自 solon.aspect 外掛包,它們註解的類,都會被 ASM 代理。跟上面原理分析的一樣,也支援“函式環繞攔截”。
- 有剋制的攔截
Solon 不支援表示式的隨意攔截,必須以註解為“切點”進行顯示攔截。所以 Solon 不用為所有的 Bean 增加代理能力,按需新增即可。