关于java:动态代理-java原生-vs-Cglib

42次阅读

共计 4992 个字符,预计需要花费 13 分钟才能阅读完成。

代理模式

代理模式的定义:

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 hello
Hello from ProxyJDK1Imp
I am check
I am last function check1
after invoke hello
before invoke check
I am check
I am last function check1
after 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 beforehello
Hello from ProxyJDK1Imp
This is cglib Proxy beforecheck
I am check
This is cglib Proxy beforecheck1
I am last function check1
This is cglib Proxy after check1
This is cglib Proxy after check
This is cglib Proxy after hello
    

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

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

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

正文完
 0