AOP的另一種實現----cglib


  大家都知道,動態代理能夠實現AOP,但是它有一個缺點,就是所有被代理的對象必須實現一個接口,否則就會報異常。那么如果被代理對象沒有實現接口那該如何實現AOP呢?當然是能的,使用CGlib就可以實現。

  1、什么是CGlib

  CGlib是一個強大的,高性能,高質量的Code生成類庫。它可以在運行期擴展Java類與實現Java接口。 然這些實際的功能是asm所提供的,asm又是什么?Java字節碼操控框架,具體是什么大家可以上網查一查,畢竟我們這里所要討論的是cglib, cglib就是封裝了asm,簡化了asm的操作,實現了在運行期動態生成新的class。 可能大家還感覺不到它的強大,現在就告訴你。 實際上CGlib為spring aop提供了底層的一種實現;為hibernate使用cglib動態生成VO/PO (接口層對象)。

  它的原理就是用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。

  2、如何使用CGlib

  舉個例子:比如DAO層有對表的增、刪、改、查操作,如果要對原有的DAO層的增、刪、改、查增加權限控制的話,修改代碼是非常痛苦的。所以可以用AOP來實現。但是DAO層沒有使用接口,動態代理不可用。這時候CGlib是個很好的選擇。

TableDao.java:

 1 package com.cglib;
 2 
 3 public class TableDao {
 4     public void create(){
 5         System.out.println("create() is running...");
 6     }
 7     public void delete(){
 8         System.out.println("delete() is running...");
 9     }
10     public void update(){
11         System.out.println("update() is running...");
12     }
13     public void query(){
14         System.out.println("query() is running...");
15     }
16 }

 

實現了MethodInterceptor接口的AuthProxy.java:用來對方法進行攔截,增加方法訪問的權限控制,這里只允許張三訪問。

 1 package com.cglib;
 2 
 3 import java.lang.reflect.Method;
 4 
 5 import net.sf.cglib.proxy.MethodInterceptor;
 6 import net.sf.cglib.proxy.MethodProxy;
 7 //方法攔截器
 8 public class AuthProxy implements MethodInterceptor {
 9     private String userName;
10     AuthProxy(String userName){
11         this.userName = userName;
12     }
13     //用來增強原有方法
14     public Object intercept(Object arg0, Method arg1, Object[] arg2,
15             MethodProxy arg3) throws Throwable {
16         //權限判斷
17         if(!"張三".equals(userName)){
18             System.out.println("你沒有權限!");
19             return null;
20         }
21         return arg3.invokeSuper(arg0, arg2);
22     }
23 }

TableDAOFactory.java:用來創建TableDao的子類的工廠類

 1 package com.cglib;
 2 
 3 import net.sf.cglib.proxy.Callback;
 4 import net.sf.cglib.proxy.Enhancer;
 5 import net.sf.cglib.proxy.NoOp;
 6 
 7 public class TableDAOFactory {
 8     private static TableDao tDao = new TableDao();  
 9     public static TableDao getInstance(){  
10         return tDao;  
11     }  
12     public static TableDao getAuthInstance(AuthProxy authProxy){  
13         Enhancer en = new Enhancer();  //Enhancer用來生成一個原有類的子類
14         //進行代理  
15         en.setSuperclass(TableDao.class); 
16         //設置織入邏輯
17         en.setCallback(authProxy);  
18         //生成代理實例  
19         return (TableDao)en.create();  
20     } 
21  }

測試類Client.java:

 1 package com.cglib;
 2 
 3 public class Client {
 4 
 5     public static void main(String[] args) {  
 6 //        haveAuth(); 
 7         haveNoAuth();
 8     }  
 9     public static void doMethod(TableDao dao){  
10         dao.create();  
11         dao.query();  
12         dao.update();  
13         dao.delete();  
14     }  
15     //模擬有權限
16     public static void haveAuth(){  
17         TableDao tDao = TableDAOFactory.getAuthInstance(new AuthProxy("張三"));  
18         doMethod(tDao);  
19     }  
20     //模擬無權限
21     public static void haveNoAuth(){  
22         TableDao tDao = TableDAOFactory.getAuthInstance(new AuthProxy("李四"));  
23         doMethod(tDao);  
24     }
25 }

  這樣就能夠對DAO層的方法進行權限控制了。但是如果又改需求了,要把DAO層的query方法讓所有用戶都可以訪問,而其他方法照樣有權限控制,該如何實現呢?這可難不倒我們了,因為我們使用了CGlib。當然最簡單的方式是去修改我們的方法攔截器,不過這樣會使邏輯變得復雜,且不利於維護。還好CGlib給我們提供了方法過濾器(CallbackFilter),CallbackFilte可以明確表明,被代理的類中不同的方法, 被哪個攔截器所攔截。下面我們就來做個過濾器用來過濾query方法。 

AuthProxyFilter.java:

 1 package com.cglib;
 2 
 3 import java.lang.reflect.Method;
 4 
 5 import net.sf.cglib.proxy.CallbackFilter;
 6 import net.sf.cglib.proxy.NoOp;
 7 
 8 public class AuthProxyFilter implements CallbackFilter {
 9 
10     public int accept(Method arg0) {
11         /*
12          * 如果調用的不是query方法,則要調用authProxy攔截器去判斷權限
13          */
14         if(!"query".equalsIgnoreCase(arg0.getName())){
15             return 0; //調用第一個方法攔截器,即authProxy
16         }
17         /*
18          * 調用第二個方法攔截器,即NoOp.INSTANCE,NoOp.INSTANCE是指不做任何事情的攔截器
19          * 在這里就是任何人都有權限訪問query方法,所以調用默認攔截器不做任何處理
20          */
21         return 1;  
22     }
23 
24 }

  至於為什么返回0或者1,注釋講的很詳細。

TableDAOFactory.java里添加如下方法:

1 public static TableDao getAuthInstanceByFilter(AuthProxy authProxy){  
2         Enhancer en = new Enhancer();  
3         en.setSuperclass(TableDao.class);  
4         en.setCallbacks(new Callback[]{authProxy,NoOp.INSTANCE});  //設置兩個方法攔截器
5         en.setCallbackFilter(new AuthProxyFilter());  
6         return (TableDao)en.create();  
7     }  

  這里得注意,en.setCallbacks()方法里的數組參數順序就是上面方法的返回值所代表的方法攔截器,如果return 0則使用authProxy攔截器,return 1則使用NoOp.INSTANCE攔截器,NoOp.INSTANCE是默認的方法攔截器,不做什么處理。

  下面在測試類中添加如下方法:

1 //模擬權限過濾器
2     public static void haveAuthByFilter(){  
3         TableDao tDao = TableDAOFactory.getAuthInstanceByFilter(new AuthProxy("張三"));  
4         doMethod(tDao);  
5         tDao = TableDAOFactory.getAuthInstanceByFilter(new AuthProxy("李四"));  
6         doMethod(tDao);  
7     }  

在main方法中調用該方法,程序運行結果如下:

create() is running...
query() is running...
update() is running...
delete() is running...
你沒有權限!
query() is running...
你沒有權限!
你沒有權限!

  這樣的話,所有用戶都對query方法有訪問權限了,而其他方法只允許張三訪問。

參考資料:http://llying.iteye.com/blog/220452

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM