9分钟带你搞懂署理 模式、静态署理 、JDK+CGLIB动态署理

[复制链接]
查看504 | 回复18 | 2024-2-2 11:17:31 | 显示全部楼层 |阅读模式
目录:

    1. 署理 模式2. 静态署理 3. 动态署理
      3.1. JDK 动态署理 机制3.2. CGLIB 动态署理 机制3.3. JDK 动态署理 和 CGLIB 动态署理 比较
    4. 静态署理 和动态署理 的比较 5. 总结
1. 署理 模式

署理 模式是一种比较  好的理解的设计模式。简单来说就是 我们使用署理 对象来取代 对真实对象(real object)的拜访 ,这样就可以在不修改原目标对象的前提下,提供额外的功能  操作,扩展目标对象的功能  。

署理 模式的主要作用是扩展目标对象的功能  ,比如  说在目标对象的某个办法 执行前后你可以增加一些自界说 的操作。

举个例子:你的找了一小红来帮你问话,小红就看作是署理 我的署理 对象,署理 的行为(办法 )是问话。



署理 模式有静态署理 和动态署理 两种实现方法 ,我们 先来看一下静态署理 模式的实现。
2. 静态署理

静态署理 中,我们对目标对象的每个办法 的增强都是手动完成的(后面会具体演示代码),异常 不灵活(比如  接口一旦新增加办法 ,目标对象和署理 对象都要进行修改)且麻烦(需要对每个目标类都零丁 写一个署理 类)。 实际应用场景异常 异常 少,日常开发几乎看不到使用静态署理 的场景。

上面我们是从实现和应用角度来说的静态署理 ,从 JVM 层面来说, 静态署理 在编译时就将接口、实现类、署理 类这些都酿成 了一个个实际的 class 文件。

静态署理 实现步调 :

    界说 一个接口及其实现类;创建  一个署理 类同样实现这个接口将目标对象注注入进署理 类,然后在署理 类的对应办法 挪用 目标类中的对应办法 。这样的话,我们就可以通过署理 类屏蔽对目标对象的拜访 ,并且  可以在目标办法 执行前后做一些自己想做的事情。
下面通过代码展示!

1.界说 发送短信的接口



2.实现发送短信的接口


3.创建  署理 类并同样实现发送短信的接口


4.实际使用


运行上述代码之后,控制台打印出:


可以输出结果看出,我们已经增加了 SmsServiceImplsend()办法 。
3. 动态署理

相比于静态署理 来说,动态署理 加倍 灵活。我们不需要针对每个目标类都零丁 创建  一个署理 类,并且  也不需要我们必须  实现接口,我们可以直接署理 实现类( CGLIB 动态署理 机制)。

从 JVM 角度来说,动态署理 是在运行时动态生成类字节码,并加载到 JVM 中的。

说到动态署理 ,Spring AOP、RPC 框架应该是两个不得不的提的,它们的实现都依赖了动态署理 。

动态署理 在我们日常开发中使用的相对较小,然则 在框架中的几乎是必用的一门技术。学会了动态署理 之后,对于我们理解和学习各类 框架的原理也异常 有赞助 。

就 Java 来说,动态署理 的实现方法 有很多种,比如   JDK 动态署理 CGLIB 动态署理 等等。

guide-rpc-framework[1] 使用的是 JDK 动态署理 ,我们先来看看 JDK 动态署理 的使用。

另外,虽然 guide-rpc-framework[2] 没有用到 CGLIB 动态署理  ,我们这里还是简单介绍一下其使用以及和JDK 动态署理 的比较 。

3.1. JDK 动态署理 机制

3.1.1. 介绍

在 Java 动态署理 机制中 InvocationHandler 接口和 Proxy 类是核心。

Proxy 类中使用频率最高的办法 是:newProxyInstance() ,这个办法 主要用来生成一个署理 对象。



这个办法 一共有 3 个参数:

    loader :类加载器,用于加载署理 对象。interfaces : 被署理 类实现的一些接口;h : 实现了 InvocationHandler 接口的对象;
