Java動態代理與Cglib庫

JDK動態代理

 

  代理模式是經常使用的Java設計模式,他的特徵是代理類與委託類有一樣的接口,代理類主要負責爲委託類預處理消息、過濾消息、把消息轉發給委託類,以及過後處理消息等。代理類與委託類之間一般會存在關聯關係,一個代理類的對象與一個委託類的對象關聯,代理類的對象自己並不真正實現服務,而是經過調用委託類的對象的相關方法,來提供特定的服務。 
  按照代理的建立時期,代理類能夠分爲兩種。 
  靜態代理:由程序員建立或特定工具自動生成源代碼,再對其編譯。在程序運行前,代理類的.class文件就已經存在了。 
  動態代理:在程序運行時,運用反射機制動態建立而成。 java

  爲何使用動態代理?由於動態代理能夠對請求進行任何處理。
  哪些地方須要動態代理?不容許直接訪問某些類;對訪問要作特殊處理等。
  目前Java開發包中包含了對動態代理的支持,可是其實現只支持對接口的的實現。 其實現主要經過java.lang.reflect.Proxy類和java.lang.reflect.InvocationHandler接口。 Proxy類主要用來獲取動態代理對象,InvocationHandler接口用來約束調用者實現。git

  如下爲模擬案例,經過動態代理實如今方法調用先後向控制檯輸出兩句字符串。程序員

  定義一個HelloWorld接口:github

 

[java] view plain copyspring

  1. package com.ljq.test;  
  2.   
  3.  /** 
  4.  * 定義一個HelloWorld接口 
  5.  *  
  6.  * @author jiqinlin 
  7.  * 
  8.  */  
  9.  public interface HelloWorld {  
  10.     public void sayHelloWorld();  
  11. }  

 

  類HelloWorldImpl是HelloWorld接口的實現:apache

 

[java] view plain copy設計模式

  1. package com.ljq.test;  
  2.   
  3.  /** 
  4.  * 類HelloWorldImpl是HelloWorld接口的實現 
  5.  *  
  6.  * @author jiqinlin 
  7.  * 
  8.  */  
  9.  public class HelloWorldImpl implements HelloWorld{  
  10.   
  11.     public void sayHelloWorld() {  
  12.         System.out.println("HelloWorld!");  
  13.     }  
  14.   
  15. }  

  HelloWorldHandler是 InvocationHandler接口實現:數組

[java] view plain copyapp

  1. package com.ljq.test;  
  2.   
  3.  import java.lang.reflect.InvocationHandler;  
  4.  import java.lang.reflect.Method;  
  5.   
  6.  /** 
  7.  * 實如今方法調用先後向控制檯輸出兩句字符串 
  8.  *  
  9.  * @author jiqinlin 
  10.  * 
  11.  */  
  12.  public class HelloWorldHandler implements InvocationHandler{  
  13.     //要代理的原始對象  
  14.      private Object obj;  
  15.       
  16.     public HelloWorldHandler(Object obj) {  
  17.         super();  
  18.         this.obj = obj;  
  19.     }  
  20.   
  21.     /** 
  22.      * 在代理實例上處理方法調用並返回結果 
  23.      *  
  24.      * @param proxy 代理類 
  25.      * @param method 被代理的方法 
  26.      * @param args 該方法的參數數組 
  27.      */  
  28.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
  29.         Object result = null;  
  30.         //調用以前  
  31.          doBefore();  
  32.         //調用原始對象的方法  
  33.         result=method.invoke(obj, args);  
  34.         //調用以後  
  35.         doAfter();  
  36.         return result;  
  37.     }  
  38.       
  39.     private void doBefore(){  
  40.         System.out.println("before method invoke");  
  41.     }  
  42.       
  43.     private void doAfter(){  
  44.         System.out.println("after method invoke");  
  45.     }  
  46.       
  47. }  

  測試類:框架

[java] view plain copy

  1. package com.ljq.test;  
  2.   
  3. import java.lang.reflect.InvocationHandler;  
  4. import java.lang.reflect.Proxy;  
  5.   
  6.   
  7. public class HelloWorldTest {  
  8.   
  9.     public static void main(String[] args) {  
  10.         HelloWorld helloWorld=new HelloWorldImpl();  
  11.         InvocationHandler handler=new HelloWorldHandler(helloWorld);  
  12.   
  13.         //建立動態代理對象  
  14.         HelloWorld proxy=(HelloWorld)Proxy.newProxyInstance(  
  15.             helloWorld.getClass().getClassLoader(),  
  16.             helloWorld.getClass().getInterfaces(),  
  17.             handler);  
  18.         proxy.sayHelloWorld();  
  19.     }  
  20. }  

  運行結果爲:

[plain] view plain copy

  1. before method invoke  
  2. HelloWorld!  
  3. after method invoke  

 

  基本流程:用Proxy類建立目標類的動態代理,建立時須要指定一個本身實現InvocationHandler接口的回調類的對象,這個回調類中有一個invoke()用於攔截對目標類各個方法的調用。建立好代理後就能夠直接在代理上調用目標對象的各個方法。

  JDK自從1.3版本開始,就引入了動態代理,而且常常被用來動態地建立代理。JDK的動態代理用起來很是簡單,但它有一個限制,就是使用動態代理的對象必須實現一個或多個接口。好比上面的HelloWorldImpl類,實現了HelloWorld接口,因此能夠用JDK的動態代理。若是想代理沒有實現接口的繼承的類,該怎麼辦? CGLIB就是最好的選擇(https://github.com/cglib/cglib,使用apache license 2.0)。其餘比較有名的還有從JBoss項目衍生出來的Javassist(https://github.com/jboss-javassist/javassist),這裏介紹Cglib。

 

Cglib代碼生成庫

 

  CGlib是一個強大的,高性能,高質量的Code生成類庫。它能夠在運行期擴展Java類與實現Java接口。其底層是經過小而快的字節碼處理框架ASM(http://forge.ow2.org/projects/asm,使用BSD License)來轉換字節碼並生成新的類。大部分功能其實是asm所提供的,CGlib只是封裝了asm,簡化了asm的操做,實現了在運行期動態生成新的class。

  CGlib被許多AOP的框架使用,例如spring AOP和dynaop,爲他們提供方法的interception(攔截);最流行的OR Mapping工具hibernate也使用CGLIB來代理單端single-ended(多對一和一對一)關聯(對集合的延遲抓取,是採用其餘機制實現的);EasyMock和jMock是經過使用模仿(moke)對象來測試java代碼的包,它們都經過使用CGLIB來爲那些沒有接口的類建立模仿(moke)對象。

  CGLIB包的基本代碼不多,但學起來有必定的困難,主要是缺乏文檔,API描述過於簡單,這也是開源軟件的一個不足之處。目前CGLIB的版本是cglib-2.2.jar,主要由一下部分組成:
  (1)net.sf.cglib.core:底層字節碼處理類,他們大部分與ASM有關係。
  (2)net.sf.cglib.transform:編譯期或運行期類和類文件的轉換。
  (3)net.sf.cglib.proxy :實現建立代理和方法攔截器的類。
  (4)net.sf.cglib.reflect :實現快速反射和C#風格代理的類。
  (5)net.sf.cglib.util:集合排序工具類。
  (6)net.sf.cglib.beans:JavaBean相關的工具類。

  CGLIB包是在ASM之上的一個高級別的層。對代理那些沒有實現接口的類很是有用。本質上,它是經過動態的生成一個子類去覆蓋所要代理類的不是final的方法,並設置好callback,則原有類的每一個方法調用就會轉變成調用用戶定義的攔截方法(interceptors),這比JDK動態代理方法快多了。可見,Cglib的原理是對指定的目標類動態生成一個子類,並覆蓋其中方法實現加強,但由於採用的是繼承,因此不能對final修飾的類和final方法進行代理。

 

用Cglib建立動態代理

 

  下圖表示Cglib經常使用到的幾類。

圖1 Cglib主要的接口

 

  建立一個具體類的代理時,一般要用到的CGLIB包的APIs:

  net.sf.cglib.proxy.Callback接口:在CGLIB包中是一個很關鍵的接口,全部被net.sf.cglib.proxy.Enhancer類調用的回調(callback)接口都要繼承這個接口。

  net.sf.cglib.proxy.MethodInterceptor接口:是最通用的回調(callback)類型,它常常被AOP用來實現攔截(intercept)方法的調用。這個接口只定義了一個方法。

 

[java] view plain copy

  1. public Object intercept(Object object, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable;  

  當net.sf.cglib.proxy.MethodInterceptor作爲全部代理方法的回調 (callback)時,當對基於代理的方法調用時,在調用原對象的方法的以前會調用這個方法,如圖下圖所示。第一個參數是代理對像,第二和第三個參數分別 是攔截的方法和方法的參數。原來的方法可能經過使用java.lang.reflect.Method對象的通常反射調用,或者使用 net.sf.cglib.proxy.MethodProxy對象調用。net.sf.cglib.proxy.MethodProxy一般被首選使用,由於它更快。在這個方法中,咱們能夠在調用原方法以前或以後注入本身的代碼。

 

圖1

  net.sf.cglib.proxy.MethodInterceptor可以知足任何的攔截(interception )須要,當對有些狀況下可能過分。爲了簡化和提升性能,CGLIB包提供了一些專門的回調(callback)類型。例如:

  net.sf.cglib.proxy.FixedValue:爲提升性能,FixedValue回調對強制某一特別方法返回固定值是有用的。

  net.sf.cglib.proxy.NoOp:NoOp回調把對方法調用直接委派到這個方法在父類中的實現。

  net.sf.cglib.proxy.LazyLoader:當實際的對象須要延遲裝載時,可使用LazyLoader回調。一旦實際對象被裝載,它將被每個調用代理對象的方法使用。

  net.sf.cglib.proxy.Dispatcher:Dispathcer回調和LazyLoader回調有相同的特色,不一樣的是,當代理方法被調用時,裝載對象的方法也總要被調用。

   net.sf.cglib.proxy.ProxyRefDispatcher:ProxyRefDispatcher回調和Dispatcher同樣,不一樣的是,它能夠把代理對象做爲裝載對象方法的一個參數傳遞。

  代理類的因此方法常常會用到回調(callback),固然你也可使用net.sf.cglib.proxy.CallbackFilter 有選擇的對一些方法使用回調(callback),這種考慮周詳的控制特性在JDK的動態代理中是沒有的。在JDK代理中,對 java.lang.reflect.InvocationHandler方法的調用對代理類的全部方法都有效。

  CGLIB的代理包也對net.sf.cglib.proxy.Mixin提供支持。基本上,它容許多個對象被綁定到一個單一的大對象。在代理中對方法的調用委託到下面相應的對象中。

  接下來咱們看看如何使 用CGLIB代理APIs建立代理。

  一、建立一個簡單的代理

  CGLIB代理最核心類net.sf.cglib.proxy.Enhancer, 爲了建立一個代理,最起碼你要用到這個類。首先,讓咱們使用NoOp回調建立一個代理。

[java] view plain copy

  1. /**  
  2.  
  3. * Create a proxy using NoOp callback. The target class  
  4. * must have a default zero-argument constructor 
  5. *  
  6. * @param targetClass the super class of the proxy  
  7. * @return a new proxy for a target class instance  
  8. */   
  9. public Object createProxy(Class targetClass) {   
  10.     Enhancer enhancer = new Enhancer();  
  11.     enhancer.setSuperclass(targetClass);  
  12.     enhancer.setCallback(NoOp.INSTANCE);  
  13.     return enhancer.create();  
  14. }   

  返回值是target類一個實例的代理。在這個例子中,咱們爲net.sf.cglib.proxy.Enhancer 配置了一個單一的回調(callback)。咱們能夠看到不多直接建立一個簡單的代理,而是建立一個net.sf.cglib.proxy.Enhancer的實例,在net.sf.cglib.proxy.Enhancer類中你可以使用靜態幫助方法建立一個簡單的代理。通常推薦使用上面例子的方法建立代理,由於它容許你經過配置net.sf.cglib.proxy.Enhancer實例很好的控制代理的建立。

  要注意的是,target類是做爲產生的代理的父類傳進來的。不一樣於JDK的動態代理,它不能在建立代理時傳target對象,target對象必須被CGLIB包來建立。在這個例子中,默認的無參數構造器時用來建立target實例的。若是你想用CGLIB來建立有參數的實例,用net.sf.cglib.proxy.Enhancer.create(Class[], Object[])方法替代net.sf.cglib.proxy.Enhancer.create()就能夠了。方法中第一個參數定義了參數的類型,第 二個是參數的值。在參數中,基本類型應被轉化成類的類型。

  二、使用MethodInterceptor建立一個代理

  爲了更好的使用代理,咱們可使用本身定義的MethodInterceptor類型回調(callback)來代替net.sf.cglib.proxy.NoOp回調。當對代理中全部方法的調用時,都會轉向MethodInterceptor類型的攔截(intercept)方法,在攔截方法中再調用底層對象相應的方法。下面咱們舉個例子,假設你想對目標對象的全部方法調用進行權限的檢查,若是沒有通過受權,就拋出一個運行時的異常AuthorizationException。其中AuthorizationService.java接口的代碼以下:

[java] view plain copy

  1. package com.lizjason.cglibproxy;   
  2.   
  3. import java.lang.reflect.Method;   
  4.   
  5. /**  
  6.  * A simple authorization service for illustration purpose.  
  7.  * @author Jason Zhicheng Li (jason@lizjason.com)  
  8.  */   
  9. public interface AuthorizationService {   
  10.     void authorize(Method method);   
  11. }  

  對net.sf.cglib.proxy.MethodInterceptor接口的實現的類AuthorizationInterceptor.java代碼以下:

[java] view plain copy

  1. package com.lizjason.cglibproxy.impl;  
  2. import java.lang.reflect.Method;  
  3. import net.sf.cglib.proxy.MethodInterceptor;  
  4. import net.sf.cglib.proxy.MethodProxy;  
  5.   
  6. import com.lizjason.cglibproxy.AuthorizationService;  
  7.   
  8. /** 
  9.  * A simple MethodInterceptor implementation to 
  10.  * apply authorization checks for proxy method calls. 
  11.  */  
  12. public class AuthorizationInterceptor implements MethodInterceptor {  
  13.   
  14.     private AuthorizationService authorizationService;  
  15.   
  16.     /** 
  17.      * Create a AuthorizationInterceptor with the given AuthorizationService 
  18.      */  
  19.     public AuthorizationInterceptor (AuthorizationService authorizationService) {  
  20.         this.authorizationService = authorizationService;  
  21.     }  
  22.   
  23.     /** 
  24.      * Intercept the proxy method invocations to inject authorization check. * The original 
  25.      * method is invoked through MethodProxy. 
  26.      */  
  27.     public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {  
  28.         if (authorizationService != null) {  
  29.             //may throw an AuthorizationException if authorization failed  
  30.             authorizationService.authorize(method);  
  31.         }  
  32.         return methodProxy.invokeSuper(object, args);  
  33.     }  
  34. }  

  咱們能夠看到在攔截方法中,首先進行權限的檢查,若是經過權限的檢查,攔截方法再調用目標對象的原始方法。因爲性能的緣由,對原始方法的調用咱們使用CGLIB的net.sf.cglib.proxy.MethodProxy對象,而不是反射中通常使用java.lang.reflect.Method對象。

  下面是一個完整的使用MethodInterceptor的例子。

 

[java] view plain copy

  1. package cglibexample;  
  2.   
  3. import java.lang.reflect.Method;  
  4. import net.sf.cglib.proxy.Enhancer;  
  5. import net.sf.cglib.proxy.MethodInterceptor;  
  6. import net.sf.cglib.proxy.MethodProxy;  
  7.   
  8. /** 
  9.  * 定義一個HelloWorld類,沒有實現接口 
  10.  * 
  11.  */  
  12. class HelloWorld {  
  13.   
  14.     public void sayHelloWorld() {  
  15.         System.out.println("HelloWorld!");  
  16.     }  
  17. }  
  18.   
  19. /** 
  20.  * 經過Cglib實如今方法調用先後向控制檯輸出兩句字符串 
  21.  * 
  22.  */  
  23. class CglibProxy implements MethodInterceptor {  
  24.   
  25.     //要代理的原始對象  
  26.     private Object obj;  
  27.   
  28.     public Object createProxy(Object target) {  
  29.         this.obj = target;  
  30.         Enhancer enhancer = new Enhancer();  
  31.         // 設置要代理的目標類,以擴展它的功能  
  32.         enhancer.setSuperclass(this.obj.getClass());  
  33.         // 設置單一回調對象,在回調中攔截對目標方法的調用  
  34.         enhancer.setCallback(this);  
  35.         //設置類裝載器  
  36.         enhancer.setClassLoader(target.getClass().getClassLoader());  
  37.         //建立代理對象  
  38.         return enhancer.create();  
  39.     }  
  40.   
  41.     /** 
  42.      * 回調方法:在代理實例上攔截並處理目標方法的調用,返回結果 
  43.      * 
  44.      * @param proxy 代理類 
  45.      * @param method 被代理的方法 
  46.      * @param params 該方法的參數數組 
  47.      * @param methodProxy 
  48.      */  
  49.     @Override  
  50.     public Object intercept(Object proxy, Method method, Object[] params,  
  51.             MethodProxy methodProxy) throws Throwable {  
  52.         Object result = null;  
  53.         // 調用以前  
  54.         doBefore();  
  55.         // 調用目標方法,用methodProxy,  
  56.         // 而不是原始的method,以提升性能  
  57.         result = methodProxy.invokeSuper(proxy, params);  
  58.         // 調用以後  
  59.         doAfter();  
  60.         return result;  
  61.     }  
  62.   
  63.     private void doBefore() {  
  64.         System.out.println("before method invoke");  
  65.     }  
  66.   
  67.     private void doAfter() {  
  68.         System.out.println("after method invoke");  
  69.     }  
  70. }  
  71.   
  72. public class TestCglib {  
  73.   
  74.     public static void main(String[] args) {  
  75.         CglibProxy cglibProxy = new CglibProxy();  
  76.         HelloWorld hw = (HelloWorld) cglibProxy.createProxy(new HelloWorld());  
  77.         hw.sayHelloWorld();  
  78.     }  
  79. }  

  輸出結果:

 

 

[plain] view plain copy

  1. before method invoke  
  2. HelloWorld!  
  3. after method invoke  

  基本流程:須要本身寫代理類,它實現MethodInterceptor接口,有一個intercept()回調方法用於攔截對目標方法的調用,裏面使用methodProxy來調用目標方法。建立代理對象要用Enhance類,用它設置好代理的目標類、有intercept()回調的代理類實例、最後用create()建立並返回代理實例。

 

  三、使用CallbackFilter在方法層設置回調

  net.sf.cglib.proxy.CallbackFilter容許咱們在方法層設置回調(callback)。假如你有一個PersistenceServiceImpl類,它有兩個方法:save和load,其中方法save須要權限檢查,而方法load不須要權限檢查。

[java] view plain copy

  1. import com.lizjason.cglibproxy.PersistenceService;  
  2. import java.lang.reflect.Method;  
  3. import net.sf.cglib.proxy.CallbackFilter;  
  4.   
  5. /** 
  6.  * A simple implementation of PersistenceService interface 
  7.  */  
  8. class PersistenceServiceImpl implements PersistenceService {  
  9.   
  10.     //須要權限檢查  
  11.     public void save(long id, String data) {  
  12.         System.out.println(data + " has been saved successfully.");  
  13.     }  
  14.   
  15.     //不須要權限檢查  
  16.     public String load(long id) {  
  17.         return "Test CGLIB CallBackFilter";  
  18.     }  
  19. }  
  20.   
  21. /** 
  22.  * An implementation of CallbackFilter for PersistenceServiceImpl 
  23.  */  
  24. public class PersistenceServiceCallbackFilter implements CallbackFilter {   
  25.     //callback index for save method  
  26.     private static final int SAVE = 0;  
  27.     //callback index for load method  
  28.     private static final int LOAD = 1;  
  29.   
  30.     /** 
  31.      * Specify which callback to use for the method being invoked.  
  32.      * @param method the method being invoked. 
  33.      * @return  
  34.      */  
  35.     @Override  
  36.     public int accept(Method method) {  
  37.         //指定各方法的代理回調索引  
  38.         String name = method.getName();  
  39.         if ("save".equals(name)) {  
  40.             return SAVE;  
  41.         }  
  42.         // for other methods, including the load method, use the  
  43.         // second callback  
  44.         return LOAD;  
  45.     }  
  46. }  

  accept方法中對代理方法和回調進行了匹配,返回的值是某方法在回調數組中的索引。下面是PersistenceServiceImpl類代理的實現。

[java] view plain copy

  1. ...  
  2. Enhancer enhancer = new Enhancer();  
  3. enhancer.setSuperclass(PersistenceServiceImpl.class);  
  4. //設置回調過濾器  
  5. CallbackFilter callbackFilter = new PersistenceServiceCallbackFilter();  
  6. enhancer.setCallbackFilter(callbackFilter);  
  7. //建立各個目標方法的代理回調  
  8. AuthorizationService authorizationService = ...  
  9. Callback saveCallback = new AuthorizationInterceptor(authorizationService);  
  10. Callback loadCallback = NoOp.INSTANCE;  
  11. //順序要與指定的回調索引一致  
  12. Callback[] callbacks = new Callback[]{saveCallback, loadCallback };  
  13. enhancer.setCallbacks(callbacks);  //設置回調  
  14. ...  
  15. return (PersistenceServiceImpl)enhancer.create();  //建立代理對象  

  在這個例子中save方法使用了AuthorizationInterceptor實例,load方法使用了NoOp實例。此外,你也能夠經過net.sf.cglib.proxy.Enhancer.setInterfaces(Class[])方法指定代理對象所實現的接口。

  除了爲net.sf.cglib.proxy.Enhancer指定回調數組,你還能夠經過net.sf.cglib.proxy.Enhancer.setCallbackTypes(Class[]) 方法指定回調類型數組。當建立代理時,若是你沒有回調實例的數組,就可使用回調類型。象使用回調同樣,你必須使用net.sf.cglib.proxy.CallbackFilter爲每個方法指定一個回調類型索引。

  四、使用Mixin

  Mixin經過代理方式將多種類型的對象綁定到一個大對象上,這樣對各個目標類型中的方法調用能夠直接在這個大對象上進行。下面是一個例子。
 

[java] view plain copy

  1. import net.sf.cglib.proxy.Mixin;  
  2.   
  3. interface MyInterfaceA {  
  4.   
  5.     public void methodA();  
  6. }  
  7.   
  8. interface MyInterfaceB {  
  9.   
  10.     public void methodB();  
  11. }  
  12.   
  13. class MyInterfaceAImpl implements MyInterfaceA {  
  14.   
  15.     @Override  
  16.     public void methodA() {  
  17.         System.out.println("MyInterfaceAImpl.methodA()");  
  18.     }  
  19. }  
  20.   
  21. class MyInterfaceBImpl implements MyInterfaceB {  
  22.   
  23.     @Override  
  24.     public void methodB() {  
  25.         System.out.println("MyInterfaceBImpl.methodB()");  
  26.     }  
  27. }  
  28.   
  29. public class Main {  
  30.   
  31.     public static void main(String[] args) {  
  32.         //各個對象對應的類型  
  33.         Class[] interfaces = new Class[]{MyInterfaceA.class, MyInterfaceB.class};  
  34.         //各個對象  
  35.         Object[] delegates = new Object[]{new MyInterfaceAImpl(), new MyInterfaceBImpl()};  
  36.         //將多個對象綁定到一個大對象上  
  37.         Object obj = Mixin.create(interfaces, delegates);  
  38.         //直接在大對象上調用各個目標方法  
  39.         ((MyInterfaceA)obj).methodA();  
  40.         ((MyInterfaceB)obj).methodB();  
  41.     }  
  42. }  

 

動態生成Bean

 

  咱們知道,Java Bean包含一組屬性字段,用這些屬性來存儲和獲取值。經過指定一組屬性名和屬性值的類型,咱們可使用Cglib的BeanGenerator和BeanMap來動態生成Bean。下面是一個例子。

[java] view plain copy

  1. import java.lang.reflect.Method;  
  2. import java.util.HashMap;  
  3. import java.util.Iterator;  
  4. import java.util.Map;  
  5. import java.util.Set;  
  6. import net.sf.cglib.beans.BeanGenerator;  
  7. import net.sf.cglib.beans.BeanMap;  
  8.   
  9. /** 
  10.  * 動態實體bean 
  11.  * 
  12.  * @author cuiran 
  13.  * @version 1.0 
  14.  */  
  15. class CglibBean {  
  16.   
  17.     //Bean實體Object  
  18.     public Object object = null;  
  19.     //屬性map  
  20.     public BeanMap beanMap = null;  
  21.   
  22.     public CglibBean() {  
  23.         super();  
  24.     }  
  25.   
  26.     @SuppressWarnings("unchecked")  
  27.     public CglibBean(Map<String, Class> propertyMap) {  
  28.         //用一組屬性生成實體Bean  
  29.         this.object = generateBean(propertyMap);  
  30.         //用實體Bean建立BeanMap,以即可以設置和獲取Bean屬性的值  
  31.         this.beanMap = BeanMap.create(this.object);  
  32.     }  
  33.   
  34.     /** 
  35.      * 給bean中的屬性賦值 
  36.      * 
  37.      * @param property 屬性名 
  38.      * @param value 值 
  39.      */  
  40.     public void setValue(String property, Object value) {  
  41.         beanMap.put(property, value);  
  42.     }  
  43.   
  44.     /** 
  45.      * 獲取bean中屬性的值 
  46.      * 
  47.      * @param property 屬性名 
  48.      * @return 值 
  49.      */  
  50.     public Object getValue(String property) {  
  51.         return beanMap.get(property);  
  52.     }  
  53.   
  54.     /** 
  55.      * 獲得該實體bean對象 
  56.      * 
  57.      * @return 
  58.      */  
  59.     public Object getObject() {  
  60.         return this.object;  
  61.     }  
  62.   
  63.     @SuppressWarnings("unchecked")  
  64.     private Object generateBean(Map<String, Class> propertyMap) {  
  65.         //根據一組屬性名和屬性值的類型,動態建立Bean對象  
  66.         BeanGenerator generator = new BeanGenerator();  
  67.         Set keySet = propertyMap.keySet();  
  68.         for (Iterator i = keySet.iterator(); i.hasNext();) {  
  69.             String key = (String) i.next();  
  70.             generator.addProperty(key, (Class) propertyMap.get(key));  
  71.         }  
  72.         return generator.create();  //建立Bean  
  73.     }  
  74. }  
  75.   
  76. /** 
  77.  * Cglib測試類 
  78.  * 
  79.  * @author cuiran 
  80.  * @version 1.0 
  81.  */  
  82. public class CglibTest {  
  83.   
  84.     @SuppressWarnings("unchecked")  
  85.     public static void main(String[] args) throws ClassNotFoundException { // 設置類成員屬性  
  86.         HashMap<String, Class> propertyMap = new HashMap<>();  
  87.         propertyMap.put("id", Class.forName("java.lang.Integer"));  
  88.         propertyMap.put("name", Class.forName("java.lang.String"));  
  89.         propertyMap.put("address", Class.forName("java.lang.String")); // 生成動態Bean  
  90.         CglibBean bean = new CglibBean(propertyMap);  
  91.         // 給Bean設置值  
  92.         bean.setValue("id", 123);  //Auto-boxing  
  93.         bean.setValue("name", "454");  
  94.         bean.setValue("address", "789");  
  95.         // 從Bean中獲取值,固然得到值的類型是Object  
  96.         System.out.println(" >> id = " + bean.getValue("id"));  
  97.         System.out.println(" >> name = " + bean.getValue("name"));  
  98.         System.out.println(" >> address = " + bean.getValue("address"));  
  99.         // 得到bean的實體  
  100.         Object object = bean.getObject();  
  101.         // 經過反射查看全部方法名  
  102.         Class clazz = object.getClass();  
  103.         Method[] methods = clazz.getDeclaredMethods();  
  104.         for (Method curMethod : methods) {  
  105.             System.out.println(curMethod.getName());  
  106.         }  
  107.     }  
  108. }  

  輸出結果:

[java] view plain copy

  1.  >> id = 123  
  2.  >> name = 454  
  3.  >> address = 789  
  4. getAddress  
  5. getName  
  6. getId  
  7. setName  
  8. setId  
  9. setAddress  

 

 

CGLIB輕鬆實現延遲加載

 

  經過使用LazyLoader,能夠實現延遲加載,即在沒有訪問對象的字段或方法以前並不加載對象,只有當要訪問對象的字段或方法時才進行加載。下面是一個例子。

[java] view plain copy

  1. import net.sf.cglib.proxy.Enhancer;  
  2. import net.sf.cglib.proxy.LazyLoader;  
  3.   
  4. class TestBean {  
  5.     private String userName;  
  6.   
  7.     /** 
  8.      * @return the userName 
  9.      */  
  10.     public String getUserName() {  
  11.         return userName;  
  12.     }  
  13.   
  14.     /** 
  15.      * @param userName the userName to set 
  16.      */  
  17.     public void setUserName(String userName) {  
  18.         this.userName = userName;  
  19.     }  
  20. }  
  21.   
  22. //延遲加載代理類  
  23. class LazyProxy implements LazyLoader {  
  24.   
  25.     //攔截Bean的加載,本方法會延遲處理  
  26.     @Override  
  27.     public Object loadObject() throws Exception {  
  28.         System.out.println("開始延遲加載!");  
  29.         TestBean bean = new TestBean(); //建立實體Bean  
  30.         bean.setUserName("test");  //給一個屬性賦值  
  31.         return bean;  //返回Bean  
  32.     }  
  33. }  
  34.   
  35. public class BeanTest {  
  36.   
  37.     public static void main(String[] args) {  
  38.         //建立Bean類型的延遲加載代理實例  
  39.         TestBean bean = (TestBean) Enhancer.create(TestBean.class, new LazyProxy());  
  40.         System.out.println("------");  
  41.         System.out.println(bean.getUserName());  
  42.     }  
  43. }  

  輸出結果:

[java] view plain copy

  1. ------  
  2. 開始延遲加載!  
  3. test  

  咱們建立TestBean類的延遲代理,經過LazyLoader中的loadObject()方法的攔截,實現對TestBean類的對象進行延遲加載。從輸出能夠看出,當建立延遲代理時,並無馬上加載目標對象(由於還有輸出「開始延遲加載!」),當經過代理訪問目標對象的getUserName()方法時,就會加載目標對象。可見loadObject()是延遲執行的。

相關文章
相關標籤/搜索