• Solon2 开发之IoC,八、动态代理的本质


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

  • 相关阅读:
    【编程题】【Scratch三级】2021.12 分身术
    Qt中表格属性相关操作,调整表格宽度高度自适应内容等
    SQLAchemy 常用操作
    数据结构 - 二叉树刷题
    【图论】【Matlab】最小生成树之Kruskal算法【贪心思想超详细详解Kruskal算法并应用】
    温振传感器有几种传输方式?
    一文学会JavaScript计时事件
    STM32CubeMX教程11 RTC 实时时钟 - 入侵检测和时间戳
    【手撕数据结构】二分查找(好多细节)
    moudo网络库剖析
  • 原文地址:https://www.cnblogs.com/noear/p/17114844.html