(JDK、Cglib)動態代理實踐 , demo詳細

序言

在看Retrofit源碼時,我們會發現裏面是使用了Java的動態代理的。 以前也接觸過動態代理,平常不用,時間就久了就會慢慢淡忘。所以平常多總結,任何知識點也是如此。

作用

  1. 爲其他的對象提供一種代理來控制這個對象的訪問。
  2. 增強目標對象的功能

靜態代理

開發者接口

public interface IDeveloper {
    void writeCode();
}    

Android開發者

public class AndroidDeveloper implements IDeveloper {

     @Override
	 public void writeCode() {
   		 System.out.println("Write Android Code");
	 }
}

測試

public class DevelopTest {

    public static void main(String arg[]){
    	IDeveloper developer=new AndroidDeveloper();
    	developer.writeCode();
	}
}

運行結果:

Write Android Code

現在麻煩的是,如果以後上級領導說,寫代碼前必須寫註釋寫文檔。 那我們就需要修改AndroidDeveloper這個類,這就不符合 “開閉原則”(對擴展開放,對修改關閉)。 所以我們可以創建個代理類,以不修改原始類中的代碼來實現增強。

代理類

	public class DeveloperProxy implements IDeveloper {
	private  IDeveloper developer;

	public DeveloperProxy(IDeveloper developer){
    	this.developer=developer;
	}

	@Override
	public void writeCode() {
   		System.out.println("write Document or Note");
    	developer.writeCode();
	}
}

測試

public class DevelopTest {

	public static void main(String arg[]){
    
       IDeveloper developer=new AndroidDeveloper();
       DeveloperProxy proxy=new DeveloperProxy(developer);
       proxy.writeCode();
    }
}

運行結果:

write Document or Note
Write Android Code

可以看到我們在沒有修改AndroidDevelop這個類,通過代理類來實現增強。

總結:

  • 被代理的類(AndroidDeveloper)和代理類(DeveloperProxy)實現了同一個接口,然後在代理類中引入AndroidDeveloper對象的引用。
  • 可以不改變被代理的類,來增強功能。

優點:

  • 代理使客戶端不需操作 被代理的類(AndroidDeveloper), 只需要操作代理類(DeveloperProxy)就行了(解耦合)。

缺點:

  • 代理類跟被代理的類需要實現同樣的接口,代理類跟被代理的類也實現了相同的方法,這樣就出現大量的重複代碼,接口增加一個方法,代理和被代理類就同時實現這個方法,增加代碼維護的複雜度。
  • 代理類只能代理一種類型(IDeveloper)的對象 ,如果代理多種類型的對象,那肯定爲每一種對象進行代理, 這種的情況靜態代理就做不了了。
    即靜態代理類只能爲特定的接口服務,如果爲多個接口服務,那必須建立多個代理類。

動態代理

開發者接口(需要動態代理的接口)

public interface IDeveloper {
    void writeCode();
}    

Android開發者(動態代理的實際對象)

public class AndroidDeveloper implements IDeveloper {

     @Override
	 public void writeCode() {
   		 System.out.println("Write Android Code");
	 }
}

代理方法被調用前後執行的操作 抽象出來。

public interface Before {

    public void before();
}


public interface After {

    void after();
}

創建自己的調用處理器,統一來處理和過濾一些消息。

public class InvocationHandlerImpl implements InvocationHandler {

private Object targetObject; //需要代理的對象

Before before; 

After after;

public Before getBefore() {
    return before;
}
public void setBefore(Before before) {
    this.before = before;
}

public After getAfter() {
    return after;
}

public void setAfter(After after) {
    this.after = after;
}

public  InvocationHandlerImpl(Object targetObject){
    this.targetObject=targetObject;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    /* 代理方法執行前 執行其他的操作*/
    if (before!=null){
        before.before();
    }
    Object result=method.invoke(targetObject,args);
    /* 代理方法執行後 執行其他的操作*/
    if (after!=null){
        after.after();
    }
    return result;
}
}

測試

