静态代理、动态代理,以及动态代理的调用说明

自作多情 提交于 2020-02-26 05:34:41

原文作者:弥诺R
原文地址:http://www.minuor.com/1524750034/article
转载声明:转载请注明原文地址,注意版权维护,谢谢!

提前说说

项目中涉及到的代码我都会上传到码云(gitee)或者github上,提供给大家下载参考,文中就以最简单的方式说明执行过程。源码的地址在文末哦!

代理模式

代理模式分为静态代理和动态代理两种方式,静态代理是在开发的时候就写好代理的过程,并且代理类要和目标类实现同一个接口。而动态代理是代理类通过实现InvocationHandler接口完成,在运行期间动态的构建代理对象,在动态代理的实现过程中还有另一个更为重要的类Proxy,准确的来说,Proxy负责生成代理对象,而InvocationHandler是根据生成的代理对象执行增强内容和目标方法或类。

静态代理

####要点:
1、代理类需要和目标类需要实现同一个接口
2、在代理类中会内置目标类对象
####代码分析

创建一个接口Transformers(变形金刚)
public interface Transformers {
    void trans2man();//变形->人
    void trans2car();//变形->车
}
Transformers的实现类TransformersImpl,可以理解为擎天柱

擎天柱实现了变形金刚接口,拥有两个功能分别是变形成人、变形成车。

public class TransformersImpl implements Transformers {

    @Override
    public void trans2man() {
        System.out.println("---->transform to man");
    }

    @Override
    public void trans2car() {
        System.out.println("---->transform to car");
    }
}
代理类TransformersProxy,和TransformersImpl一样都要实现Transformers接口
public class TransformersProxy implements Transformers {

    public Transformers transformers;

    public void init(Object transformers) { //初始化
        this.transformers = (Transformers) transformers;
    }

    @Override
    public void trans2man() {
        System.out.println("---->transform to man before");
        transformers.trans2man();
    }

    @Override
    public void trans2car() {
        System.out.println("---->transform to car before");
        transformers.trans2car();
    }
}
运行测试方法,测试代理过程
//静态代理方式
@Test
public void staticProxyTest() {
    System.out.println("=========static proxy test start=========");
    TransformersProxy proxy = new TransformersProxy();
    proxy.init(new TransformersImpl());
    proxy.trans2man();
    System.out.println("~~~~~~~~~华丽分隔线~~~~~~~~~~");
    proxy.trans2car();
    System.out.println("=========static proxy test end=========");
}

执行结果:

=========static proxy test start=========
---->transform to man before
---->transform to man
~~~~~~~~~华丽分隔线~~~~~~~~~~
---->transform to car before
---->transform to car
=========static proxy test end=========

先执行代理类中的逻辑,再执行目标方法,这样就完成了代理的过程。如果现在又有一个变形金刚大黄蜂实现了Transformers类,它的增强内容和擎天柱是相同,这个时候代理类中的增强代码就可复用,只要在初始化代理对象的时候,传入大黄蜂实现类对象即可。

缺点:

随着项目的迭代升级,代理的目标增多,代理的增强内容变多(可能不同的实现类需要增强的内容不同),代理类也会越来越庞大,对整个维护过程也会变得复杂。

动态代理

动态代理用的很是广泛,如面试必问、项目必用的AOP,心中一阵绞痛,想当年因为这问题也在面试中被虐的体无完肤。具体的AOP源码抛开暂时不看,我们所知道的就是它的重要一个实现机制就是动态代理,那就从最基础的了解动态代理的实现。

代码分析

创建变形金刚接口TransformersDynamic
public interface TransformersDynamic {
    void trans2man();//变形->人
    void trans2car();//变形->车
}
创建擎天柱TransformersDynamicImpl,实现变形金刚接口
public class TransformersDynamicImpl implements TransformersDynamic {
    @Override
    public void trans2man() {
        System.out.println("---->transform to man");
    }

    @Override
    public void trans2car() {
        System.out.println("---->transform to car");
    }
}
创建代理类TransformersDynamicProxy,实现InvocationHandler接口
public class TransformersDynamicProxy implements InvocationHandler {

    private Object proxyObject;

   public TransformersDynamicProxy(Object proxyObject){
        this.proxyObject = proxyObject;
    }

