從JDK動態代理看Spring之AOP實現(轉帖)

  Spring 缺省使用J2SE 動態代理(dynamic proxies 來作爲AOP 的代理。這樣任何接口都可以被代理。

Spring 也支持使用CGLIB 代理. 對於需要代理類而不是代理接口的時候CGLIB 代理是很有必要的。 如果一個業務對象並沒有實現一個接口,默認就會使用CGLIB------- 這是Spring Framework 開發手冊中對AOP 的一個簡要概括

  其實個人看來 Spring Framework 是個大雜燴,它提供了許多框架的接口。當然它自己也有一套 MVC 框架。其中最爲常見也最爲重要也就是 IoC AOP 。所謂的 LightWeight Container( 輕量級 ) 就是整個容器的傾入性極低或者沒有傾入性讓對象與對象之間的關係通過配置來體現避免了對象之間的直接調用 ( 當然這不是輕量級容器的完全定義 ), 輕量級帶來的就是單例和工廠的有效減少。嗯 …IoC 是一種思想,它的實現有依賴注入和依賴查找。開發中遇到比較多的就是依賴注入 Spring 所提供的方法有 (Setter 方法注入,構造器注入 以及接口注入 ) 三種方法的使用程度也如我所列的順序一樣,當然各人所好不同。

  AOP 就是面向切面編程,平時我們所面對的都是 OO 那是一個縱向的編程思想,而 AOP 的出現使得面向的切面 ( 即橫向編程 ) 的理念得到了衆多的認可。其實 AOP 的思想早期在 EJB( 個人對 EJB 瞭解不是很夠,這裏就不在細說 ) 中也得以體現,最爲常用的就是聲明式事務的使用。 For Example :比如我聲明這個類中所有以 save 開頭的接口都用事務,所以當其被調用時開啓事務,成功後提交事務,失敗了就回滾事務。那 Spring 中所提供的 AOP EJB 中的所採用的攔截機制有什麼區別呢?對早期的 EJB(3.0 之前 ) 來說,你只有實現了 EJB 纔有該功能而 Spring 則不同, Spring 對普通的 POJO 都可以實現 AOP 。這就是爲什麼 EJB2.x 以失敗而告終,所以當 EJB3.0 捲土重來時它就加入對 Spring 的集成。這也是開源的一大優勢吶。

  一下說的有點多了,吐出來的知識點可能也多了點。還是迴歸主題吧。談談 AOP Spring 中默認是通過 JDK 動態代理來是 AOP 。其實你要是對 JDK 動態代理理解爛熟於心我想我下面的內容你是不用看了。如果你還是不怎麼熟悉,希望大家一起學習。我把自己學習 AOP 的心得寫一寫,以示例爲主便於理解,講的不好還希望大家指點指點 …..

  JDK 動態代理分靜態代理和動態代理,其中靜態代理適用於代理比較少的情形它是一個實實在在的代理類所以當代理比較多的時候你得去編寫許多代理類效率自然就下降了。而動態是在運行時才生產的,當你調用時才生成代理當然它的前提是繼承接口 (invocationHandler) 實現 invoke() 方法。下面我們看個動態代理的例子:(一個很普通的 JAVA PROJECT

               

這個 project 中,我寫了:一個接口: UserManagery

接口的實現類: UserManageryImpl

代理類: SecurityHandler

以及一個簡單的客戶端: Client

先看接口:很普通,就是幾個方法。

          

Java代碼 複製代碼
  1. package com.jummy.spring;   
  2.   
  3. public interface UserManager {   
  4.  public void  addUser(int id,String name);   
  5.  public void delUser(int id);   
  6.  public void modifyUser(int id,String name,int age);   
  7. }       
package com.jummy.spring;

public interface UserManager {
 public void  addUser(int id,String name);
 public void delUser(int id);
 public void modifyUser(int id,String name,int age);
}     

 

接着看實現類:也很普通,各個方法的具體實現。

Java代碼 複製代碼
  1. package com.jummy.spring;   
  2.   
  3. public class UsreManagerImpl implements UserManager {   
  4.   
  5.     public void addUser(int id, String name) {   
  6.         /*  
  7.          * 比如說要在添加之前做一些安全性檢查,當然最原始的做法時在調用方法之前寫一些驗證代碼。 Of  
  8.          * course你可以將驗證專門抽取出來寫成一個方法甚至一個類,然後進行調用。 For example  
  9.          * 該類中抽取出一個scurity()的方法用於驗證,不過你每次驗證都需要如下的調用  
  10.          * 如此來若需要的調用的方法多了,方法甚至類就不再單一了。甚至一眼看不出這到底是一個具體功能模塊  
  11.          * 還是驗證模塊。這樣類就不再便於管理(方法太多)。於是就出現了代理,通過代理類來實現那些不是主要的功能  
  12.          * 這樣模塊的功能就很清晰,同時你在不修改原先類的情況下給該類添加功能實現  
  13.          */  
  14.         // security();   
  15.         System.out.println("---UsreManagerImpl中的addUser方法的實現-----");   
  16.     }   
  17.   
  18.     public void delUser(int id) {   
  19.         System.out.println("-----delUser方法的實現-----");   
  20.     }   
  21.   
  22.     public void modifyUser(int id, String name, int age) {   
  23.         System.out.println("----modifyUser方法的實現-----");   
  24.     }   
  25.   
  26.     public void security() {   
  27.         System.out.println("-----調用security方法-------");   
  28.     }   
  29. }  
package com.jummy.spring;

public class UsreManagerImpl implements UserManager {

	public void addUser(int id, String name) {
		/*
		 * 比如說要在添加之前做一些安全性檢查,當然最原始的做法時在調用方法之前寫一些驗證代碼。 Of
		 * course你可以將驗證專門抽取出來寫成一個方法甚至一個類,然後進行調用。 For example
		 * 該類中抽取出一個scurity()的方法用於驗證,不過你每次驗證都需要如下的調用
		 * 如此來若需要的調用的方法多了,方法甚至類就不再單一了。甚至一眼看不出這到底是一個具體功能模塊
		 * 還是驗證模塊。這樣類就不再便於管理(方法太多)。於是就出現了代理,通過代理類來實現那些不是主要的功能
		 * 這樣模塊的功能就很清晰,同時你在不修改原先類的情況下給該類添加功能實現
		 */
		// security();
		System.out.println("---UsreManagerImpl中的addUser方法的實現-----");
	}

	public void delUser(int id) {
		System.out.println("-----delUser方法的實現-----");
	}

	public void modifyUser(int id, String name, int age) {
		System.out.println("----modifyUser方法的實現-----");
	}

	public void security() {
		System.out.println("-----調用security方法-------");
	}
}
 

只是此時提及一下代理的作用,比如說我在調用 ADD() 方法之前需要進行安全驗證(這是個很常規的步驟)傳統的編碼方式就是將驗證方法直接寫在類中,當然這無可厚非但是當需要調用的方法不斷增加時整個類的就會很模糊。有人說我將需要驗證的方法單獨抽象出來成一個類。但這樣你也要在原來的類中不斷的用實例化這個驗證類,這也存在所需方法不斷增多的情況。這樣我們就考慮 又要調用驗證又要不去破壞(修改)原來類的代碼。所以代理就粉墨登場,通過一個代理類來實現這個功能。

  看一下代理類:這是關鍵,理解這個你就理解了 AOP

Java代碼 複製代碼
  1. package com.jummy.spring;   
  2. /*  
  3.  * 創建一個專門的執行security方法的類。實現InvocationHandler接口  
  4.  *   
  5.  *   
  6.  *   
  7.  *   
  8.  */  
  9. import java.lang.reflect.InvocationHandler;   
  10. import java.lang.reflect.Method;   
  11. import java.lang.reflect.Proxy;   
  12.   
  13. public class SecurityHandler implements InvocationHandler {   
  14.     //適合於所有的對象   
  15.     private Object object;   
  16.        
  17.     //通過構造方法將參數傳遞   
  18.     public Object newProxy(Object object){   
  19.         this.object=object;   
  20.         //生成動態代理 3個參數   
  21.         return Proxy.newProxyInstance(object.getClass().getClassLoader(),   
  22.                                       object.getClass().getInterfaces(),   
  23.                                       this);   
  24.     }   
  25.     public Object invoke(Object arg0, Method method, Object[] arg2)   
  26.             throws Throwable {   
  27.         //代理類在調用任何方法都先調用invoke()方法,這裏的invoke()方法中先執行checkSecurity()方法   
  28.         checkSecurity();   
  29.         //這裏可以查看invoke中調用方法的名稱   
  30.         System.out.println("method name="+method.getName());   
  31.         //該參數是一個數組類型Object[] arg2   
  32.         for(int i=0;i<arg2.length;i++){   
  33.             System.out.println(arg2[i]);   
  34.         }   
  35.         Object result=null;   
  36.         //下面纔是真正調用的方法,將對象添加到invoke 方法中   
  37.         try{   
  38.         result=method.invoke(object, arg2);   
  39.         //方法執行之後,也可以自定義方法   
  40.         }catch(Exception e){   
  41.             e.printStackTrace();   
  42.         }   
  43.         return result;   
  44.     }   
  45.     public void checkSecurity(){   
  46.         System.out.println("-----checkSecurity------");   
  47.     }   
  48.   
  49. }  
package com.jummy.spring;
/*
 * 創建一個專門的執行security方法的類。實現InvocationHandler接口
 * 
 * 
 * 
 * 
 */
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class SecurityHandler implements InvocationHandler {
    //適合於所有的對象
	private Object object;
	
	//通過構造方法將參數傳遞
	public Object newProxy(Object object){
		this.object=object;
		//生成動態代理 3個參數
		return Proxy.newProxyInstance(object.getClass().getClassLoader(),
				                      object.getClass().getInterfaces(),
				                      this);
	}
	public Object invoke(Object arg0, Method method, Object[] arg2)
			throws Throwable {
		//代理類在調用任何方法都先調用invoke()方法,這裏的invoke()方法中先執行checkSecurity()方法
		checkSecurity();
		//這裏可以查看invoke中調用方法的名稱
		System.out.println("method name="+method.getName());
		//該參數是一個數組類型Object[] arg2
		for(int i=0;i<arg2.length;i++){
			System.out.println(arg2[i]);
		}
		Object result=null;
		//下面纔是真正調用的方法,將對象添加到invoke 方法中
		try{
		result=method.invoke(object, arg2);
		//方法執行之後,也可以自定義方法
		}catch(Exception e){
			e.printStackTrace();
		}
		return result;
	}
	public void checkSecurity(){
		System.out.println("-----checkSecurity------");
	}

}
 

  下面我們來細細分析這個類,首先實現接口 invocationHandler ,還有方法 invoke(). 當代理類產生代理之後,在調用所有的方法之前都會先執行 invoke() 方法(想到 AOP 中的 BeforeAdvice 了嗎?)。方法 checkSecurity() 就是我提到的驗證方法,我們寫在代理類中,而你在原先類中去看不到他的具體引用。其中 method.getName() 是獲得所傳入對象所調用的方法的方法名。 ( 假如你只需要對名稱爲 addUser 的方法進行單獨驗證,加個條件判斷不就可以了麼,有點 Spring 的味道了嗎? ) 再看, method.invoke() 這纔是真正調用的方法,在到這一步之前我已經添加了許多驗證方法了,而這在原先類中卻什麼都看不到一切我們都在代理類中實現的。這就避免了對原先類的修改了。當然這個方法調用之後,你還可以繼續添加方法(想到了 AOP afteradvice 通知了嗎?)

  當你所需要的方法不斷增多是你不是可以寫成 xml 文件麼,通過 xml 文件來配置方法。 Spring 大致就是從這個思想演變過來的。

最後看客戶端:

Java代碼 複製代碼
  1. package com.jummy.spring;   
  2.   
  3. public class Client {   
  4.   public static void main(String args[]){   
  5.       SecurityHandler handler=new SecurityHandler();   
  6.       UserManager userManager=(UserManager)handler.newProxy(new UsreManagerImpl());   
  7.       userManager.addUser(19861018"Jummy");   
  8.   }   
  9. }  
package com.jummy.spring;

public class Client {
  public static void main(String args[]){
	  SecurityHandler handler=new SecurityHandler();
	  UserManager userManager=(UserManager)handler.newProxy(new UsreManagerImpl());
	  userManager.addUser(19861018, "Jummy");
  }
}
 

通過代類理的實例來代理原先類( newProxy(new UserManagerImple()) . 然後你再去調用 addUser() 方法。所有的驗證都添加進去了。

  可能講了半天有的朋友還只是說沒有 AOP ,其實要講 AOP 不一定要把 AOP 啃個遍,關鍵是要理解如何實現 AOP 如何區別於 OOP 的縱向編程。當然, OO 起家的我覺得 AOP 始終是個補充,萬事無絕對。

 

             

 

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