终于搞懂动态代理了!

这篇具有很好参考价值的文章主要介绍了终于搞懂动态代理了!。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

学习动态代理之前我们需要一些前置知识点:

学一点关于JVM类加载的知识

Java反射,看完就会用

然后我们从设计模式中的代理模式开始说。

代理模式

代理模式中有3个角色:

• Subject抽象角色:负责定义RealSubject和Proxy应该实现的接口。

• RealSubject真实角色:真正完成业务服务功能。

• Proxy代理角色:包含对RealSubject的引用,自己不做业务。

代理模式在实际生活中的应用实例有:明星经纪人、租房中介等等。

代理模式又分为静态代理动态代理

静态代理

代理我们大概知道什么意思了,那静态是什么意思?

静态就是说我们的代理类都在程序员编码阶段就已经实现了,通过编译器后可以直接编译成class文件然后加载到JVM内存,程序运行时,class就已经存在了。

下面给出一个代码示例来了解静态代理模式:

将车站的售票服务抽象出一个接口 TicketService ,包含问询,卖票,退票功能,车站类 Station 实现了 TicketService 接口,车票代售点 StationProxy 则实现了代理角色的功能。

TicketService抽象角色:

public interface TicketService {
    public void sellTicket();

    public void inquire();

    public void withdraw();
}

Station真实角色:

public class Station implements TicketService{
    @Override
    public void sellTicket() {
        System.out.println("\n\t售票...\n");
    }
    
    @Override
    public void inquire() {
        System.out.println("\n\t问询...\n");

    }
    
    @Override
    public void withdraw() {
        System.out.println("\n\t退票...\n");

    }
}

StationProxy 代理角色:

public class StationProxy implements TicketService{

    private Station station;

    public StationProxy(Station station) {
        this.station = station;
    }

    @Override
    public void sellTicket() {
        // 1.做真正业务之前提示信息
        this.showAlertInfo("您正在使用车票代售点进行购票,每张票会收取5元手续费!");
        // 2.调用真实业务逻辑
        station.sellTicket();
        // 3.后处理
        this.takeHandlingFee();
        this.showAlertInfo("感谢您的光临,再见!");
    }

    @Override
    public void inquire() {
        // 1做真正业务前,提示信息
        this.showAlertInfo("欢迎光临本代售点,问询服务不会收取任何费用,本问询信息仅供参考,具体信息以车站真实数据为准!");
        // 2.调用真实逻辑
        station.inquire();
        // 3。后处理
        this.showAlertInfo("感谢您的光临,再见!\n");
    }

    @Override
    public void withdraw() {
        // 1。真正业务前处理
        this.showAlertInfo("欢迎光临本代售点,退票除了扣除票额的20%外,本代理处额外加收2元手续费!");
        // 2.调用真正业务逻辑
        station.withdraw();
        // 3.后处理
        this.takeHandlingFee();

    }

    private void takeHandlingFee() {
        System.out.println("收取手续费,打印发票。。。。。\n");
    }

    private void showAlertInfo(String info) {
        System.out.println(info);
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        StationProxy proxy = new StationProxy(new Station());
        proxy.sellTicket();
    }
}

结果:

您正在使用车票代售点进行购票,每张票会收取5元手续费!

    售票...

收取手续费,打印发票。。。。。

感谢您的光临,再见!

静态代理在实际开发中会存在这样3个问题:

  • • 如果有100个类需要代理的话我们就要手动编写100个代理类,麻烦的要死

  • • 如果每个类里面有100个方法我们就要每个类里手动写100次业务增强的方法,麻烦的要死

  • • 如果要修改业务就要动源代码,违反开闭原则

那么怎么办呢?

那静态的反义词当然就是动态,下面就说动态代理。

动态代理

什么是动态代理?

既然静态代理是编译阶段生成class文件加载到内存,那动态自然就是在运行阶段生成class文件加载到内存了。

那动态代理解决了静态代理的问题了吗?

我们想一个问题:

动态代理是运行阶段生成了class文件,换句话说,就是编译阶段没有生成class文件。

再换句话说,就是在编码阶段我们根本就没写代理类!!!

代理类都没写,那我只能说,一点都不麻烦了。

但是,哪有什么真正的岁月静好,只不过是有人替我们负重前行!

谁在替我们负重前行?

那自然是我们伟大无私的 JDK , Java Development Kit !

JDK 给我们提供了现成的动态代理机制!

在说 JDK 动态代理之前,我们先对动态代理整个的设计思路捋一下。

我们前边说了动态代理的动态体现在在程序运行期间动态生成字节码,那怎么生成呢?

