在 Java 裡動態代理,主要分:介面動態代理 和 類動態代理。因為它的代理類都是動態建立的,所以名字裡會帶上「動態」。
官網的有些地方叫「代理」,也有些地方叫「動態代理」。都是一個意思。
這是 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{
//...
}
}
一般,介面動態代理是為了:轉發處理。
類的動態代理,略麻煩些,且要藉助字元碼工具框架(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);
}
}
一般,類動態代理是為了:攔截並附加處理。
對 Solon 來講,只有一個函數反射後再經 MethodWrap 執行的,就是被代理了。所有的「函數環繞攔截」處理就封裝在 MethodWrap 裡面。
這兩個註解類,沒有 ASM 的類程式碼,但是它們的 Method 會轉為 MethodWrap ,幷包裝成 Action 註冊到路由器。即它們是經 MethodWrap 再呼叫的。所以它們有代理能力,支援「函數環繞攔截」。
這三個註解,來自 solon.aspect 外掛包,它們註解的類,都會被 ASM 代理。跟上面原理分析的一樣,也支援「函數環繞攔截」。
Solon 不支援表示式的隨意攔截,必須以註解為「切點」進行顯示攔截。所以 Solon 不用為所有的 Bean 增加代理能力,按需新增即可。