代理及動態代理

    代理模式爲常用的設計模式之一,代理模式爲實際對象提供了一個代理類,用代理類來控制實際對象的訪問(本地或是遠程訪問、起到權限或是對資源的控制),同時也起到隔離和屏蔽實際對象的具體實現細節。其UML圖如下:


接口

public interface Subject {

    public void add();

}


實現類

public class RealSubject implements Subject {

    @Override
    public void add() {
        System.out.println("add method");

    }
}


代理類

public class SubjectAgent implements Subject {

    private Subject sell ;

    public SubjectAgent(Subject sell) {
        this.sell = sell;
    }

    @Override
    public void add() {
        sell.add();
    }
}
代理類中依賴了接口的一個具體實現,來實現Subject接口的方法,屏蔽了Subject接口的具體實現細節。同時,代理類還具有很好的拓展性,可以在代理類裏面添加其他一些跟具體業務無關的邏輯,例如:操作日誌、權限的控制等。

靜態代理特點:

1、代理類與實現類需要共同實現同一個接口

2、不修改實現對象功能的前提下,可以對目標功能做拓展

3、可能存在多個實現類,也會有多個代理類,一旦增加新的接口或方法,目標對象和代理對象都很難維護


使用動態代理就可以很方便的維護代理對象和目標方法。

1.代理對象,不需要實現目標接口
2.代理對象的生成,是利用JDK的API,動態的在內存中構建代理對象(需要我們指定創建代理對象/目標對象實現的接口的類型)
3.動態代理也叫做:JDK代理,接口代理


動態代理

   動態代理是在運行時,通過反射機制實現動態代理,並且能夠代理各種類型的對象。JDK 5中引入的動態代理機制,允許開發人員在運行時刻動態的創建出代理類及其對象。在運行時刻,可以動態創建出一個實現了多個接口的代理類。每個代理類的對象都會關聯一個表示內部處理邏輯的InvocationHandler接 口的實現。當使用者調用了代理對象所代理的接口中的方法的時候,這個調用的信息會被傳遞給InvocationHandler的invoke方法。在 invoke方法的參數中可以獲取到代理對象、方法對應的Method對象和調用的實際參數。invoke方法的返回值被返回給使用者,這種做法實際上相當於對方法調用進行了攔截,這樣我們就可以在調用invoke的前後做自己想做的事。
public class DynamicProxy implements InvocationHandler {

    private  Object targt;

    public    Object  GetInstance(Object target){
        this.targt =target;
        return  Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(proxy.getClass().toString());
        Field [] fields = proxy.getClass().getDeclaredFields();
        for(Field f:fields){
            System.out.println(f.getName());
        }
        Method[] methods = proxy.getClass().getDeclaredMethods();
        for(Method m :methods){
            System.out.println(m.getName());
        }
        System.out.println("before");
        Object result = method.invoke(targt,args);
        System.out.println("after");
        return result;
    }
}

public class Test {

    public static void main(String[] args) {
        Subject proxy = (Subject) new DynamicProxy().GetInstance(new SubjectAgent(new RealSubject()));
        proxy.add();
    }
}

缺點是目標類需要實現接口,原因是JDK在生成代理類時需要傳入接口。
Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);

那麼動態代理是怎麼把代理對象跟目標對象聯繫起來的呢?換句話說,在調用目標對象的相關方法時是怎樣跟代理對象的invoke方法聯繫起來的?

invoke方法中的參數proxy對象看似沒有起到作用,實際上是通過該對象把代理對象和目標對象關聯的。在編譯動態代理類時會生成內部類class $Proxy0 ,也就是此處的invoke方法參數中的proxy類,該類繼承自Proxy類並實現了Subject接口,其部分代碼如下:

 public final void add() {  
        try {  
            super.h.invoke(this, m3, null);  
            return;  
        } catch (Error e) {  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
可以看到此處的目標對象的add()方法跟代理對象關聯起來了,實際調用的是Proxy中InvocationHandler的invoke方法。再看Proxy類中的關鍵代碼
class Proxy{  
    InvocationHandler h=null;  
    protected Proxy(InvocationHandler h) {  
        this.h = h;  
    }  
    ...  
}  

動態代理的不足之處就是目標對象必須實現接口,如果目標對象沒有實現接口,那麼就需要使用Cglib代理


Cglib代理

上面的靜態代理和動態代理模式都是要求目標對象是實現一個接口的目標對象,但是有時候目標對象只是一個單獨的對象,並沒有實現任何的接口,這個時候就可以使用以目標對象子類的方式類實現代理,這種方法就叫做:Cglib代理

Cglib代理,也叫作子類代理,它是在內存中構建一個子類對象從而實現對目標對象功能的擴展.

  • JDK的動態代理有一個限制,就是使用動態代理的對象必須實現一個或多個接口,如果想代理沒有實現接口的類,就可以使用Cglib實現.
  • Cglib是一個強大的高性能的代碼生成包,它可以在運行期擴展java類與實現java接口.它廣泛的被許多AOP的框架使用,例如Spring AOP和synaop,爲他們提供方法的interception(攔截)
  • Cglib包的底層是通過使用一個小而塊的字節碼處理框架ASM來轉換字節碼並生成新的類.不鼓勵直接使用ASM,因爲它要求你必須對JVM內部結構包括class文件的格式和指令集都很熟悉.

Cglib子類代理實現方法:
1.需要引入cglib的jar文件,但是Spring的核心包中已經包括了Cglib功能,所以直接引入pring-core-3.2.5.jar即可.
2.引入功能包後,就可以在內存中動態構建子類
3.代理的類不能爲final,否則報錯
4.目標對象的方法如果爲final/static,那麼就不會被攔截,即不會執行目標對象額外的業務方法


目標對象

public class CglibSubject {
	
	public void add(){
		System.out.println("add");
	}
}


動態代理類

public class CglibProxy implements MethodInterceptor {
	
	private Object target;
	
	public Object getProxyInstance(Object target){
		this.target = target;
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(target.getClass());
		enhancer.setCallback(this);
		return enhancer.create();
		
	}
	
	@Override
	public Object intercept(Object arg0, Method arg1, Object[] arg2,
			MethodProxy arg3) throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("start");
		Object result = arg1.invoke(target, arg2);
		System.out.println("end");
		return result;
	}
}

測試類:

public class Test {
	public static void main(String[] args) {
		new Test().testCglibProxy();
	}
	
	public void testCglibProxy(){
		CglibSubject proxy = (CglibSubject) new CglibProxy().getProxyInstance(new CglibSubject());
		proxy.add();
	}
}

如果目標對象有實現接口,用JDK代理
如果目標對象沒有實現接口,用Cglib代理

@姍姍來遲

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章