JDK动态代理

​​​​​



前言

相信每一个学习后端的程序员,都对java的动态代理不陌生,但是我们工作中一般不会真正去书写这部分代码,所以对其可能也不是很明白怎么实现的.今天我们来扒一扒它的底层逻辑是怎么实现的,看完你会发现这玩意其实也没那么神奇



一、什么是代理模式?

为其他对象提供一个代理以控制对某个对象的访问。代理类主要负责为委托了(真实对象)预处理消息、过滤消息、传递消息给委托类,代理类不现实具体服务,而是利用委托类来完成服务,并将执行结果封装处理。



二、静态代理

在将动态代理之前,首先要先了解一下静态代理,我们这里可以举一个例子,就好比我们小时候老师给我们留了作业(这好比父接口)学习好的同学人家做完了(这好比真正的实现类)然后呢我比较懒不想动脑子,于是把同学的作业拿过来抄,但是抄的时候又怕被老师发现我又故作聪明加了点别的答案,此时我的这种行为就叫静态代理.

测试方法我们再使用一个例子: 假如对于汽车的定义了有如下要求,让汽车厂商来制造满足如下要求的汽车.

public interface Car {

    void run();

    void stop();

    void brand(String color);

    int tank();

    Car didi();
}

 于是乎大众汽车就按照要求造了一辆"黑色高级轿车"

public class PassantCar implements Car {
    @Override
    public void run() {
        System.out.println("一辆大众高级轿车再跑!");
    }

    @Override
    public void stop() {
        System.out.println("一辆大众高级轿车停在那!");
    }

    @Override
    public void brand(String color) {
        System.out.println("一辆大众" + color + "高级轿车!");
    }

    @Override
    public int tank() {
        return 50;
    }

    @Override
    public Car didi() {
        return this;
    }
}

车造好了那就拉出来溜溜吧

public class TestMain {
    public static void main(String[] args) {
        Car car = new PassantCar();
        car.run();
        car.stop();
        int tank = car.tank();
        System.out.println("油箱容量" + tank);
        car.brand("黑色");

    }
}



运行结果很不错,此时有人眼红了...这车这么高级我得造个比他更高级的,可奈何自己没有技术那怎么办?好说,我不自己造我都买别人的,别人有的我都有,然后我还可以改装这不就是解决了,于是乎有了这么一个类  (注意这里静态代理和装饰者模式还是有那么一点区别的)              

public class PassantPlusCar implements Car{

    private Car car;

    /**
     * 静态代理和装饰者模式的区别:
     * 静态代理如下构造,一个空参构造器,而且代理类中明确需要代理的对象
     * 装饰者模式如第二个有参构造,不明确需要代理的对象,只有运行期间调用者传入才知道
      */
    public PassantPlusCar() {
        this.car = new PassantCar();
    }

    public PassantPlusCar(Car car) {
        this.car = car;
    }

    @Override
    public void run() {
        System.out.println("改装过的轿车加速更快!");
        car.run();
    }

    @Override
    public void stop() {
        System.out.println("停车更显尊贵!");
        car.stop();
    }

    @Override
    public void brand(String color) {
        System.out.println("颜色更显高级!");
        car.brand(color);
    }

    @Override
    public int tank() {
        return car.tank();
    }

    @Override
    public Car didi() {
        return car.didi();
    }
}

车造好了,也拉出来溜溜吧,顺便跟原版比比"性能如何"

public class TestMain {
    public static void main(String[] args) {
        Car car = new PassantCar();
        car.run();
        car.stop();
        int tank = car.tank();
        System.out.println("油箱容量" + tank);
        car.brand("黑色");

        System.out.println("=================加强后的高级轿车====================");

        PassantPlusCar plusCar = new PassantPlusCar();
        plusCar.run();
        plusCar.stop();
        plusCar.brand("黑色");
        int tank1 = plusCar.tank();
        System.out.println("油箱容量" + tank1);
    }
}

 

 三、动态代理

动态代理我们还回到第一个例子,还是小时的我,抄了几次作业后我呢变得更懒了,我现在抄都不想抄了,于是我直接把同学写好的作业拿到了打印店,   诶!我给啥作业就能打印啥作业而且我还能改造这个打印机让它按照我的想法给我在作业上加上我的"笔记"岂不快哉,这就叫做动态代理,此处应该说一句"java源于生活而高于生活"

可以看到动态代理简直舒服的一批,那"打印机"是怎么工作的呢?接下载我们就给它"拆了"

首先来了解一下这个方法ProxyGenerator.generateProxyClass(),里面可以传两个参数,一个是类路径和类名,另一个就是代理类,例如把Car.class传进去,生成的是一个字节数组,于是我们可以把它打印到本地来看一看它生成的是什么!,此方法只需要了解即可,我们并不用它

