4

Solon2 开发之容器,八、动态代理的本质 - 带刺的坐椅

 1 year ago
source link: https://www.cnblogs.com/noear/p/17114844.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK