java核心基礎之代理機制詳解(靜態代理、動態代理:JDK、CGlib)

(一)什麼是代理?

在生活中經常會遇到代理,比如買房我們是去找中介,而不是自己一棟樓一棟樓去挑選,這裏的中介就是代理。代理即通過代理對象訪問目標對象,還可以在目標對象基礎上增強額外的功能。java的代理分爲靜態代理和動態代理。靜態代理即在代碼運行前,代理類就已經存在了。動態代理指代理類不是寫在代碼中的,而是在運行過程中產生的。

(二)靜態代理

靜態代理就是在代碼運行之前,代理類就已經存在了。通過一個實例來模擬靜態代理;:

以租房爲例,租房有兩種方式,一種是直接找房東去租房,另一種是找租房軟件比如自如(不是廣告),這種就是通過代理訪問目標對象。

首先新建一個租房的接口,只帶一個rent方法:

public interface Renting {
    String rent(String name);
}

接着新建一個Person類實現接口,這個類的意思是直接找房東租房,也就是目標對象:

public class Person implements Renting{
    @Override
    public String rent(String name) {
        return "租了"+name;
    }
}

然後新建代理類Ziru,可以直接通過代理類去訪問目標對象,我們前面也講到了代理類不光可以實現訪問目標對象,還可以在目標對象基礎上增強額外的功能。因此增加一條收取服務類的額外功能。

public class Ziru implements Renting{
    Person person;
    public Ziru(){
        person=new Person();
    }
    @Override
    public String rent(String name) {
        System.out.println("收取手續費");
        return person.rent(name);
    }
}

通過兩種方式:使用代理和不使用代理來訪問目標對象:

public class run {
    public static void main(String[] args) {
          //不使用代理
//        Person person=new Person();
//        person.rent("碧桂園");
          //使用代理
          Ziru ziru=new Ziru();
          System.out.println(ziru.rent("碧桂園"));
    }
}

得到運行結果。

(三)動態代理

動態代理指代理類不是寫在代碼中的,而是在運行過程中產生的。java提供了兩種實現動態代理的方式,分別是基於Jdk的動態代理和基於Cglib的動態代理。

(3.1) 基於Jdk的Proxy

首先介紹基於Jdk的Proxy,繼續使用租房的案例:

其中Renting和Person代碼和上面的靜態代理相同,新建一個JdkProxy類,這個類是動態代理的核心部分,實現InvocationHandler 接口。InvocationHandler 是一個接口,每個代理的實例都有一個與之關聯的 InvocationHandler 實現類,如果代理的方法被調用,那麼代理便會通知和轉發給內部的 InvocationHandler 實現類invoke,由它實現處理內容。這段代碼的另外一個核心是Proxy.newProxyInstance,該方法需要三個參數,目的是運行期間生成代理類,每個參數的功能已經寫在了註釋中。這段代碼的意思就是Proxy 動態產生的代理對象會調用 InvocationHandler 實現類invoke。

public class JdkProxy implements InvocationHandler {
    Renting obj;

    public Renting getProxy(Renting obj){
        this.obj=obj;
        //運行期間創建對象
        Object proxy = Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
        //obj.getClass().getClassLoader()類加載器
        //obj.getClass().getInterfaces() 目標類實現的接口
        //InvocationHandler對象
        return (Renting) proxy;
    }

    //proxy 代理對象
    //method 要實現的方法
    //args 方法的參數
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //拿到目標方法執行結果
        Object invoke = method.invoke(obj, args);
        return invoke;
    }
}

接下來是實現類:

public class run {
    public static void main(String[] args) {
        Person person=new Person();
        JdkProxy jdkProxy=new JdkProxy();
        Renting proxy = jdkProxy.getProxy(person);
        String res = proxy.rent("碧桂園");
        System.out.println(res);
    }
}

當調用proxy.rent()方法時,會自動執行invoke方法,以實現動態代理。

你可以在invoke方法中增加一些自己的代碼,比如我在invoke方法中寫一條

運行時就會在控制檯上打印出來,這也是Spring AOP的核心

JDK動態代理類實現了InvocationHandler接口,重寫的invoke方法。

JDK動態代理的基礎是反射機制(method.invoke(對象,參數))Proxy.newProxyInstance()

(3.2)基於Cglib的動態代理

通過CGlib也能實現動態代理,在寫代碼之前導入兩個jar包asm-3.3.1,cglib-2.2.2

CGlib動態代理的實現,其中Renting和Person代碼和靜態代理相同,這裏就不再展示。CGlib還可以直接代理類,爲了比較,這裏依舊使用接口。新建CglibProxy,實現了MethodInterceptor接口,該接口需要實現intercept方法。intercept方法中依舊調用method.invoke()方法。與Jdk不同的是創建代理對象的方式,CGlib中使用Enhance創建代理對象。

public class CglibProxy implements MethodInterceptor {
    Renting obj;
    //通過Enhance動態創建對象
    public Renting getProxy(Renting obj){
        this.obj=obj;
        Enhancer enhancer=new Enhancer();
        //設置父類
        enhancer.setSuperclass(obj.getClass());
        //設置回調方法
        enhancer.setCallback(this);
        return (Renting) enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object invoke = method.invoke(obj, objects);
        return invoke;
    }
}

接下來是實現類:

public class run {
    public static void main(String[] args) {
        Person person=new Person();
        CglibProxy cglibProxy=new CglibProxy();
        Renting proxy = cglibProxy.getProxy(person);
        String res = proxy.rent("碧桂園");
        System.out.println(res);
    }
}

得到結果:

(四)總結

1.JDK動態代理只能夠對接口進行代理,不能對普通的類進行代理(因爲所有生成的代理類的父類爲Proxy,Java類繼承機制不允許多重繼承);CGLIB能夠代理普通類;

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

發佈了68 篇原創文章 · 獲贊 962 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章