    /**
     * 获取代理对象
     */
    public Object newProxyInstance() {
        return Proxy.newProxyInstance(proxyObject.getClass().getClassLoader(),
                proxyObject.getClass().getInterfaces(),
                this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("---->dynamic invoke before");
        method.invoke(proxyObject, args); //执行目标方法
        System.out.println("---->dynamic invoke after");
        return null;
    }
}

说明:实现InvocationHandler接口后实现invoke接口,这个接口中就定义了这一个接口,源码:

/**
 * @author      Peter Jones
 * @see         Proxy
 * @since       1.3
 */
public interface InvocationHandler {

    /**
     *  此处省略100000行注释……
     * @see     UndeclaredThrowableException
     */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

proxy:指我们所代理的真实对象
method:指的是我们所要调用真实对象的某个方法Method对象
args:指调用真实对象某个方法时接受的参数
在newProxyInstance方法内,通过Proxy类生成代理对象,第二个参数Interfaces,是获取当前目标类实现的所有接口。

运行测试方法,测试代理过程
//动态代理方式
@Test
public void dynamicProxyTest() {
    System.out.println("=========dynamic proxy test start=========");
    TransformersDynamicProxy dynamicProxy = new TransformersDynamicProxy(new TransformersDynamicImpl());
    TransformersDynamic proxy = (TransformersDynamic) dynamicProxy.newProxyInstance();
    System.out.println("----->" + proxy.getClass());
    proxy.trans2man();
    System.out.println("~~~~~~~~~华丽分隔线~~~~~~~~~~");
    proxy.trans2car();
    System.out.println("=========dynamic proxy test end=========");
}

执行结果:

=========dynamic proxy test start=========
----->class com.sun.proxy.$Proxy11
---->dynamic invoke before
---->transform to man
---->dynamic invoke after
~~~~~~~~~华丽分隔线~~~~~~~~~~
---->dynamic invoke before
---->transform to car
---->dynamic invoke after
=========dynamic proxy test end=========

打印的proxy可以看出,这个对象并不是在创建时传入的TransformersDynamicImpl对象,而是通过Proxy生成的动态代理对象。
到这里静态代理和动态代理的最基本原理已经说完了。但是这里还是需要说点其他的。

动态代理的坑

上面动态代理可以看出来,trans2man和trans2car都会目标方法,在执行的时候都会执行before和after,但是下面这个演示,你将看到不一样的结果。

代码的改动

1、将dynamicProxyTest方法改成如下内容:

//动态代理方式
@Test
public void dynamicProxyTest() {
    System.out.println("=========dynamic proxy test start=========");
    TransformersDynamicProxy dynamicProxy = new TransformersDynamicProxy(new TransformersDynamicImpl());
    TransformersDynamic proxy = (TransformersDynamic) dynamicProxy.newProxyInstance();
    System.out.println("----->" + proxy.getClass());
    proxy.trans2man();
    System.out.println("=========dynamic proxy test end=========");
}

2、在TransformersDynamicImpl类的trans2man方法中调用trans2car方法:

@Override
public void trans2man() {
    System.out.println("---->transform to man");
    trans2car();
}

执行结果:

=========dynamic proxy test start=========
----->class com.sun.proxy.$Proxy11
---->dynamic invoke before
---->transform to man
---->transform to car
---->dynamic invoke after
=========dynamic proxy test end=========

从结果分析可以看出来,在输出transform to car前后少了一对before和after,也就意味着这个时候trans2car没有被增强,为什么呢,trans2car是被增强的啊。
这里需要理解的是,在trans2man中调用trans2car方法前面还隐含着一个调用对象,补全就是this.trans2car(),也就是当前对象调用的trans2car方法,并不是代理对象调用,那就肯定没有增强逻辑的执行了。代理被绕过,没有生效。在这里有这个问题,那么对于Spring AOP的动态代理有没有问题呢?

源码提供

具体的代码是在com.minuor.staticProxy包(静态代理示例)、com.minuor.dynamicProxy包(动态代理示例),测试类在test下com.minuor.MinuorJunitService。
gitHub:https://github.com/minuor/proxy
码云:https://gitee.com/minuor/proxy

文章推荐

项目发布成功、失败,Java项目来实现邮件通知相关人员,实时反馈提醒信息
Spring AOP+反射实现自定义动态配置校验规则,让校验规则飞起来
项目上线,旧数据需要修改,写SQL太麻烦,看Excel配合简单SQL的强大功能

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!