有很多开源框架已经实现了此功能,比如ASM,Javassist等。

至于具体怎么实现的,我们就不关心了暂时。我们只需要知道它们可以做到就可以了。

动态代理优于静态代理就是因为它不需要我们手动创建Proxy类。

但是最终它们需要实现相同的作用,我们可以理解为程序通过字节码技术帮我们写了一个Proxy类。

Proxy类和RealSubject类(被代理类)都必须达成一个条件:

它们的功能都必须相同,或者说public 方法都必须相同!

它们的功能都必须相同,或者说public 方法都必须相同!

它们的功能都必须相同,或者说public 方法都必须相同!

我们再重申一遍,你动态牛,是牛在方便快捷上,但你本质上和我静态代理是一样的。

那我们怎么能做到Proxy类和RealSubject类实现相同的功能呢?

  1. 1. 定义一个Subject接口,让它俩实现

  2. 2. 通过继承,让Proxy继承RealSubject类

静态代理和 JDK 动态代理都是使用第一种方式,而 cglib 动态代理则是使用第二种方式。

JDK动态代理

下面来到本篇的重中之重,只有搞懂了 JDK 动态代理,才能搞懂 CGLib 动态代理,才能搞懂以后的 AOP!

我们先不说废话,直接上代码看看 JDK 动态代理怎么实现,也不用管能不能看懂先。

现在定义两个接口 Vehicle 和 Rechargable ,Vehicle 表示交通工具类,有 drive() 方法;Rechargable 接口表示可充电的(工具),有recharge() 方法;定义一个实现两个接口的类ElectricCar。

Vehicle接口:

public interface Vehicle {
    public void drive();
}

Rechargable 接口:

public interface Rechargable {
    public void recharge();
}

ElectricCar 实现2个接口:

public class ElectricCar implements Rechargable, Vehicle{
    @Override
    public void recharge() {
        System.out.println("电车充电中...");
    }

    @Override
    public void drive() {
        System.out.println("电车行驶中...");
    }
}

InvocationHandler 实现类:

public class InvocationHandlerImpl implements InvocationHandler {
    private ElectricCar car;

    public InvocationHandlerImpl(ElectricCar car) {
        this.car = car;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method.getName()+"方法执行前...");
        method.invoke(car, null);
        System.out.println(method.getName()+"方法执行完毕...");
        return null;
    }
}

创建动态代理类:

public class Test {

    public static void main(String[] args) {

        ElectricCar car = new ElectricCar();
        // 1.获取对应的ClassLoader
        ClassLoader classLoader = car.getClass().getClassLoader();

        // 2.获取ElectricCar 所实现的所有接口
        Class[] interfaces = car.getClass().getInterfaces();

        // 3.设置一个来自代理传过来的方法调用请求处理器,处理所有的代理对象上的方法调用
        InvocationHandler handler = new InvocationHandlerImpl(car);
        /*
          4.根据上面提供的信息,创建代理对象 在这个过程中,
             a.JDK会通过根据传入的参数信息动态地在内存中创建和.class 文件等同的字节码
             b.然后根据相应的字节码转换成对应的class,
             c.然后调用newInstance()创建实例
         */
        Object o = Proxy.newProxyInstance(classLoader, interfaces, handler);
        Vehicle vehicle = (Vehicle) o;
        vehicle.drive();
        Rechargable rechargeable = (Rechargable) o;
        rechargeable.recharge();
    }
}

结果:

drive方法执行前...
电车行驶中...
drive方法执行完毕...
recharge方法执行前...
电车充电中...
recharge方法执行完毕...

看完我估计是一脸懵,这都什么玩意?

InvocationHandler 是什么东西?

Proxy.newProxyInstance(classLoader, interfaces, handler)又是什么东西?

InvocationHandler 接口

handler是管理者,处理器的意思。

invocation这个单词认识吗?

不认识?

invoke认识吗?

不认识?

回去学反射再回来。

所以这个InvocationHandler的意思就是方法执行的处理器

我们在静态代理中可以发现,代理类所做的事,无非就是在执行某个方法(真正的业务逻辑)前后,做了一些额外的操作。

所以为了构造出具有通用性和简单性的代理类,我们就需要一个专门处理方法调用的角色,这个角色就是InvocationHandler

我们在InvocationHandler 的实现类里单独把对方法的加工处理写好,然后把它奉送给Proxy。

所以动态代理工作的基本模式就是将自己的方法功能的实现交给 InvocationHandler角色,外界对Proxy角色中的每一个方法的调用,Proxy角色都会交给InvocationHandler来处理,而InvocationHandler则调用 RealSubject 角色的方法。如图所示:

