1. 程式人生 > >靜態代理 動態代理 cglib原理區分

靜態代理 動態代理 cglib原理區分

本文主要從三個方面介紹代理模式,什麼是代理模式,提供了什麼好處;代理模式的三種實現方式;三種代理的區別

首先簡單說明下為什麼需要代理模式:為其他物件提供一種代理以控制對這個物件的訪問,可以隔離客戶端和委託類的中介。我們還可以藉助代理來在增加一些功能,而不需要修改原有程式碼。

重點是代理模式的三種實現方式:

先給出簡單的介面和實現類:

public interface IHello {
    void sayHello();
}
public final class Hello implements IHello{
    @Override
    public void sayHello() {
        System.out.println("hello");

    }
}

1靜態代理模式

public class StaticProxy {
    IHello hello;
    public StaticProxy(IHello hello){
        this.hello=hello;
    }
    public void syaHello(){
        System.out.println("before");
        hello.sayHello();
        System.out.println("after");
    }
    public static void  main(String  args[]){
        new StaticProxy(new Hello()).syaHello();
    }
}

輸出為:

before hello after

2java動態代理實現的動態代理

//java動態代理實現的動態代理
public class DynamicProxy implements InvocationHandler {
    Object target;
    public DynamicProxy(Object target){
        this.target=target;
    }

    public Object bind(){
        return Proxy.newProxyInstance(this.target.getClass().getClassLoader(),this.target.getClass().getInterfaces(),this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before");
        method.invoke(this.target,args);
        System.out.println("after");
        return null;
    }

    public static void  main(String  args[]){
        //此處返回的必定是介面,不可以轉為具體實體類,所以java反射要求被代理類必須實現了介面;但不要求final
IHello hello = (IHello) new DynamicProxy(new Hello()).bind(); hello.sayHello(); } }

java動態代理實現代理步驟:

a定義介面及介面實現類

b定義實現InvocationHandler介面的類並重寫invoke方法

c呼叫 Proxy.newProxyInstance(被代理的類.getClass().getClassLoader(),被代理的類.getClass().getInterfaces(),InvocationHandler的實現類);生成被代理的反射類

d呼叫需要執行的方法

3cglib實現的動態代理

public class CGLibProxy {
    public static void  main(String  args[]){
        Enhancer enhancer = new Enhancer();
        //注意如果此時將Hello類宣告為final,則會報IllegalArgumentException;但不要求實現介面
        enhancer.setSuperclass(Hello.class);
        enhancer.setCallback(new MethodInterceptor(){
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("before");
                Object result = methodProxy.invokeSuper(o,objects);
                System.out.println("after");
                return result;
            }
        });
        Hello hello = (Hello)enhancer.create();
        hello.sayHello();
    }
}

Ehancer介紹:

Enhancer Enhancer可能是CGLIB中最常用的一個類,和Java1.3動態代理中引入的Proxy類差不多(如果對Proxy不懂,可以參考這裡)。和Proxy不同的是,Enhancer既能夠代理普通的class,也能夠代理介面。Enhancer建立一個被代理物件的子類並且攔截所有的方法呼叫(包括從Object中繼承的toString和hashCode方法)。Enhancer不能夠攔截final方法,例如Object.getClass()方法,這是由於Java final方法語義決定的。基於同樣的道理,Enhancer也不能對fianl類進行代理操作。這也是Hibernate為什麼不能持久化final class的原因。

public class SampleClass {     public String test(String input){         return "hello world";     } } 下面我們將以這個類作為主要的測試類,來測試呼叫各種方法

@Test public void testFixedValue(){     Enhancer enhancer = new Enhancer();     enhancer.setSuperclass(SampleClass.class);     enhancer.setCallback(new FixedValue() {         @Override         public Object loadObject() throws Exception {             return "Hello cglib";         }     });     SampleClass proxy = (SampleClass) enhancer.create();     System.out.println(proxy.test(null)); //攔截test,輸出Hello cglib     System.out.println(proxy.toString());      System.out.println(proxy.getClass());     System.out.println(proxy.hashCode()); } 程式的輸出為:

Hello cglib Hello cglib class com.zeus.cglib.SampleClass$$EnhancerByCGLIB$$e3ea9b7

java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number

    at com.zeus.cglib.SampleClass$$EnhancerByCGLIB$$e3ea9b7.hashCode(<generated>)     ... 上述程式碼中,FixedValue用來對所有攔截的方法返回相同的值,從輸出我們可以看出來,Enhancer對非final方法test()、toString()、hashCode()進行了攔截,沒有對getClass進行攔截。由於hashCode()方法需要返回一個Number,但是我們返回的是一個String,這解釋了上面的程式中為什麼會丟擲異常。

Enhancer.setSuperclass用來設定父型別,從toString方法可以看出,使用CGLIB生成的類為被代理類的一個子類,形如:SampleClass$$EnhancerByCGLIB$$e3ea9b7

Enhancer.create(Object…)方法是用來建立增強物件的,其提供了很多不同引數的方法用來匹配被增強類的不同構造方法。(雖然類的構造放法只是Java位元組碼層面的函式,但是Enhancer卻不能對其進行操作。Enhancer同樣不能操作static或者final類)。我們也可以先使用Enhancer.createClass()來建立位元組碼(.class),然後用位元組碼動態的生成增強後的物件。  

原理上:

Java動態代理使用Java原生的反射API進行操作,在生成類上比較高效;CGLIB使用ASM框架直接對位元組碼進行操作,在類的執行過程中比較高效

使用上:

Java動態代理只能夠對介面進行代理,不能對普通的類進行代理(因為所有生成的代理類的父類為ProxyJava類繼承機制不允許多重繼承);CGLIB能夠代理普通類,但是不能代理final修改時的類;

java動態代理是利用反射機制生成一個實現代理介面的匿名類,在呼叫具體方法前呼叫InvokeHandler來處理。而cglib動態代理是利用asm開源包,對代理物件類的class檔案載入進來,通過修改其位元組碼生成子類來處理。

  1. 如果目標物件實現了介面,預設情況下會採用JDK的動態代理實現AOP  2、如果目標物件實現了介面,可以強制使用CGLIB實現AOP  3、如果目標物件沒有實現了介面,必須採用CGLIB庫,會自動在JDK動態代理和CGLIB之間轉換

借鑑:https://blog.csdn.net/danchu/article/details/70238002?utm_source=copy