CGlib:AOP的另一種實現(xiàn)
1、什么是CGlib
CGlib是一個強大的,高性能,高質(zhì)量的Code生成類庫。它可以在運行期擴(kuò)展Java類與實現(xiàn)Java接口。然這些實際的功能是asm所提供的,asm又是什么?Java字節(jié)碼操控框架,具體是什么大家可以上網(wǎng)查一查,畢竟我們這里所要討論的是cglib,cglib就是封裝了asm,簡化了asm的操作,實現(xiàn)了在運行期動態(tài)生成新的class。可能大家還感覺不到它的強大,現(xiàn)在就告訴你。實際上CGlib為spring aop提供了底層的一種實現(xiàn);為hibernate使用cglib動態(tài)生成VO/PO (接口層對象)。
  它的原理就是用Enhancer生成一個原有類的子類,并且設(shè)置好callback  , 則原有類的每個方法調(diào)用都會轉(zhuǎn)成調(diào)用實現(xiàn)了MethodInterceptor接口的proxy的intercept() 函數(shù):
public  Object intercept(Object o,Method method,Object[] args,MethodProxy  proxy)
  在intercept()函數(shù)里,你可以在執(zhí)行Object  result=proxy.invokeSuper(o,args);來執(zhí)行原有函數(shù),在執(zhí)行前后加入自己的東西,改變它的參數(shù),也可以瞞天過海,完全干別的。說白了,就是AOP中的around  advice。
2、如何使用CGlib
舉個例子:比如DAO層有對表的增、刪、改、查操作,如果要對原有的DAO層的增、刪、改、查增加權(quán)限控制的話,修改代碼是非常痛苦的。所以可以用AOP來實現(xiàn)。但是DAO層沒有使用接口,動態(tài)代理不可用。這時候CGlib是個很好的選擇。
TableDao.java:
- package com.cglib;
 - public class TableDao {
 - public void create(){
 - System.out.println("create() is running...");
 - }
 - public void delete(){
 - System.out.println("delete() is running...");
 - }
 - public void update(){
 - System.out.println("update() is running...");
 - }
 - public void query(){
 - System.out.println("query() is running...");
 - }
 - }
 
實現(xiàn)了MethodInterceptor接口的AuthProxy.java:用來對方法進(jìn)行攔截,增加方法訪問的權(quán)限控制,這里只允許張三訪問。
- package com.cglib;
 - import java.lang.reflect.Method;
 - import net.sf.cglib.proxy.MethodInterceptor;
 - import net.sf.cglib.proxy.MethodProxy;
 - //方法攔截器
 - public class AuthProxy implements MethodInterceptor {
 - private String userName;
 - AuthProxy(String userName){
 - this.userName = userName;
 - }
 - //用來增強原有方法
 - public Object intercept(Object arg0, Method arg1, Object[] arg2,
 - MethodProxy arg3) throws Throwable {
 - //權(quán)限判斷
 - if(!"張三".equals(userName)){
 - System.out.println("你沒有權(quán)限!");
 - return null;
 - }
 - return arg3.invokeSuper(arg0, arg2);
 - }
 - }
 
TableDAOFactory.java:用來創(chuàng)建TableDao的子類的工廠類
- package com.cglib;
 - import net.sf.cglib.proxy.Callback;
 - import net.sf.cglib.proxy.Enhancer;
 - import net.sf.cglib.proxy.NoOp;
 - public class TableDAOFactory {
 - private static TableDao tDao = new TableDao();
 - public static TableDao getInstance(){
 - return tDao;
 - }
 - public static TableDao getAuthInstance(AuthProxy authProxy){
 - Enhancer en = new Enhancer(); //Enhancer用來生成一個原有類的子類
 - //進(jìn)行代理
 - en.setSuperclass(TableDao.class);
 - //設(shè)置織入邏輯
 - en.setCallback(authProxy);
 - //生成代理實例
 - return (TableDao)en.create();
 - }
 - }
 
