JAVA反射機制的應用(2)

JAVA反射機制的應用(2)

Spring中的AOP(面向切面的編程)

在MVC中,業務層對象扮演了相當重要的作用,它的方法代表了核心業務邏輯,但是可能還有一些附加的操作,比如寫日誌等其它操作也會包含在其中,那麼可能會帶來幾個問題:

  • 冗餘代碼的出現
  • 破壞了面向對象的思想。各個方法應該各司其職,只做它應該做的那一部分工作,而這裏這些輔助操作的引入會破壞這個特性。

這裏我們把這些附加操作(比如寫日誌操作)看成一個切面,並且把它從業務方法中抽離出來,然後把它植入到各個業務方法中,這樣維護這些輔助操作是相當容易了,而且也不會出現冗餘代碼。那麼這種思想怎麼實現呢?

這裏把上面提到的業務對象稱爲目標對象,再引入一個委託對象(下面會講到兩種委託對象),我們不直接對目標對象進行操作,而是對委託對象進行方法調用,在委託對象的方法中可以完成切面操作,接着再把方法調用轉交到相應的目標對象上。

 

AOP的兩種實現方式:

(1)CgLib 運行時代碼增強工具,它是一個第三方的工具,運行時使用cglib可以 動態地給一個類生成一個子類 ,並使用子類對象作爲父類對象的委託。 CGLIB包對代理那些沒有實現接口的類非常有用。 它是通過動態的生成一個子類去覆蓋所要代理類的不是final的方法,並設置好callback ,setCallback的參數是一個MethodInterceptor接口的實現類實例,則原有類的每個方法調用就會轉變成調用用戶在 MethodInterceptor 自定義的攔截方法(intercept)。 在這個攔截方法中,我們可以在intercept方法中首先處理我們要輔助的邏輯,完了之後再使用 proxy.invokeSuper(o, os)來調用父類中的相應 方法來處理

步驟:首先需要一個cglib的jar包,並配置到classPath中,cglib有一個核心接口:MethodInterceptor接口,我們需要創建類來實現這個接口,並實現其中的方法:

public Object intercept(Object o, Method m, Object[] os, MethodProxy proxy)

該方法中我們可以定義一些特殊的邏輯,比如寫日誌等操作,這些操作完了之後再調用父類中的相關方法。

除此之外,在該類中還要創建一個Enhancer對象,參考以下內容

用Enhancer生成一個原有類的子類,並且設置好callback , 則 原有類的每個方法調用都會轉成調用實現了MethodInterceptor接口的proxy的intercept() 函數       public Object intercept(Object o,Method method,Object[] args,MethodProxy proxy)  

     在intercept()函數裏,你可以執行Object result=proxy.invokeSuper(o,args)來執行原有函數,在執行前後加入自己的東西,改變它的參數,也可以瞞天過海,完全乾別 的。說白了,就是AOP中的around advice。

public class TestCglibProxy {
	public static void main(String[] args) {
		AProxy ap=new AProxy();
		A a=(A)ap.getProxy(A.class);   //利用cglib創建A類的子類對象(代理對象)
  		a.method1();   //調用父對象的method1方法時會會調用代理對象的intercept方法
                a.method2();
	}
}

class A{
	public void method1(){
		System.out.println("A method1");
	}
	public void method2(){
		System.out.println("A method2");
	}
}

class AProxy implements MethodInterceptor{
	private Logger log=Logger.getLogger("A");
	private Enhancer en=new Enhancer();
	public Object getProxy(Class c){   //c是所要代理的父類的Class對象
                en.setCallback(this);  //表示調用父對象的方法時,回調當前對象的intercept方法
    		en.setSuperclass(c);  //設置父類對象
 		return en.create();    //創建一個代理類的實例
 }
	public Object intercept(Object o, Method m, Object[] os, MethodProxy proxy) throws Throwable {
		log.info("invoke method "+m.getName());
		Object result=proxy.invokeSuper(o, os);
		return result;
	}
} 


(2)動態代理方式

目標類T和代理類P都實現同樣一個接口I 其中 P類對象中包含了一個T類的對象 ,在P和T實現接口I的同一個方法M時,可以在P中的M中調用T的M 方法 同時在調用T的M之前或者之後進行一些自定義的操作。

一個簡單的代理模式實現

Interface I{

   void method1();

}

class T implements I{