public class DevelopTest {

public static void main(String arg[]){

    final IDeveloper p=new AndroidDeveloper();//需要代理的對象
    InvocationHandlerImpl invocationHandler=new InvocationHandlerImpl(p);
    invocationHandler.setBefore(new Before() {
        @Override
        public void before() {
            System.out.println("write Document or Note ");
        }
    });
    invocationHandler.setAfter(new After() {
        @Override
        public void after() {
            System.out.println("Review Code ");
        }
    });

    IDeveloper proxy= (IDeveloper) Proxy.newProxyInstance(p.getClass().getClassLoader(), new Class[]{IDeveloper.class},invocationHandler);
    proxy.writeCode();
}
}

運行結果:

write Document or Note
Write Android Code
Review Code

說明:

  • 當 proxy.writeCode()執行時, 會回調到這個InvocationHandlerImpl中的invoke方法。
  • InvocationHandler中的invode方法中的三個參數 1: 代理對象(proxy) 2: 方法對象(writeCode) 3:方法參數
  • newProxyInstance方法中的三個參數 1: 類加載器 2: 代理所實現的接口 3: 表示的是當動態代理對象調用方法時,會關聯到這個對象上

JDK動態代理實現的步驟:

  • 創建被代理的類和以及實現的接口;
  • 創建一個實現接口InvocationHandler的類。 實現invoke方法;
  • 調用Proxy的newProxyInstance 靜態方法,動態生成代理類;
  • 通過代理對象調用目標方法, 然後會轉發到invoke方法;

總結:

  • 動態代理類在程序運行時動態生成,不需要寫代理類源代碼;
  • 被代理的類必須實現接口,代理的類沒有實現接口的就不行(原因是Java不能進行多繼承,只能實現多接口);
  • 基於Java內部的反射機制來實現;

Cglib 代理

Android開發者(動態代理的實際對象 不需實現任何接口)

public class AndroidDeveloperr {

	 public void writeCode() {
   		 System.out.println("Write Android Code");
	 }
}

代理方法被調用前後執行的操作 抽象出來。

public interface Before {

    public void before();
}


public interface After {

    void after();
}

創建自己的方法攔截器,統一來處理和過濾一些消息。

public class MethodInterceptorImpl implements MethodInterceptor {

Before before;

After after;

public Before getBefore() {
    return before;
}
public void setBefore(Before before) {
    this.before = before;
}

public After getAfter() {
    return after;
}

public void setAfter(After after) {
    this.after = after;
}

@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

    if (before!=null){
        before.before();
    }
     Object result=methodProxy.invokeSuper(o,args);

    if (after!=null){
        after.after();
    }
    return result;
}
}

測試

public class DevelopTest {

public static void main(String arg[]){
    MethodInterceptorImpl methodInterceptor=new MethodInterceptorImpl();
    methodInterceptor.setBefore(new Before() {
        @Override
        public void before() {
            System.out.println("write Document or Note ");
        }
    });
    methodInterceptor.setAfter(new After() {
        @Override
        public void after() {
            System.out.println("Review Code ");
        }
    });
    Enhancer enhancer=new Enhancer();//創建增強類
    enhancer.setSuperclass(AndroidDeveloper.class);//設置繼承被代理類
    enhancer.setCallback(methodInterceptor);//設置回調
    AndroidDeveloper androidDeveloper= (AndroidDeveloper) enhancer.create();//生成代理對象
    androidDeveloper.writeCode();//在調用代理方法時,會回調到我們設置的方法攔截器上所攔截
}
}

運行結果:

write Document or Note
Write Android Code
Review Code

說明:

  • MethodInterceptor中intercept 四個參數的含義 1:cglib生成的代理對象 2: 被代理的對象的方法對象 3: 方法的參數 4: 代理方法對 象

總結 :

  • 可以對類進行代理,不用基於接口代理;
  • 不能對final修飾的類和final方法進行代理(原因是final修飾的 不能被繼承)

JDK和Cglib 區別

  1. JDK原生動態代理不需要引入任何的依賴,但是它是基於接口來進行代理;
  2. Cglib 是通過繼承的方法來進行代理,不管目標對象有沒有實現接口都能進行代理,但是無法代理final的情況。還有就是需要引入相應的依賴;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章