抽丝剥茧——代理设计模式

代理设计模式

代理设计模式再生活中应该很常见了,现在各种中间商的货物代售方便了我们的生活也增加了我们生活的成本。这种生活中的中间商行为就是一种代理模式。

拿一个品牌来说明:

在编程领域中一般存在两种代理模式

  • 静态代理。(仅仅可以代理一个类的行为,不能随类的变化而变化)
  • 动态代理。(可以代理所有类的行为)

接下来我们先来看静态代理

1. 静态代理

仅仅用来代理一个类的行为。

代码演示一下:

  • 继承实现代理(不推荐,耦合性大
class NaiKe {

    void run() {
        System.out.println("耐克");
    }
}


//代理类
class ShoesProxy extends NaiKe{
    @Override
    void run() {
        System.out.println("agency shoes before");
        super.run();
        System.out.println("agency shoes after");
    }
}
  • 组合实现代理(推荐)
class NaiKe{

    void run() {
        System.out.println("耐克");
    }
}

class ShoesProxy {

    NaiKe naiKe = new NaiKe();

    void run() {
        System.out.println("agency shoes before");
        naiKe.run();
        System.out.println("agency shoes after");
    }
}
  • 多态实现代理,多个代理嵌套
public class ProxyDesgin {
    public static void main(String[] args) {
        Shoes shoes = new ShoesProxy(new ShoesTimer(new NaiKe()));
        shoes.run();
    }
}

abstract class Shoes{
   abstract void run();
}

class NaiKe extends Shoes{

    @Override
    void run() {
        System.out.println("耐克");
    }
}

class Adi extends Shoes{
    @Override
    void run() {
        System.out.println("阿迪达斯");
    }
}

//代理类
class ShoesProxy extends Shoes {

    Shoes shoes ;

    public ShoesProxy(Shoes shoes){
        this.shoes = shoes ;
    }

    void run() {
        System.out.println("agency shoes before");
        shoes.run();
        System.out.println("agency shoes after");
    }
}


class ShoesTimer extends Shoes {

    Shoes shoes ;

    public ShoesTimer(Shoes shoes){
        this.shoes = shoes ;
    }

    void run() {
        System.out.println("log timer shoes before");
        shoes.run();
        System.out.println("log timer shoes after");
    }
}

画个图瞅瞅静态代理

这个就是静态代理,兄弟们应该已经发现了它的缺点,只能指定自己想要进行代理的类,而不能对所有的类进行代理,扩展性太差,所以引出了动态代理

2.动态代理

谈到动态代理,脑子里第一个出现的肯定就是Java动态代理了。我们先来聊一下Java动态代理。

2.1 Java动态代理

先来看一个动态代理的案例

NaiKe naiKe = new NaiKe();
        Shoes shoes = (Shoes) Proxy.newProxyInstance(NaiKe.class.getClassLoader(),new Class[]{Shoes.class},new InvocationHandler() {
            @Override
            public Object invoke(Object proxy,Method method,Object[] args) throws Throwable {
                System.out.println("begin timer : " + System.currentTimeMillis());
                method.invoke(naiKe,args);
                System.out.println("after timer : " + System.currentTimeMillis());
                return null;
            }
        });
        shoes.run();
  • 第一个参数。通过动态代理创建的对象被哪个加载器加载,一般使用本类的类加载器即可
  • 第二个参数。被代理对象要实现的方法
  • 第三个参数。被代理对象被调用的时候该如何处理逻辑

我们看一下动态代理的源码。

我们可以通过以下方式让JVM将动态生成的代理类保存到我们的项目中

  • JDK1.8使用System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
  • JDK1.8以上可以使用1 System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles","true");

生成的代理类如下:

final class $Proxy0 extends Proxy implements Shoes {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
    }

    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 String toString() throws  {
    }

    public final int hashCode() throws  {
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals",Class.forName("java.lang.Object"));
            m3 = Class.forName("desgin.proxy.Shoes").getMethod("run");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

从这个类的结构中,我们可以看出很多的东西

  • 为什么说JAVA动态代理仅仅只能代理接口。(类单继承,代理对象默认继承Proxy类
  • 动态代理的第二个参数,接口内部的方法会被代理对象重写,然后调用第三个参数的invoke方法。

上面两个也是动态代理的原理了。我们来仔细看一下我们的run()方法,也就是我们代理对象要实现的接口

public final void run() throws  {
        try {
            super.h.invoke(this,(Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
  • 调用了父类的h,父类的hInvocationHandler,然后调用了invoke方法执行了我们的执行逻辑。

这个就是动态代理的全部实现过程

还有一个非常牛逼的点,它怎么生成的这个代理类。来看一下代理的全过程

图中的ASM就是为我们动态生成一个代理类的工具,它直接操作了Class字节码的二进制,然后创建了一个代理类,返回给我们。

Java动态代理就聊到这里了。下面看一看CGLIbAOP

2.2 CGLIB动态代理

弥补了Java动态代理的不足,CGLIB动态代理可以代理类。它直接创建了一个被代理对象的子类,实现了对其的代理过程。我们来看一下它的代理过程

//打印生成的代理对象,放置于当前项目下
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,".");
        //创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
        Enhancer enhancer = new Enhancer();
        //设置目标类的字节码文件
        enhancer.setSuperclass(Tank.class);
        //设置回调函数
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o,Object[] objects,MethodProxy methodProxy) throws Throwable {
                methodProxy.invokeSuper(o,objects);
                return null;
            }
        });

        //这里的creat方法就是正式创建代理类
        Tank proxyDog = (Tank)enhancer.create();
        //调用代理类的eat方法
        proxyDog.tank();

还是和Java动态代理相似,传入一个需要代理的Class,设置代理的回调函数。然后调用create创建一个代理对象,调用代理对象的方法。

代理第一行可以输出代理对象,会生成三个代理对象。

查看中间那个,可以看到我们被代理对象的方法

public class Tank$$EnhancerByCGLIB$$a4ec679a extends Tank implements Factory {
    //构造方法
    public Tank$$EnhancerByCGLIB$$a4ec679a() {
        CGLIB$BIND_CALLBACKS(this);
    }
    //被代理方法
    final void tank() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            //调用增强的方法
            var10000.intercept(this,CGLIB$tank$0$Method,CGLIB$emptyArgs,CGLIB$tank$0$Proxy);
        } else {
            super.tank();
        }
    }
}

在之前的CGLIB动态代理实现中,我们看到了拦截的回调中传入了四个参数,从上面的源码中可以看到对应参数的作用。

  • Object o代表生成的代理对象
  • Method method代表当前代理对象调用的方法
  • Object[] objects代表方法的参数
  • MethodProxy methodProxy我们调用方法的方法代理,它没有使用Java本身的反射,而是动态生成一个新的类,(继承FastClass),向类中写入委托类实例直接调用方法的语句。

我们可以看一下superinvoke的源码

public Object invokeSuper(Object obj,Object[] args) throws Throwable {
        try {
            this.init();
            MethodProxy.FastClassInfo fci = this.fastClassInfo;
            return fci.f2.invoke(fci.i2,obj,args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        }
    }

private static class FastClassInfo {
        FastClass f1;
        FastClass f2;
        int i1;
        int i2;

        private FastClassInfo() {
        }
    }

一个图理解CgLib动态代理过程

写了这么多,感觉对于代理设计模式讲解的篇幅不是很大,而是着重讲解了动态代理的实现方式。总的而言,代理设计模式与我们日常生活非常的接近,生活中的事物几乎都在被代理,所以这个设计模式应该很好懂,所以着重讲解了动态代理的实现方式。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


什么是设计模式一套被反复使用、多数人知晓的、经过分类编目的、代码 设计经验 的总结;使用设计模式是为了 可重用 代码、让代码 更容易 被他人理解、保证代码 可靠性;设计模式使代码编制  真正工程化;设计模式使软件工程的 基石脉络, 如同大厦的结构一样;并不直接用来完成代码的编写,而是 描述 在各种不同情况下,要怎么解决问题的一种方案;能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免引
单一职责原则定义(Single Responsibility Principle,SRP)一个对象应该只包含 单一的职责,并且该职责被完整地封装在一个类中。Every  Object should have  a single responsibility, and that responsibility should be entirely encapsulated by t
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强烈推荐。原文截图*************************************************************************************************************************原文文本************
适配器模式将一个类的接口转换成客户期望的另一个接口,使得原本接口不兼容的类可以相互合作。
策略模式定义了一系列算法族,并封装在类中,它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
设计模式讲的是如何编写可扩展、可维护、可读的高质量代码,它是针对软件开发中经常遇到的一些设计问题,总结出来的一套通用的解决方案。
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
迭代器模式提供了一种方法,用于遍历集合对象中的元素,而又不暴露其内部的细节。
外观模式又叫门面模式,它提供了一个统一的(高层)接口,用来访问子系统中的一群接口,使得子系统更容易使用。
单例模式(Singleton Design Pattern)保证一个类只能有一个实例,并提供一个全局访问点。
组合模式可以将对象组合成树形结构来表示“整体-部分”的层次结构,使得客户可以用一致的方式处理个别对象和对象组合。
装饰者模式能够更灵活的,动态的给对象添加其它功能,而不需要修改任何现有的底层代码。
观察者模式(Observer Design Pattern)定义了对象之间的一对多依赖,当对象状态改变的时候,所有依赖者都会自动收到通知。
代理模式为对象提供一个代理,来控制对该对象的访问。代理模式在不改变原始类代码的情况下,通过引入代理类来给原始类附加功能。
工厂模式(Factory Design Pattern)可细分为三种,分别是简单工厂,工厂方法和抽象工厂,它们都是为了更好的创建对象。
状态模式允许对象在内部状态改变时,改变它的行为,对象看起来好像改变了它的类。
命令模式将请求封装为对象,能够支持请求的排队执行、记录日志、撤销等功能。
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。 基本介绍 **意图:**在不破坏封装性的前提下,捕获一个对象的内部状态,并在该
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为
享元模式(Flyweight Pattern)(轻量级)(共享元素)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结