測試類Client.java:
- package com.cglib;
 - public class Client {
 - public static void main(String[] args) {
 - // haveAuth();
 - haveNoAuth();
 - }
 - public static void doMethod(TableDao dao){
 - dao.create();
 - dao.query();
 - dao.update();
 - dao.delete();
 - }
 - //模擬有權(quán)限
 - public static void haveAuth(){
 - TableDao tDao = TableDAOFactory.getAuthInstance(new AuthProxy("張三"));
 - doMethod(tDao);
 - }
 - //模擬無權(quán)限
 - public static void haveNoAuth(){
 - TableDao tDao = TableDAOFactory.getAuthInstance(new AuthProxy("李四"));
 - doMethod(tDao);
 - }
 - }
 
這樣就能夠?qū)AO層的方法進(jìn)行權(quán)限控制了。但是如果又改需求了,要把DAO層的query方法讓所有用戶都可以訪問,而其他方法照樣有權(quán)限控制,該如何實現(xiàn)呢?這可難不倒我們了,因為我們使用了CGlib。當(dāng)然最簡單的方式是去修改我們的方法攔截器,不過這樣會使邏輯變得復(fù)雜,且不利于維護(hù)。還好CGlib給我們提供了方法過濾器(CallbackFilter),CallbackFilte可以明確表明,被代理的類中不同的方法,被哪個攔截器所攔截。下面我們就來做個過濾器用來過濾query方法。
AuthProxyFilter.java:
- package com.cglib;
 - import java.lang.reflect.Method;
 - import net.sf.cglib.proxy.CallbackFilter;
 - import net.sf.cglib.proxy.NoOp;
 - public class AuthProxyFilter implements CallbackFilter {
 - public int accept(Method arg0) {
 - /*
 - * 如果調(diào)用的不是query方法,則要調(diào)用authProxy攔截器去判斷權(quán)限
 - */
 - if(!"query".equalsIgnoreCase(arg0.getName())){
 - return 0; //調(diào)用第一個方法攔截器,即authProxy
 - }
 - /*
 - * 調(diào)用第二個方法攔截器,即NoOp.INSTANCE,NoOp.INSTANCE是指不做任何事情的攔截器
 - * 在這里就是任何人都有權(quán)限訪問query方法,所以調(diào)用默認(rèn)攔截器不做任何處理
 - */
 - return 1;
 - }
 - }
 
至于為什么返回0或者1,注釋講的很詳細(xì)。
在TableDAOFactory.java里添加如下方法:
- public static TableDao getAuthInstanceByFilter(AuthProxy authProxy){   
 -        Enhancer en = new Enhancer();   
 -        en.setSuperclass(TableDao.class);   
 -         en.setCallbacks(new Callback[]{authProxy,NoOp.INSTANCE});  //設(shè)置兩個方法攔截器 
 -         en.setCallbackFilter(new AuthProxyFilter());   
 -        return (TableDao)en.create();   
 -     }   
 -  
 
這里得注意,en.setCallbacks()方法里的數(shù)組參數(shù)順序就是上面方法的返回值所代表的方法攔截器,如果return 0則使用authProxy攔截器,return 1則使用NoOp.INSTANCE攔截器,NoOp.INSTANCE是默認(rèn)的方法攔截器,不做什么處理。
下面在測試類中添加如下方法:
- //模擬權(quán)限過濾器
 - public static void haveAuthByFilter(){
 - TableDao tDao = TableDAOFactory.getAuthInstanceByFilter(new AuthProxy("張三"));
 - doMethod(tDao);
 - tDao = TableDAOFactory.getAuthInstanceByFilter(new AuthProxy("李四"));
 - doMethod(tDao);
 - }
 
在main方法中調(diào)用該方法,程序運行結(jié)果如下:
create()  is running...
query() is running...
update() is running...
delete() is  running...
你沒有權(quán)限!
query() is  running...
你沒有權(quán)限!
你沒有權(quán)限!
這樣的話,所有用戶都對query方法有訪問權(quán)限了,而其他方法只允許張三訪問。















 
 
 


 
 
 
 