资讯专栏INFORMATION COLUMN

JDK动态代理的理解与分析

stormjun / 2999人阅读

摘要:类所实现的方法包装了对被代理对象的反射调用,后文中的动态代理类正是调用此方法来调用被代理对象的方法。

前言

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 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 ,源码注释如下:

* 

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 类。MobilePhoneHandler类所实现的invoke方法包装了对被代理对象的反射调用,后文中的动态代理类正是调用此invoke方法来调用被代理对象的方法。

3、创建main方法调用

MobilePhone mp=new AndroidMobilePhone("杰克","23");
InvocationHandler handler=new MobilePhoneHandler(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:
     * 
     *     Proxy.getProxyClass(loader, interfaces).
     *         getConstructor(new Class[] { InvocationHandler.class }).
     *         newInstance(new Object[] { handler });
     * 
*

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

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

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

long num = nextUniqueNumber.getAndIncrement();这一行使用cas生成一个自增长的序号 。
关注ProxyGenerator.generateProxyClass 方法:

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

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

到目录中找到此class文件:

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

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

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

总结

我们再看一下之前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等等,不必手动创建,使用起来更灵活。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/74176.html

相关文章

  • 从源码入手,一文带你读懂Spring AOP面向切面编程

    摘要:,,面向切面编程。,切点,切面匹配连接点的点,一般与切点表达式相关,就是切面如何切点。例子中,注解就是切点表达式,匹配对应的连接点,通知,指在切面的某个特定的连接点上执行的动作。,织入,将作用在的过程。因为源码都是英文写的。 之前《零基础带你看Spring源码——IOC控制反转》详细讲了Spring容器的初始化和加载的原理,后面《你真的完全了解Java动态代理吗?看这篇就够了》介绍了下...

    wawor4827 评论0 收藏0
  • Java 代理模式 AOP

    摘要:本文首发于作者最近在学,研究了下和代理模式,写点心得和大家分享下。所以下面来重点分析下代理模式。这里代理模式分为静态代理和动态代理两种,我们分别来看下。代理模式,代理,意味着有一方代替另一方完成一件事。 本文首发于 https://jaychen.cc作者 jaychen 最近在学 Spring,研究了下 AOP 和代理模式,写点心得和大家分享下。 AOP 先说下AOP,AOP 全称 ...

    jk_v1 评论0 收藏0
  • Java动态代理 jdk和cglib实现比较

    摘要:与静态代理对比,动态代理是在动态生成代理类,由代理类完成对具体方法的封装,实现的功能。本文将分析中两种动态代理的实现方式,和,比较它们的异同。那如何动态编译呢你可以使用,这是一个封装了的库,帮助你方便地实现动态编译源代码。 发现Java面试很喜欢问Spring AOP怎么实现的之类的问题,所以写一篇文章来整理一下。关于AOP和代理模式的概念这里并不做赘述,而是直奔主题,即AOP的实现方...

    h9911 评论0 收藏0
  • 学Aop?看这篇文章就够了!!!

    摘要:又是什么其实就是一种实现动态代理的技术,利用了开源包,先将代理对象类的文件加载进来,之后通过修改其字节码并且生成子类。 在实际研发中,Spring是我们经常会使用的框架,毕竟它们太火了,也因此Spring相关的知识点也是面试必问点,今天我们就大话Aop。特地在周末推文,因为该篇文章阅读起来还是比较轻松诙谐的,当然了,更主要的是周末的我也在充电学习,希望有追求的朋友们也尽量不要放过周末时...

    boredream 评论0 收藏0
  • Java 动态代理 理解

    摘要:之后通过类的静态方法取得一个代理类实例再次鄙视自己。值得一提,动态代理把也代理了。总结动态代理优点相比静态代理,不用每代理一个类就得写一个新的代理类。缺点只能代理实现了接口的类,因为是单继承,代理类已经是类的子类了。 动态代理 这里暂时只做JDK动态代理分析。动态代理应用广泛,例如AOP。 showImg(https://segmentfault.com/img/bVUmAr?w=21...

    3fuyu 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<