JDK动态代理的理解与分析

栏目: 后端 · 发布时间: 5年前

内容简介:java的设计模式中有一项设计模式叫做代理模式,所谓代理模式,就是通过代理方来操作目标对象,而不是自己直接调用。代理又分为静态代理和动态代理,静态代理就是针对每个被代理对象写一个代理类,操作不够优雅;动态代理,可以根据接口动态的生成代理类,这动态生成的类不需要自己书写,jdk帮你完成了。无论是动态代理还是静态代理,最终都会产生一个代理类(class文件),里面都含有对被代理对象的封装,只是诞生的途径不一样。下面我在代码层面详细介绍一下这两种代理的实现和原理。本文来自于我的博客网站1、创建手机接口 ,拥有打电

前言

java的设计模式中有一项 设计模式 叫做代理模式,所谓代理模式,就是通过代理方来操作目标对象,而不是自己直接调用。代理又分为静态代理和动态代理,静态代理就是针对每个被代理对象写一个代理类,操作不够优雅;动态代理,可以根据接口动态的生成代理类,这动态生成的类不需要自己书写,jdk帮你完成了。无论是动态代理还是静态代理,最终都会产生一个代理类(class文件),里面都含有对被代理对象的封装,只是诞生的途径不一样。下面我在代码层面详细介绍一下这两种代理的实现和原理。

本文来自于我的博客网站 http://51think.net ,欢迎来访。

一、静态代理

1、创建手机接口 ,拥有打电话的行为

public interface MobilePhone {
    //打电话给jack
    void callJack();
}

2、创建实现类安卓手机,实现此接口

public class AndroidMobilePhone implements MobilePhone{
     private String name;
     private String age;

    public AndroidMobilePhone(String name, String age) {
        this.name = name;
        this.age = age;
    }
    //打电话给jack
    @Override
    public void callJack(){
         System.out.println(" hey boy! name="+name+",age="+age);
    }
}

3、创建静态代理类,实现此接口

public class AndroidMobileStaticProxyPhone implements MobilePhone{
     private MobilePhone amp;

    public AndroidMobileStaticProxyPhone(MobilePhone amp) {
        this.amp = amp;
    }
    //打电话给jack
    @Override
    public void callJack(){
        System.out.println("--静态代理前置--");
        amp.callJack();
        System.out.println("--静态代理后置--");
    }
}

从静态代理类AndroidMobileStaticProxyPhone 中,我们可以发现,他持有了MobilePhone 类型的对象,一旦将被代理对象传入,它就可以操作被代理对象了。

4、创建main方法调用

如果我们不使用代理,调用是这样的:

MobilePhone mp=new AndroidMobilePhone("杰克","23");
mp..callJack();

如果使用静态代理,调用变成如下方式:

MobilePhone mp=new AndroidMobilePhone("杰克","23");
 MobilePhone staticProxy=new AndroidMobileStaticProxyPhone(mp);
 staticProxy.callJack();

从上述代码中,我们可以看出,静态代理其实就是通过一个包装类来调用目标对象而已。

二、动态代理

1、仍然沿用MobilePhone接口类

2、创建java.lang.reflect.InvocationHandler接口的实现类MobilePhoneHandler

public class MobilePhoneHandler<T> implements InvocationHandler {
    private T target;

    public MobilePhoneHandler(T target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //前置处理
        System.out.println("--动态代理前置处理--");
        Object obj=method.invoke(target,args);
        //后置处理
        System.out.println("--动态代理后置处理--");
        return obj;
    }
}

关于InvocationHandler ,源码注释如下:

* <p>Each proxy instance has an associated invocation handler.
 * When a method is invoked on a proxy instance, the method
 * invocation is encoded and dispatched to the {@code invoke}
 * method of its invocation handler.

即,每个代理实例都需要关联一个invocation handler,当一个方法被代理实例调用时,这个方法会被编码并发送到invocation handler中进行处理。这里所说的invocation handler即本文中刚刚创建的 MobilePhoneHandler<T> 类。MobilePhoneHandler类所实现的invoke方法包装了对被代理对象的反射调用,后文中的动态代理类正是调用此invoke方法来调用被代理对象的方法。

3、创建main方法调用

