cglib動態代理介紹

看JDK動態代理時看到的,瞭解一下還不錯

原文:http://blog.csdn.net/xiaohai0504/article/details/6832990

一、原理

      代理爲控制要訪問的目標對象提供了一種途徑。當訪問對象時,它引入了一個間接的層。JDK自從1.3版本開始,就引入了動態代理,並且經常被用來動態地創建代理。JDK的動態代理用起來非常簡單,當它有一個限制,就是使用動態代理的對象必須實現一個或多個接口。如果想代理沒有實現接口的繼承的類,該怎麼辦?現在我們可以使用CGLIB包。

二、什麼是cglib

     CGLIB是一個強大的高性能的代碼生成包。

      1>它廣泛的被許多AOP的框架使用,例如:Spring AOP和dynaop,爲他們提供方法的interception(攔截);

       2>hibernate使用CGLIB來代理單端single-ended(多對一和一對一)關聯(對集合的延遲抓取,是採用其他機制實現的);

       3>EasyMock和jMock是通過使用模仿(moke)對象來測試java代碼的包。

     它們都通過使用CGLIB來爲那些沒有接口的類創建模仿(moke)對象。

三、底層

      CGLIB包的底層是通過使用一個小而快的字節碼處理框架ASM(Java字節碼操控框架),來轉換字節碼並生成新的類。除了CGLIB包,腳本語言例如 Groovy和BeanShell,也是使用ASM來生成java的字節碼。當不鼓勵直接使用ASM,因爲它要求你必須對JVM內部結構包括class文件的格式和指令集都很熟悉。所以cglib包要依賴於asm包,需要一起導入。下圖爲cglib與一些框架和語言的關係(CGLIB Library and ASM Bytecode Framework)

      Spring AOP和Hibernate同時使用JDK的動態代理和CGLIB包。Spring AOP,如果不強制使用CGLIB包,默認情況是使用JDK的動態代理來代理接口。

四、實例場景模擬

1. 我們創建一個對Table操作的DAO類,提供了CRUD方法。 
    BookServiceBean.java

[java] view plaincopy
  1. package com.tech.cglibx;  
  2. public class BookServiceBean {  
  3.  public void create(){     
  4.         System.out.println("create() is running !");     
  5.     }     
  6.     public void query(){     
  7.         System.out.println("query() is running !");     
  8.     }     
  9.     public void update(){     
  10.         System.out.println("update() is running !");     
  11.     }     
  12.     public void delete(){     
  13.         System.out.println("delete() is running !");     
  14.     }     
  15. }  

    OK,它就是一個javaBean,提供了CRUD方法的javaBean。 
    下面我們創建一個DAO工廠,用來生成DAO實例。

[java] view plaincopy
  1. package com.tech.cglibx;  
  2. public class BookServiceFactory {  
  3.  private static BookServiceBean service = new BookServiceBean();  
  4.  private BookServiceFactory() {  
  5.  }  
  6.  public static BookServiceBean getInstance() {  
  7.   return service;  
  8.  }  

    接下來我們創建客戶端,用來調用CRUD方法。

[java] view plaincopy
  1. public class Client {     
  2.     
  3.     public static void main(String[] args) {     
  4.         BookServiceBean service = BookServiceFactory.getInstance();   
  5.         doMethod(service);     
  6.     }     
  7.     public static void doMethod(BookServiceBean service){     
  8.         service.create();  
  9.         service.update();  
  10.         service.query();  
  11.         service.delete();   
  12.     }     
  13. }   

    OK,完成了,CRUD方法完全被調用了。

    當然這裏並沒有CGlib的任何內容。問題不會這麼簡單的就結束,新的需求來臨了。

 
2. one day,Boss告訴我們這些方法不能開放給用戶,只有“boss”纔有權使用。怎麼辦,難道我們要在每個方法上面進行判斷嗎?好像這麼做也太那啥了吧?對了,Proxy可能是最好的解決辦法。jdk的代理就可以解決了。 好了我們來動手改造吧。等等jdk的代理需要實現接口,這樣, 我們的dao類需要改變了。既然不想改動dao又要使用代理,我們這就請出CGlib。
我們只需新增一個權限驗證的方法攔截器。

[java] view plaincopy
  1. package com.tech.cglibx;  
  2. import java.lang.reflect.Method;  
  3. import net.sf.cglib.proxy.Enhancer;  
  4. import net.sf.cglib.proxy.MethodInterceptor;  
  5. import net.sf.cglib.proxy.MethodProxy;  
  6. import org.apache.log4j.Logger;  
  7. public class MyCglibProxy implements MethodInterceptor{  
  8.  private Logger log=Logger.getLogger(MyCglibProxy.class);  
  9.  public Enhancer enhancer = new Enhancer();  
  10.  private String name;  
  11.    
  12.  public MyCglibProxy(String name) {  
  13.        this.name = name ;  
  14.  }  
  15.  /** 
  16.   * 根據class對象創建該對象的代理對象 
  17.   * 1、設置父類;2、設置回調 
  18.   * 本質:動態創建了一個class對象的子類 
  19.   *  
  20.   * @param cls 
  21.   * @return 
  22.   */  
  23.  public Object getDaoBean(Class cls) {  
  24.   enhancer.setSuperclass(cls);  
  25.   enhancer.setCallback(this);  
  26.   return enhancer.create();  
  27.  }  
  28.    
  29.  @Override  
  30.  public Object intercept(Object object, Method method, Object[] args,  
  31.    MethodProxy methodProxy) throws Throwable {  
  32.   log.info("調用的方法是:" + method.getName());  
  33.   //用戶進行判斷  
  34.   if(!"張三".equals(name)){   
  35.    System.out.println("你沒有權限!");   
  36.    return null;   
  37.   }   
  38.   Object result = methodProxy.invokeSuper(object, args);  
  39.     
  40.   return result;  
  41.  }  
  42. }  

        當然不能忘了對我們的dao工廠進行修改,我們提供一個使用代理的實例生成方法。上面的類中已經提供了一個通用的獲取代理實例的方法,沒有特殊需求(如下3)的方式可以使用上面的方式獲取代理對象。

[java] view plaincopy
  1. public static BookServiceBean getProxyInstance(MyCglibProxy myProxy){    
  2.      Enhancer en = new Enhancer();     
  3.      //進行代理     
  4.      en.setSuperclass(BookServiceBean.class);     
  5.      en.setCallback(myProxy);     
  6.      //生成代理實例     
  7.      return (BookServiceBean)en.create();     
  8.  } <span style="font-family: Arial, Verdana, sans-serif; white-space: normal; "> </span>  

     我們這就可以看看客戶端的實現了。添加了兩個方法用來驗證不同用戶的權限

[java] view plaincopy
  1. BookServiceBean service = BookServiceFactory.getProxyInstance(new MyCglibProxy("boss"));  
  2. service.create();  
  3. BookServiceBean service2 = BookServiceFactory.getProxyInstance(new MyCglibProxy("john"));  
  4. service2.create();  

OK,"boss"的正常執行,"john"的沒有執行。 
看到了嗎?簡單的aop就這樣實現了 
難道就這樣結束了麼? 

3.grd Boss又來訓話了,不行不行,現在除了"boss"其他人都用不了了,現在不可以這樣。必須使用開放查詢功能。 
 哈哈,現在可難不倒我們了,因爲我們使用了CGlib。當然最簡單的方式是去修改我們的方法攔截器,不過這樣會使邏輯變得複雜,且不利於維護。

還好CGlib給我們提供了方法過濾器(CallbackFilter),CallbackFilte可以明確表明,被代理的類中不同的方法,被哪個攔截器所攔截。

下面我們就來做個過濾器用來過濾query方法。 

[java] view plaincopy
  1. package com.tech.cglibx;  
  2. import java.lang.reflect.Method;  
  3. import net.sf.cglib.proxy.CallbackFilter;  
  4. public class MyProxyFilter implements CallbackFilter {  
  5.  @Override  
  6.  public int accept(Method arg0) {     
  7.         if(!"query".equalsIgnoreCase(arg0.getName()))     
  8.             return 0;     
  9.         return 1;     
  10.     }  
  11. }  

我們在工場中新增一個使用了過濾器的實例生成方法。 

[java] view plaincopy
  1. public static BookServiceBean getAuthInstanceByFilter(MyCglibProxy myProxy){    
  2.      Enhancer en = new Enhancer();     
  3.      en.setSuperclass(BookServiceBean.class);     
  4.      en.setCallbacks(new Callback[]{myProxy,NoOp.INSTANCE});     
  5.      en.setCallbackFilter(new MyProxyFilter());     
  6.      return (BookServiceBean)en.create();     
  7.  }  

   setCallbacks中定義了所使用的攔截器,其中NoOp.INSTANCE是CGlib所提供的實際是一個沒有任何操作的攔截器, 
   他們是有序的,一定要和CallbackFilter裏面的順序一致。上面return返回(0/1)的就是返回的順序。也就是說如果調用query方法就使用NoOp.INSTANCE進行攔截。
現在看一下客戶端代碼。

[java] view plaincopy
  1. BookServiceBean service = BookServiceFactory.getProxyInstanceByFilter(new MyCglibProxy("jhon"));  
  2.  service.create();  
  3.  BookServiceBean service2 = BookServiceFactory.getProxyInstanceByFilter(new MyCglibProxy("jhon"));  
  4.  service2.query();  


ok,現在"李四"也可以使用query方法了,其他方法仍然沒有權限。 
當然這個代理的實現沒有任何侵入性,無需強制讓dao去實現接口。 


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