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

代理设计模式

代理设计模式再生活中应该很常见了,如今各类中间商的货物代售方便了咱们的生活也增长了咱们生活的成本。这种生活中的中间商行为就是一种代理模式。java

拿一个品牌来讲明:编程

在编程领域中通常存在两种代理模式设计模式

  • 静态代理。(仅仅能够代理一个类的行为,不能随类的变化而变化)
  • 动态代理。(能够代理全部类的行为)

接下来咱们先来看静态代理ide

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");
    }
}

画个图瞅瞅静态代理this

这个就是静态代理,兄弟们应该已经发现了它的缺点,只能指定本身想要进行代理的类,而不能对全部的类进行代理,扩展性太差,因此引出了动态代理设计

2.动态代理

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

2.1 Java动态代理

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

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, m3, (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, Method method, 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动态代理过程

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

相关文章
相关标签/搜索