终于搞懂动态代理了!

再来简单说一下这个invoke方法:

public Object invoke(Object proxy, Method method, Object[] args) 

我们前边说了,在调用代理类每个方法的时候,它都交给了InvocationHandler,而InvocationHandler只有一个 invoke 方法,那它就会调用这个方法。

那这个方法我得知道是哪个代理实例的方法正在被调用吧?——Object proxy

那我得知道具体哪个方法在被调用吧?——Method method, Object[] args

Proxy 类

Proxy 干嘛用呢?

当然是生成代理对象啦。

研究一下这个方法:

Proxy.newProxyInstance(classLoader, interfaces, handler)

我得加载动态生成的字节码来进行类加载吧?——classLoader

前边说了我们是通过接口来保证Proxy类和RealSubject类功能相同吧?——interfaces

前边说了我们代理实例的方法都是要调用InvocationHandler实现类的方法吧?——handler

当我们把整个过程捋了一遍再回头看一下给的示例代码,应该就能看懂了?

什么?还看不懂?

看不懂,说明看的遍数少了!

那既然有了 JDK 动态代理,怎么 Spring 还不用它非要用 cglib 动态代理?

那自然是 cglib 有它的优势。

下面就说一下 cglib 动态代理。

CGLib 动态代理

前边我们说要做到 Proxy 类和 RealSubject 类实现相同功能,JDK 动态代理使用了第一种方式,所以我们看它的实现一定是要有一个Subject接口,Proxy.newProxyInstance(classLoader, interfaces, handler) 中也必须穿入 interfaces 参数。

那这问题不就来了?

谁规定所有类都有借口的?

那万一我有一个类就是没有接口,但我就要使用代理,怎么办?

反正 JDK 是办不了了。

于是 CGLib 就成了我们的宠儿。

CGLIB(Code Generation Library),是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。

前边讲 JDK 动态代理,最重要的 2 员大将就是 InvocationHandler 接口和 Proxy 类。

那CGLib这边类似的也有 2 员大将。

MethodInterceptor 接口

Enhancer 类

功能都差不多,用起来也都差不多,我们直接上代码。

定义一个用户类:

public class UserDao {
    public void select() {
        System.out.println("查询...");
    }

    public void update() {
        System.out.println("更新...");
    }
}

实现方法拦截器接口:

public class LogInterceptor implements MethodInterceptor {
    /**
     * @param o 表示要进行增强的对象
     * @param method 表示拦截的方法
     * @param objects 数组表示参数列表,基本数据类型需要传入其包装类型,如int-->Integer、long-Long、double-->Double
     * @param methodProxy 表示对方法的代理,invokeSuper方法表示对被代理对象方法的调用
     * @return 执行结果
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        // 注意这里是调用 invokeSuper 而不是 invoke,否则死循环,methodProxy.invokesuper执行的是原始类的方法,method.invoke执行的是子类的方法
        Object result = methodProxy.invokeSuper(o, objects);
        after();
        return result;
    }

    private void after() {
        System.out.println("日志结束时间:" + new Date());

    }

    private void before() {
        System.out.println("日志开始时间:" + new Date());
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        LogInterceptor logInterceptor = new LogInterceptor();

        //cglib 中加强器,用来创建动态代理
        Enhancer enhancer = new Enhancer();

        // 设置要创建动态代理的类
        enhancer.setSuperclass(UserDao.class);

        // 设置回调,这里相当于是对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实行intercept()方法进行拦截
        enhancer.setCallback(logInterceptor);

        UserDao userDao = (UserDao) enhancer.create();
        userDao.update();
        userDao.select();
    }
}

结果:

日志开始时间:Fri Dec 29 11:12:20 CST 2023
更新...
日志结束时间:Fri Dec 29 11:12:20 CST 2023
日志开始时间:Fri Dec 29 11:12:20 CST 2023
查询...
日志结束时间:Fri Dec 29 11:12:20 CST 2023

以上就是对 CGLib 动态代理的说明。

以上所有内容对于动态代理的解释大部分还是浮于表面,本文的目的有 2 个:

  1. 1. 搞明白动态代理大概是怎么一回事

  2. 2. 如果我需要用动态代理,这篇文章给我提供一个使用模板

对于更加深入底层的实现本文并没有进行研究,有兴趣有能力的同学可以自行研究。


联系我:https://stanezhang.github.io/文章来源地址https://www.toymoban.com/news/detail-769380.html

到了这里,关于终于搞懂动态代理了!的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Java学习笔记】 动态代理

    1、什么是动态代理? 前面已经提到了,动态代理就是【在内存】中动态生成【字节码代理类】的技术。(虽然不需要开发者书写,但是在内存层面依然存在该代理对象】 优点 : 减少了代理类的数量 并且解决了代码复用的问题。 动态代理常见的实现技术包括以下三种 JDK内

    2024年02月04日
    浏览(36)
  • 学习笔记-Java动态代理的简单使用

    一种设计模式 简单地说,在代理模式中存在三个角色 用户 代理 被代理的对象 用户调用代理,代理去调用被代理的对象 以此来实现功能的增强 动态代理在java中有两种实现方法 JDK中的Proxy类 CGLIB 实现InvocationHandler接口,创建自己的调用处理器 通过为Proxy类指定ClassLoader和一组

    2024年02月10日
    浏览(46)
  • Java SE 学习笔记(十八)—— 注解、动态代理

    Java 注解(Annotation)又称Java标注,是JDK 5.0引入的一种注释机制,Java语言中的类、构造器、方法、成员变量、参数等都可以被注解进行标注,至于到底做何种处理由业务需求来决定。 例如: JUnit 框架中,标记了注解 @Test 的方法就可以被当成测试方法执行,而没有标记的就不

    2024年02月08日
    浏览(36)
  • Spring AOP 学习(动态代理、JdbcTemplate、Junit)

    Proxy  jdk动态代理,面向接口 cglib   第三方动态代理,面向父类 在 不修改原有代码 ,或者没有办法修改原有代码的情况下, 增强对象功能 ,使用代理对象代替原来的对象去完成功能,进而达到拓展功能的目的 JDK Proxy 动态代理是 面向接口 的动态代理,一定要有接口和实现

    2024年02月08日
    浏览(40)
  • 电压和电流反馈判别及例子,绝对让你通透,其实也没有那么难,一次就看懂!从此终于搞懂了电压反馈和电流反馈!

    一个简单粗暴的判断方法: 先看反馈是否直接连到Uo输出端(若不是直接从输出端引出,则为电流反馈) 再假设输出电压Uo为零,或者令负载电阻RL两端电压为0 ,然后看反馈信号是否存在。 若反馈信号不存在了,说明反馈信号与输出电压成比例,是电压反馈。 否则是电流反

    2024年02月09日
    浏览(41)
  • 数据结构与算法之美学习笔记:41 | 动态规划理论:一篇文章带你彻底搞懂最优子结构、无后效性和重复子问题

    本节课程思维导图: 今天,我主要讲动态规划的一些理论知识。学完这节内容,可以帮你解决这样几个问题:什么样的问题可以用动态规划解决?解决动态规划问题的一般思考过程是什么样的?贪心、分治、回溯、动态规划这四种算法思想又有什么区别和联系? 什么样的问

    2024年02月02日
    浏览(64)
  • 一文带你彻底搞懂Nginx反向代理

    举一个通俗的例子,因为众所周知的原因,我们无法访问谷歌,但是因为某些原因,我们必须要访问谷歌,这时候我们会买一个“梯子”,既然我们无法直接访问谷歌,我们就去麻烦“梯子”帮助我们访问。 事实上我们还是没法访问谷歌,只是这个“梯子”能够访问,它只是

    2024年02月04日
    浏览(42)
  • Spring学习(五):一篇讲清楚动态代理(jdk和cglib)的使用、原理和源码

    目录 一、jdk动态代理的基本使用 二、cglib动态代理的基本使用 2.1 方法一:method.invoke() 方法反射调用 2.2 方法二(spring使用的这个方法): methodProxy.invoke() 2.3 方法三:methodProxy.invokeSuper() 三、jdk实现代理的原理  四、jdk实现代理的源码 五、jdk对代理的优化  六、cglib实现动

    2023年04月14日
    浏览(45)
  • Spring5系列学习文章分享---第三篇(AOP概念+原理+动态代理+术语+Aspect+操作案例(注解与配置方式))

    开篇: 欢迎再次来到 Spring 5 学习系列!在这个博客中,我们将深入研究 Spring 框架的AOP概念+原理+动态代理+术语+Aspect+操作案例(注解与配置方式)。 概念 什么是AOP (1)面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得 业务逻辑各部分之间的

    2024年01月24日
    浏览(45)
  • Android 动态分区详解(一) 5 张图让你搞懂动态分区原理

    本文主要包含动态分区的物理数据布局,内存核心数据结构和动态分区映射示例 3 个部分。 如果你对动态分区没啥概念,建议直接从头阅读 如果只关心动态分区在设备上是如何存储的,请跳转到第 3 节 如果只关心动态分区在内存中的数据结构,请跳转到第 4 节 如果想看动态

    2023年04月08日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包