要实现动态署理 的话,还必须  需要实现InvocationHandler 来自界说 处理  逻辑。当我们的动态署理 对象挪用 一个办法 时候,这个办法 的挪用 就会被转发到实现InvocationHandler 接口类的 invoke 办法 来挪用 。



invoke() 办法 有下面三个参数:

    proxy :动态生成的署理 类method : 与署理 类对象挪用 的办法 相对应args : 当前 method 办法 的参数
也就是说:你通过Proxy 类的 newProxyInstance() 创建  的署理 对象在挪用 办法 的时候,实际会挪用 到实现InvocationHandler 接口的类的 invoke()办法 。 你可以在 invoke() 办法 中自界说 处理  逻辑,比如  在办法 执行前后做什么事情。

3.1.2. JDK 动态署理 类使用步调

    界说 一个接口及其实现类;自界说  InvocationHandler 并重写invoke办法 ,在 invoke 办法 中我们会挪用 原生办法 (被署理 类的办法 )并自界说 一些处理  逻辑;通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 办法 创建  署理 对象;
3.1.3. 代码示例

这样说可能会有点空洞和难以理解,我上个例子,年夜 家感触感染 一下吧!

1.界说 发送短信的接口




2.实现发送短信的接口


3.界说 一个 JDK 动态署理 类
import java.lang.reflect.InvocationHandler;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;/** * @author shuang.kou * @createTime 2020年05月11日 11:23:00 */public class DebugInvocationHandler implements InvocationHandler {    /**     * 署理 类中的真实对象     */    private final Object target;    public DebugInvocationHandler(Object target) {        this.target = target;    }    public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {        //挪用 办法 之前,我们可以添加自己的操作        System.out.println("before method " + method.getName());        Object result = method.invoke(target, args);        //挪用 办法 之后,我们同样可以添加自己的操作        System.out.println("after method " + method.getName());        return result;    }}
invoke() 办法 : 当我们的动态署理 对象挪用 原生办法 的时候,最终实际上挪用 到的是 invoke() 办法 ,然后 invoke() 办法 取代 我们去挪用 了被署理 对象的原生办法 。

4.获取署理 对象的工厂  类

public class JdkProxyFactory {    public static Object getProxy(Object target) {        return Proxy.newProxyInstance(                target.getClass().getClassLoader(), // 目标类的类加载                target.getClass().getInterfaces(),  // 署理 需要实现的接口,可指定多个                new DebugInvocationHandler(target)   // 署理 对象对应的自界说  InvocationHandler        );    }}
getProxy() :主要通过Proxy.newProxyInstance()办法 获取某个类的署理 对象

5.实际使用

SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());smsService.send("java");
运行上述代码之后,控制台打印出:



3.2. CGLIB 动态署理 机制

3.2.1. 介绍

JDK 动态署理 有一个最致命的问题是其只能署理 实现了接口的类。

为了解决这个问题,我们可以用 CGLIB 动态署理 机制来避免。

CGLIB[3](Code Generation Library)是一个基于ASM[4]的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB 通过继承方法 实现署理 。很多知名的开源框架都使用到了CGLIB[5], 例如 Spring 中的 AOP 模块中:如果目标对象实现了接口,则默认采取  JDK 动态署理 ,不然 采取  CGLIB 动态署理 。

在 CGLIB 动态署理 机制中 MethodInterceptor 接口和 Enhancer 类是核心。

你需要自界说  MethodInterceptor 并重写 intercept 办法 ,intercept 用于拦截增强被署理 类的办法 。

public interface MethodInterceptorextends Callback{    // 拦截被署理 类中的办法     public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,                               MethodProxy proxy) throws Throwable;}
    obj :被署理 的对象(需要增强的对象)method :被拦截的办法 (需要增强的办法 )args :办法 入参methodProxy :用于挪用 原始办法
你可以通过 Enhancer类来动态获取被署理 类,当署理 类挪用 办法 的时候,实际挪用 的是 MethodInterceptor 中的 intercept 办法 。

3.2.2. CGLIB 动态署理 类使用步调

    界说 一个类;自界说  MethodInterceptor 并重写 intercept 办法 ,intercept用于拦截增强被署理 类的办法 ,和 JDK 动态署理 中的 invoke 办法 类似;通过 Enhancer 类的 create()创建  署理 类;
3.2.3. 代码示例

不合  于 JDK 动态署理 不需要额外的依赖。CGLIB[6](Code Generation Library) 实际是属于一个开源项目,如果你要使用它的话,需要手动添加相关依赖。



1.实现一个使用阿里云发送短信的类


2.自界说  MethodInterceptor(办法 拦截器)
import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;/** * 自界说 MethodInterceptor */public class DebugMethodInterceptor implements MethodInterceptor {    /**     * @param o           被署理 的对象(需要增强的对象)     * @param method      被拦截的办法 (需要增强的办法 )     * @param args        办法 入参     * @param methodProxy 用于挪用 原始办法      */    @Override    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {        //挪用 办法 之前,我们可以添加自己的操作        System.out.println("before method " + method.getName());        Object object = methodProxy.invokeSuper(o, args);        //挪用 办法 之后,我们同样可以添加自己的操作        System.out.println("after method " + method.getName());        return object;    }}
3.获取署理 类



4.实际使用

AliSmsService aliSmsService = (AliSmsService) CglibProxyFactory.getProxy(AliSmsService.class);aliSmsService.send("java");
运行上述代码之后,控制台打印出:



3.3. JDK 动态署理 和 CGLIB 动态署理 比较

    JDK 动态署理 只能只能署理 实现了接口的类,而 CGLIB 可以署理 未实现任何接口的类。 另外, CGLIB 动态署理 是通过生成一个被署理 类的子类来拦截被署理 类的办法 挪用 ,因此不克不及 署理 声明为 final 类型的类和办法 。就二者的效率来说,年夜 部分  情况都是 JDK 动态署理 更优秀,随着 JDK 版本的升级,这个优势加倍 明显。
4. 静态署理 和动态署理 的比较

    灵活性 :动态署理 加倍 灵活,不需要必须  实现接口,可以直接署理 实现类,并且  可以不需要针对每个目标类都创建  一个署理 类。另外,静态署理 中,接口一旦新增加办法 ,目标对象和署理 对象都要进行修改,这是异常 麻烦的!JVM 层面 :静态署理 在编译时就将接口、实现类、署理 类这些都酿成 了一个个实际的 class 文件。而动态署理 是在运行时动态生成类字节码,并加载到 JVM 中的。
5. 总结

这篇文章中主要介绍了署理 模式的两种实现:静态署理 以及动态署理 。涵盖了静态署理 和动态署理 实战、静态署理 和动态署理 的区别、JDK 动态署理 和 Cglib 动态署理 区别等内容。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

123459540 | 2024-2-2 11:18:27 | 显示全部楼层
转发了
回复

使用道具 举报

种千体袋戏 | 2024-2-2 11:19:02 | 显示全部楼层
转发了
回复

使用道具 举报

拓牛李强 | 2024-2-2 11:19:25 | 显示全部楼层
转发了
回复

使用道具 举报

坏女孩458 | 2024-2-2 11:20:24 | 显示全部楼层
转发了
回复

使用道具 举报

123462078 | 2024-2-2 11:20:44 | 显示全部楼层
转发了
回复

使用道具 举报

LQ2017 | 2024-2-2 11:21:15 | 显示全部楼层
转发了
回复

使用道具 举报

ziming20170520 | 2024-2-2 11:21:23 | 显示全部楼层
转发了
回复

使用道具 举报

酷爱黑丝袜黑f | 2024-2-2 11:22:20 | 显示全部楼层
转发了
回复

使用道具 举报

忧伤428 | 2024-2-2 11:22:55 | 显示全部楼层
转发了
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

18

主题

36

回帖

136

积分

注册会员

Rank: 2

积分
136