代理模式

代理模式的定义:

Provide a surrogate or placeholder for another object to control access to it.

即为对象提供一个代理以管制对其的拜访。

代理模式利用最多的场景是不侵入被代理对象的前提下,对被代理对象进行性能加强。

不侵入被代理对象却能加强被代理对象的性能,这一NB个性使得代理模式无论是日常编码、亦或是各类框架中被广泛应用。

最NB的利用场景是AOP,动静代理是AOP最根底的底层技术逻辑,不论是哪一家的AOP框架,其外围其实都是通过动静代理实现的。

动静代理的概念

代理分为动态代理和动静代理,动态代理指的是在代码编写的过程中实现代理模式,绝对非常简单。

动静代理指的是在运行时生成代理对象的字节码、从而实现在运行过程中扭转或者加强被代理对象的行为。因为动静代理不须要在编码阶段通过代码形式写死代理模式的实现,因而具备很强的灵活性。

JAVA中利用最广的动静代理实现形式有两种,一种是JDK原生动静代理实现形式,另一种是通过Cglib实现。

这两种形式的基本区别就是,JDK原生动静代理基于接口实现,运行时生成的代理对象通过实现该接口生成。所以,要求被代理对象也必须实现该接口。

Cglib是基于继承实现,所以,对被代理对象是否继承接口没有要求。运行时生成的代理对象是被代理对象的子类的对象。因而,要求被代理对象的被代理办法不能是final的(final办法不能被扩大)、被代理对象必须有无参结构器。

这两个要求如果不满足的话,Cglig有不同的解决:
final办法不能被代理,即便你创立了Cglib代理对象、通过代理对象调用了被代理对象的final办法,但最终执行的依然是被代理对象的办法,也就是说,该办法无奈被代理对象加强。所以,应用Cglib调用代理对象的final办法后,运行并不会报错,只不过是不能达到加强办法的目标。

然而如果被代理对象没有无参结构器的话,Cglib代理对象运行时会抛出异样,程序无奈运行。没有无参结构器当然不是说没有显式定义无参结构器,因为如果某一个类没有任何显式定义任何结构器的话,这个类是有无参结构器的,然而如果定义了有参数结构器、然而却没有显式定义无参结构器,这种状况下才是真正意义的没有无参结构器。

实现案例

咱们就用最简略的HelloWorld举例,假如场景为:HelloWorld有三个办法:hello、check、check1,办法非常简单,就是打印一句话。

要实现的指标是:通过动静代理实现在调用hello办法前、调用后别离打印一句话,hello办法调用check办法,check办法调用check1。通过在被代理对象外部调用check和chekc1办法来察看一下,check和check1办法是否能被代理加强。

JDK原生动静代理

应用JDK原生动静代理须要以下元素:

  1. 被代理对象(必须实现接口)。
  2. 实现了InvocationHandler接口的动静代理回调处理器。
  3. 代理对象。

被代理对象:
因为JDK原生动静代理要求被代理对象必须实现接口,所以,首先定义接口:

public interface HelloWorldInterface {    void hello();    void check();    void check1();}

被代理对象的实现类:

public class HelloWroldImp implements HelloWorldInterface {    @Override    public void hello() {        System.out.println("Hello from ProxyJDK1Imp");        check();    }    @Override    public void check(){        System.out.println("I am check");        check1();    }    @Override    public void check1(){        System.out.println("I am last function check1");    }

回调处理器
回调处理器是针对代理对象而言的,代理对象执行被代理对象的接口办法时,会调用回调对象的invoke办法。理论动静代理的办法加强性能齐全是依赖于回调处理器的invoke办法实现的:回调对象中的target就是被代理对象,通过invoke办法最终调用到被代理对象的原始办法,而在invoke办法中咱们能够实现加强性能。本例需要只是在调用前后打印日志,所以在调用被代理对象办法前后别离输入日志即可。

public class JDKProxyHandle implements InvocationHandler {    private Object target;    public JDKProxyHandle(Object target){        this.target=target;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("before invoke " + method.getName());        method.invoke(target,args);        System.out.println("after invoke "+ method.getName());        return null;    }}

代理对象
通过Proxy的静态方法.newProxyInstance创立代理对象,首先须要创立被代理对象实例,并将给实例作为参数传入回调对象的初始化办法创立回调对象。之后创立代理对象:

public static void main(String[] args) {        HelloWorldInterface helloWorldInterface=new HelloWroldImp();        InvocationHandler handler=new JDKProxyHandle(helloWorldInterface);        HelloWorldInterface proxyHelloWorld=(HelloWorldInterface) Proxy.newProxyInstance(                helloWorldInterface.getClass().getClassLoader(),                helloWorldInterface.getClass().getInterfaces(),                handler        );}

测试
执行代理对象的hello办法:

public static void main(String[] args) {        HelloWorldInterface helloWorldInterface=new HelloWroldImp();        InvocationHandler handler=new JDKProxyHandle(helloWorldInterface);        HelloWorldInterface proxyHelloWorld=(HelloWorldInterface) Proxy.newProxyInstance(                helloWorldInterface.getClass().getClassLoader(),                helloWorldInterface.getClass().getInterfaces(),                handler        );        proxyHelloWorld.hello();        proxyHelloWorld.check();           }

输入

before invoke helloHello from ProxyJDK1ImpI am checkI am last function check1after invoke hellobefore invoke checkI am checkI am last function check1after invoke check    

从测试后果能够失去如下论断:

  1. 被代理对象的hello()办法被加强。
  2. hello()办法外部调用的check()办法,check()办法外部调用check1()。check()和check1()办法均不能再次被加强。
  3. 独自通过代理对象调用check()办法,check()办法被加强

所以,JDK原生动静代理的加强性能在代理对象外部调用中不能被传递!!!

Cglib动静代理

应用Cglib动静代理须要以下元素:

  1. 被代理对象(不须要实现接口)。
  2. 实现了MethodInterceptor接口的拦截器。
  3. 代理对象。

被代理对象:
Cglib动静代理底层通过扩大生成被代理对象的子类实现,所以,被代理对象不须要实现接口,依然应用上例的HelloWorldImp作为被代理对象。
MethodInterceptor拦截器
拦截器的作用同JDK原生动静代理的回调器,拦截器外部须要持有被代理对象target,须要实现回调办法intercept,并在intercept办法外部实现加强。

public class CglibInterceptor implements MethodInterceptor {    private Object target;    public CglibInterceptor(Object target){        this.target=target;    }    @Override    public Object intercept(Object o, Method method,                            Object[] objects,                            MethodProxy methodProxy) throws Throwable {        System.out.println("This is cglib Proxy before"+method.getName());        methodProxy.invokeSuper(o,objects);        System.out.println("This is cglib Proxy after " + method.getName());        return null;    }}

代理对象
以下创立代理对象的办法能够在client端实现,也能够在拦截器外部实现,也能够专门创立一个Proxy创立工厂获取。其中最重要的就是这个Cglib的enhancer,他是实现Cglib动静代理的要害。enhancer有两个最重要的成员变量,一个是superclass,另一个是回调对象。很容易了解,superclass是被代理对象的class对象,用来创立代理对象,回调对象就是下面咱们创立的拦截器,用来在执行代理对象办法的时候进行回调、实现加强。

//创立代理    public Object createProxy(){        //创立外围类        Enhancer enhancer = new Enhancer();            //设置父类        enhancer.setSuperclass( target.getClass());        //设置回调        CglibInterceptor cli=new CglibInterceptor();        enhancer.setCallback(cli);        // 生成代理        Object proxy = enhancer.create();        return proxy;    }

测试

public static void main(String[] args) {                HelloWroldImp hwi = new HelloWroldImp();        CglibInterceptor  cgi=new CglibInterceptor(hwi);        HelloWroldImp proxyHwi=(HelloWroldImp)cgi.createProxy();        proxyHwi.hello();    }

输入

This is cglib Proxy beforehelloHello from ProxyJDK1ImpThis is cglib Proxy beforecheckI am checkThis is cglib Proxy beforecheck1I am last function check1This is cglib Proxy after check1This is cglib Proxy after checkThis is cglib Proxy after hello    

从测试后果能够失去如下论断:

  1. 被代理对象的hello()办法被加强。
  2. hello()办法外部调用的check()办法,check()办法外部调用check1()。均被再次加强。

所以,Cglib动静代理的加强性能在代理对象外部调用中能够被传递加强!!!