Solon2 開發之容器,八、動態代理的本質

2023-02-13 06:01:10

在 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 增加代理能力,按需新增即可