MobilePhone mp=new AndroidMobilePhone("杰克","23");
InvocationHandler handler=new MobilePhoneHandler<MobilePhone>(mp);
MobilePhone mpProxy=(MobilePhone)Proxy.newProxyInstance(MobilePhone.class.getClassLoader(),new Class<?>[]{MobilePhone.class},handler );
mpProxy.callJack();

输出如下:

--动态代理前置处理--
 hey boy! name=杰克,age=23
--动态代理后置处理--

在输出内容的前置处理和后置处理中,我们可以加一些横向的处理逻辑,这样就变成了spring 的AOP。

关注Proxy.newProxyInstance这个方法调用,同样是来自于java.lang.reflect包里的类。看一下源码注释:

* Returns an instance of a proxy class for the specified interfaces
     * that dispatches method invocations to the specified invocation
     * handler.  This method is equivalent to:
     * <pre>
     *     Proxy.getProxyClass(loader, interfaces).
     *         getConstructor(new Class[] { InvocationHandler.class }).
     *         newInstance(new Object[] { handler });
     * </pre>
     *

注释中表明,这个newProxyInstance方法返回了一个特定接口代理类的实例,这个代理实例将方法调用分配给特定的invocation handler。这个Proxy.newProxyInstance方法等同于如下调用:

Proxy.getProxyClass(loader, interfaces). getConstructor(new Class[] { InvocationHandler.class }).newInstance(new Object[] { handler });

我们用debug方式跟踪一下代码,newProxyInstance方法最终会执行到Proxy的内部类ProxyClassFactory的apply方法:

JDK动态代理的理解与分析

long num = nextUniqueNumber.getAndIncrement();这一行使用cas生成一个自增长的序号 。

关注ProxyGenerator.generateProxyClass 方法:

JDK动态代理的理解与分析

此方法动态生成一个class文件,这个class文件就是我们所说的动态代理类!我们用代码的方式将这个class文件写出来:

byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", AndroidMobilePhone.class.getInterfaces());
        String path = "E:\\projectspace\\Test\\bin\\com\\proxy\\MobileProxy.class";
        try(FileOutputStream fos = new FileOutputStream(path)) {
            fos.write(classFile);
            fos.flush();
            System.out.println("代理类class文件写入成功");
        } catch (Exception e) {
            System.out.println("写文件错误");
        }

到目录中找到此class文件:

JDK动态代理的理解与分析

反编先看一下反编译的类名和实现关系:

JDK动态代理的理解与分析

从此图中可以看出,动态代理类最终还是实现了我们的MobilePhone接口,即动态代理类也是MobilePhone接口的一个实现类,它也实现了callJack方法。如下:

JDK动态代理的理解与分析

红框标注this.h.invoke(this, m3, null);中的h正是我们上文中创建的MobilePhoneHandler类的对象。这样即可完成对被代理对象的调用。类的调用关系如下:

JDK动态代理的理解与分析

总结

我们再看一下之前main方法中的这一行:

MobilePhone mpProxy=(MobilePhone)Proxy.newProxyInstance(MobilePhone.class.getClassLoader(),new Class<?>[]{MobilePhone.class},handler );

现在可以得知Proxy.newProxyInstance返回的是动态生成的代理类$Proxy0的对象,也可以称作是MobilePhone 接口的一个实现类的对象。当调用mpProxy.callJack()时,其实是调用$Proxy0.callJack(),然后对照刚刚的类调用关系图,即可调用到被代理对象AndroidMobilePhone实例的callJack方法,从而实现了动态代理。

当我们具象的查看某一个动态代理class反编译文件时,比如$Proxy0,它内部就是采用静态代理的方式进行包装。其动态是体现在,能够在给定的接口和invocationHandler情况下,动态生成代理类,如$Proxy0,$Proxy1,$Proxy2等等,不必手动创建,使用起来更灵活。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Powerful

Powerful

Patty McCord / Missionday / 2018-1-25

Named by The Washington Post as one of the 11 Leadership Books to Read in 2018 When it comes to recruiting, motivating, and creating great teams, Patty McCord says most companies have it all wrong. Mc......一起来看看 《Powerful》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

随机密码生成器
随机密码生成器

多种字符组合密码

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具