public class TestDynamicMain {
    public static void main(String[] args) {
        byte[] bytes = ProxyGenerator.generateProxyClass("com.dynamicproxy.MyCar", new Class[]{Car.class});

        try(FileOutputStream fos = new FileOutputStream("D:\\MyCar.class")) {
            fos.write(bytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
}

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class MyCar extends Proxy implements Car {
    private static Method m1;
    private static Method m5;
    private static Method m3;
    private static Method m6;
    private static Method m2;
    private static Method m4;
    private static Method m7;
    private static Method m0;

    public MyCar(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final Car didi() throws  {
        try {
            return (Car)super.h.invoke(this, m5, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void run() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void brand(String var1) throws  {
        try {
            super.h.invoke(this, m6, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void stop() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int tank() throws  {
        try {
            return (Integer)super.h.invoke(this, m7, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m5 = Class.forName("com.dynamicproxy.Car").getMethod("didi");
            m3 = Class.forName("com.dynamicproxy.Car").getMethod("run");
            m6 = Class.forName("com.dynamicproxy.Car").getMethod("brand", Class.forName("java.lang.String"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("com.dynamicproxy.Car").getMethod("stop");
            m7 = Class.forName("com.dynamicproxy.Car").getMethod("tank");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

 以上是ProxyGenerator.generateProxyClass()生成的class文件通过反编译得到的内容,由以上内容可知我们接口中的所有方法都可以看到,然后我们把注意力放到下面的这个静态代码块,我们都知道静态代码块中的内容是最先加载的,所以说在这个类加载的时候这些方法也就完成了加载 

然后再来看其中一个方法,在这个方法调用时使用了一个h调用了invoke()方法,然后再来看其父类中的h其实就是InvocationHandler对象,此时我们也就明了了它内部使用了反射的原理来进行了方法的调用

 

 

 然后再来看这个类的唯一的一个构造器,它要求是传一个InvocationHandler对象进来,进来后呢还给了它的父类Proxy,这也就是方法中.h的来源

 搞清楚了这些,我们再来看ProxyGenerator.generateProxyClass()生成的class文件,上面提到该类只有一个构造器,所以想要创建这个这个对象我们不能这么干,看如下的newProxyInstance方法,前面提到生成的class中的方法时使用invoke()方法调用的,所以我们重写invoke方法就可以达到增强的目的然后我们打断点跟一下它所创建的类是不是跟我们上面的类一样!

public class TestDynamicMain {
    public static void main(String[] args) {

        Car car = (Car) Proxy.newProxyInstance(TestDynamicMain.class.getClassLoader(),
                new Class[]{Car.class},
                new MyInvocationHandler());
        car.run();
        car.stop();
    }

    public static class MyInvocationHandler implements InvocationHandler {

        private Car car;

        public MyInvocationHandler() {
            this.car = new PassantCar();
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("当前运行的方法名称: " + method.getName());
            if ("run".equals(method.getName())) {
                System.out.println("给车抛个光!");
                Object result = method.invoke(car, args);
                return result;
            }
            return null;
        }
    }

}

进来以后我们需要关注的是前两个参数,我们的类加载器和需要代理的接口

 找到apply的实现类,上面的一对判断我们不用管,需要关注的一个叫做proxyPkg的变量,此刻它给自己拼了一个包路径,记住这个路径我们继续往下走

 然后下面这里它还是在拼这个路径,nextUniqueNumber.getAndIncrement()是一从0个累加的整数,每访问一次就会累加一次,而且是线程安全的,下面又为自己的类名拼接了一个常量$Proxy

 此刻有没有觉得这个方法似曾相识,没错这就是一开始我们用来生成class文件的那个方法,只不过,这里多了一个参数accessFlags,他是用来指明类的类型的,默认都是public,下面再调用的方法就是把类加载器和class文件的字节数组,还有字节数组的有效片段都传给这个方法,由它来创建这个Class类, 该方法是一个本地方法内部是由虚拟机实现的,所以我们看不到

 然后回到newProxyInstance方法中,这里获取到了生成类中的构造器,我们前说过生成代理类的构造器是唯一的所以它必然是需要InvocationHandler 对象的,下面则是利用反射的方式创建了这个对象并返回!

 好了,委托别人造的汽车也好了,运行也能达到想要的效果

 

注意:如果一般情况下不要去调用Proxy这个参数这个参数一代表了代理类的本身,此处我就简单打印一下 ,就会出现堆栈移除异常

 那这个问题怎么出现的呢,此时我们知道代理类的方法是使用invoke方法执行的,然后我们打印proxy对象,其实相当于是System.out.println(proxy.toString());因为我们调用快了toString方法,而toString方法的调用也需要invoke方法,这时递归就出现了,所以说proxy一般情况下不要使用,那什么时候可以使用呢,我们往下看

 也就是我们在做链式编程的时候可以使用到,其他时候不要用



总结

以上就是我对JDK动态代理的全部总结,是不是觉得动态代理其实也没有做什么太深奥的东西,扒出源码其实就是在为自己拼字符串,然后利用反射的方式创建出代理对象!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值