  public void method() {

   ...

}

}

class P implements I {

private T t;

public P(T t) {

this.t = t;

}

public void methid() {

//do something

t.method();

//do something

}

}

上面的代碼相信大家都能看懂 ,這就是一個代理模式的簡單實現。

下面來介紹一下JAVA中已有的動態代理框架,三個關鍵類:Proxy,InvocationHandler,Method對象,首先我們需要創建目標類動態代理對象,怎麼創建呢?調用:

 

newProxyInstance ( ClassLoader loader, Class <?>[] interfaces, InvocationHandler h)

前面講過了代理類和目標類實現共同的接口,因此需要將目標類所實現的接口(這裏是一個Class數組)傳給Proxy,除此之外還需要傳 遞目標類的類加載器和InvocationHandler實例,類加載器當然是用來加載生成的代理類的class文件,而這裏的 InvocationHandler是什麼呢?顧名思義,調用處理器,當對代理類調用相應的方法時(這裏的方法其實是我們想對目標類調用的),該方法調用 會被攔截,並回調InvocationHandler實現類的invoke方法,我們來簡單看看invoke方法:

 

invoke ( Object  proxy, Method  method, Object [] args)

invoke方法中又包含了三個參數,第一個是代理對象proxy,第二個是Method對象,第三個是一個Object數組,這裏簡單介紹下這三個參 數:proxy對象大家應該都知道了,即代理對象 上面利用Proxy的靜態newProxyInstance方法創建的 ;method對象,即方法對象,這裏的方法對象是什麼呢?剛纔說到了對代理對象調用方法時會被攔截,而這裏的method就是被攔截的方法對象 最後一個是args,相信大家對這個變量不會陌生,這裏表示的是對代理對象調用方法時所傳遞的參數,參數可能爲一個,兩個或者多個,當然也可能沒有,因此這裏用一個數組表示。

肯定有人會問我在這個方法裏寫什麼呢?愛寫什麼你寫什麼,呵呵,開個玩笑。 按照常理來說,這裏面肯定會調用目標對象的method方法了,不然這代理還有什麼意義!!!那怎麼調用呢?相信有java反射基礎的人大家應該都知道吧,不知道或者不熟練就查查API:

 

invoke ( Object obj, Object ... args)

第 一個對象是目標對象,第二個是參數數組,這裏又涉及問題,怎麼把目標對象傳遞到這兒呢?我這裏介紹兩種方法,其實大家應該都能想到,在定義 InvocationHandler實現類時,把目標類作爲它的成員變量,通過構造函數把目標對象賦值給它,那麼在invoke方法中當然就可以直接使用 了;另外一種更簡單的方法,InvocationHandler的實例使用匿名內部類創建:

new InvocationHandler() {

    public  Object invoke(Object proxy, Method method, Object[] args)  {

       ...

      method,invoke(target, args);

      ...

    }

}

這裏的target可以來自方法參數,但一定要是final的。

public class TestProxy {
	public static void main(String[] args){
		/*
		I target=new Target();
		I proxy=(I)ProxyFactory.getProxy(target);
		proxy.method();
		*/
		List list=new ArrayList();
		List listProxy=(List)ProxyFactory.getProxy(list);
		listProxy.add("abc");
		listProxy.add("def");
		listProxy.add(0,"123");
		listProxy.remove(1);
		listProxy.size();
		for(Object o:list){
			System.out.println(o);
		}
	}
}
interface I{
	void method();
}
class Target implements I{
	public void method(){
		System.out.println("Target method");
	}
}
class TargetProxy implements I{
	I target;
	public TargetProxy(I target){
		this.target=target;
	}
	public void method(){
		System.out.println("do sth in proxy");
		target.method();
	}
}
class ProxyFactory{
	public static Object getProxy(final Object target){
		Class c=target.getClass();
		ClassLoader loader=c.getClassLoader();
		Class[] is=c.getInterfaces();
		return Proxy.newProxyInstance(loader,is,new InvocationHandler(){
			public Object invoke(Object proxy, Method m, Object[] os) throws Throwable {
				System.out.println("invoke "+m.getName());
				return m.invoke(target, os);
			}
		});
	}
}
 

以上內容部分來自:http://nucchenyibin.iteye.com/blog/667733

                           http://www.zx2010.com/program/java-cglib-proxy.asp

 

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