详细分析一下JDK动态代理和CGLIB动态代理

适合阅读者

复习代理模式或者需要加深理解的人群,
初学者建议找课听,和这个博客结合着看看.
免责声明:纯自己学习的理解,如有错误,欢迎斧正.

JDK动态代理

JDK动态代理一般实现步骤

1.创建一个目标类要实现的接口(后面涉及到要使用接口名称的地方,我统一写成接口)

interface 接口{}

2.创建一个接口实现类(要代理的目标对象所归属的类)

3.创建一个代理工厂类(提供了创建代理对象的方法的类)

3.1 创建代理工厂类

3.2 声明要代理的目标对象

private 目标类类 目标对象 = new 目标类();

3.3 提供创建代理对象的方法

public 接口 getProxyObject(){...}//这里返回值是对象多态的体现

测试是否成功


要深入了解的步骤

步骤3.3 提供创建代理对象的方法

这个方法要书写的代码如下:(主要是使用Proxy.newProxyInstance方法生成代理对象,然后返回代理对象


 接口 proxyObject = (接口)Proxy.newProxyInstance(//这个方法生成的是Object对象,所以要强转
                目标对象.getClass().getClassLoader(),//目标对象类加载器
                目标对象.getClass().getInterfaces(),//代理类实现的接口的字节码对象
                new InvocationHandler() {//代理对象的调用处理程序(这里是什么意思呢,下文详细分析)

                    /*
                        Object proxy : 代理对象。和proxyObject对象是同一个对象,在invoke方法中基本不用
                        Method method : 对接口中的方法进行封装的method对象
                        Object[] args : 调用方法的实际参数

                        返回值: 方法的返回值。
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //System.out.println("invoke方法执行了");
                        System.out.println("增加的代码逻辑");
                        //执行目标对象的方法
                        Object obj = method.invoke(目标对象, args);//这里就是调用目标对象的方法
                        return obj;
                    }
                }
        );
        return proxyObject;

由newProxyInstance方法的第二个参数: 目标对象.getClass().getInterfaces(),//代理类实现的接口的字节码对象 , 可以确定: 代理类需要和目标类一样实现同一个接口


new InvocationHandler(){…}这个参数在做什么

要明白 new InvocationHandler(){…}这个参数在做什么,需要明白Proxy.newProxyInstance生成代理对象时利用的 代理对象所归属的 类的结构是什么(简而言之,需要明白代理类的构造)

代理类是在运行阶段生成的,使用工具arthas-boot.jar打印出代理类$Proxy0代码,如下:(这里代理类中删去了不相关的代码)

public final class $Proxy0
        extends Proxy
        implements 接口 {
    private static Method m3;
   

    public $Proxy0(InvocationHandler invocationHandler) {
        super(invocationHandler);
    }

    static {
            m3 = Class.forName("com.itheima.pattern.proxy.jdk_proxy.SellTickets").getMethod("接口中的抽象方法名", new Class[0]);
    }

    public final void 接口中的抽象方法名() {
            this.h.invoke(this, m3, null);//这里我用的方法参数个数为0,故第三个值为null
    }
}
//Java提供的动态代理相关类
public class Proxy implements java.io.Serializable {
    protected InvocationHandler h;

    protected Proxy(InvocationHandler h) {
        this.h = h;
    }
}

new InvocationHandler(){…}理解步骤

  1. 通过Proxy.newProxyInstance方法在运行时动态的生成了一个代理类,他实现了 目标对象实现的接口
  2. 代理类通过反射接口字节码对象拿到接口中的抽象方法 所封装的方法对象

  1. 当调用代理对象中的同名方法时:
public final void 接口中的抽象方法名() {
            this.h.invoke(this, m3, null);//这里我用的方法参数个数为0,故第三个值为null
    }

可以看出,调用的就是在Proxy.newProxyInstance方法中的第三个参数(创建的InvocationHandler实现类对象)中的invoke方法,又因为传入了接口中的方法包装的对象method,并且在代理工厂类(提供了创建代理对象的方法的类)声明了目标对象,所以可以通过Object obj = method.invoke(目标对象, args);调用目标对象的方法,获得该方法返回值,接下来就不用说了.

new InvocationHandler() {//代理对象的调用处理程序(这里是什么意思呢,下文详细分析)

                    /*
                        Object proxy : 代理对象。和proxyObject对象是同一个对象,在invoke方法中基本不用
                        Method method : 对接口中的方法进行封装的method对象
                        Object[] args : 调用方法的实际参数

                        返回值: 方法的返回值。
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //System.out.println("invoke方法执行了");
                        System.out.println("增加的代码逻辑");
                        //执行目标对象的方法
                        Object obj = method.invoke(目标对象, args);//这里就是调用目标对象的方法
                        return obj;
                    }
                }

总结原理

关键在于生成的代理类中的同名方法中调用的就是new InvocationHandler(){…}这个实现类对象的invoke方法,所以使用代理对象的同名方法时,走的逻辑是 : 我们在Proxy.newProxyInstance方法中第三个参数new InvocationHandler(){…}中实现的invoke方法的逻辑


CGLIB动态代理

CGLIB动态代理一般流程例子

//这里使用我学习时用到的例子
//火车站
public class TrainStation {

    public void sell() {
        System.out.println("火车站卖票");
    }
}

//代理工厂
public class ProxyFactory implements MethodInterceptor {

    private TrainStation target = new TrainStation();

    public TrainStation getProxyObject() {
        //创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
        Enhancer enhancer =new Enhancer();
        //设置父类的字节码对象
        enhancer.setSuperclass(target.getClass());
        //设置回调函数
        enhancer.setCallback(this);
        //创建代理对象
        TrainStation obj = (TrainStation) enhancer.create();
        return obj;
    }

    /*
        intercept方法参数说明:
            o : 代理对象
            method : 真实对象中的方法的Method实例
            args : 实际参数
            methodProxy :代理对象中的方法的method实例
     */
    public TrainStation intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("代理点收取一些服务费用(CGLIB动态代理方式)");
        TrainStation result = (TrainStation) methodProxy.invokeSuper(o, args);
        return result;
    }
}

//测试类
public class Client {
    public static void main(String[] args) {
        //创建代理工厂对象
        ProxyFactory factory = new ProxyFactory();
        //获取代理对象
        TrainStation proxyObject = factory.getProxyObject();

        proxyObject.sell();
    }
}

分析(仅加深使用层面的理解,原理不讨论)

使用方式和JDK动态代理大体过程我觉得极为相似,只是需要设置的参数有点不同,这里分析一下需要理解的地方,两个代理做个对比(这里仅仅讨论使用方式的区别,原理不讨论)

1.CGLIB生成的动态代理对象类是目标对象类的子类,JDK动态代理两个类的关系是实现了同一个接口

2.动态代理类JDK调用的是new InvocationHandler()的invoke方法,CGLIB调用的是实现了MethodInterceptor接口重写的intercept方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值