1. 程式人生 > >java動態代理與Cglib庫

java動態代理與Cglib庫

JDK動態代理

  代理模式是常用的Java設計模式,他的特徵是代理類與委託類有同樣的介面,代理類主要負責為委託類預處理訊息、過濾訊息、把訊息轉發給委託類,以及事後處理訊息等。代理類與委託類之間通常會存在關聯關係,一個代理類的物件與一個委託類的物件關聯,代理類的物件本身並不真正實現服務,而是通過呼叫委託類的物件的相關方法,來提供特定的服務。 
  按照代理的建立時期,代理類可以分為兩種。 
  靜態代理:由程式設計師建立或特定工具自動生成原始碼,再對其編譯。在程式執行前,代理類的.class檔案就已經存在了。 
  動態代理:在程式執行時,運用反射機制動態建立而成。

  為什麼使用動態代理?因為動態代理可以對請求進行任何處理。
  哪些地方需要動態代理?不允許直接訪問某些類;對訪問要做特殊處理等。
  目前Java開發包中包含了對動態代理的支援,但是其實現只支援對介面的的實現。 其實現主要通過java.lang.reflect.Proxy類和java.lang.reflect.InvocationHandler介面。
Proxy類主要用來獲取動態代理物件,InvocationHandler介面用來約束呼叫者實現。

  以下為模擬案例,通過動態代理實現在方法呼叫前後向控制檯輸出兩句字串。

  定義一個HelloWorld介面:

[java] view plain copy print?
  1. package com.ljq.test;  
  2.  /** 
  3.  * 定義一個HelloWorld介面 
  4.  *  
  5.  * @author jiqinlin 
  6.  * 
  7.  */
  8.  publicinterface HelloWorld {  
  9.     publicvoid sayHelloWorld();  
  10. }  
package com.ljq.test;

 /**
 * 定義一個HelloWorld介面
 * 
 * @author jiqinlin
 *
 */
 public interface HelloWorld {
    public void sayHelloWorld();
}

  類HelloWorldImpl是HelloWorld介面的實現:

[java] view plain copy print?
  1. package com.ljq.test;  
  2.  /** 
  3.  * 類HelloWorldImpl是HelloWorld介面的實現 
  4.  *  
  5.  * @author jiqinlin 
  6.  * 
  7.  */
  8.  publicclass HelloWorldImpl implements HelloWorld{  
  9.     publicvoid sayHelloWorld() {  
  10.         System.out.println("HelloWorld!");  
  11.     }  
  12. }  
package com.ljq.test;

 /**
 * 類HelloWorldImpl是HelloWorld介面的實現
 * 
 * @author jiqinlin
 *
 */
 public class HelloWorldImpl implements HelloWorld{

    public void sayHelloWorld() {
        System.out.println("HelloWorld!");
    }

}
  HelloWorldHandler是 InvocationHandler介面實現:
[java] view plain copy print?
  1. package com.ljq.test;  
  2.  import java.lang.reflect.InvocationHandler;  
  3.  import java.lang.reflect.Method;  
  4.  /** 
  5.  * 實現在方法呼叫前後向控制檯輸出兩句字串 
  6.  *  
  7.  * @author jiqinlin 
  8.  * 
  9.  */
  10.  publicclass HelloWorldHandler implements InvocationHandler{  
  11.     //要代理的原始物件
  12.      private Object obj;  
  13.     public HelloWorldHandler(Object obj) {  
  14.         super();  
  15.         this.obj = obj;  
  16.     }  
  17.     /** 
  18.      * 在代理例項上處理方法呼叫並返回結果 
  19.      *  
  20.      * @param proxy 代理類 
  21.      * @param method 被代理的方法 
  22.      * @param args 該方法的引數陣列 
  23.      */
  24.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
  25.         Object result = null;  
  26.         //呼叫之前
  27.          doBefore();  
  28.         //呼叫原始物件的方法
  29.         result=method.invoke(obj, args);  
  30.         //呼叫之後
  31.         doAfter();  
  32.         return result;  
  33.     }  
  34.     privatevoid doBefore(){  
  35.         System.out.println("before method invoke");  
  36.     }  
  37.     privatevoid doAfter(){  
  38.         System.out.println("after method invoke");  
  39.     }  
  40. }  
package com.ljq.test;

 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;

 /**
 * 實現在方法呼叫前後向控制檯輸出兩句字串
 * 
 * @author jiqinlin
 *
 */
 public class HelloWorldHandler implements InvocationHandler{
    //要代理的原始物件
     private Object obj;
    
    public HelloWorldHandler(Object obj) {
        super();
        this.obj = obj;
    }

    /**
     * 在代理例項上處理方法呼叫並返回結果
     * 
     * @param proxy 代理類
     * @param method 被代理的方法
     * @param args 該方法的引數陣列
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        //呼叫之前
         doBefore();
        //呼叫原始物件的方法
        result=method.invoke(obj, args);
        //呼叫之後
        doAfter();
        return result;
    }
    
    private void doBefore(){
        System.out.println("before method invoke");
    }
    
    private void doAfter(){
        System.out.println("after method invoke");
    }
    
}
  測試類:
[java] view plain copy print?
  1. package com.ljq.test;  
  2. import java.lang.reflect.InvocationHandler;  
  3. import java.lang.reflect.Proxy;  
  4. publicclass HelloWorldTest {  
  5.     publicstaticvoid main(String[] args) {  
  6.         HelloWorld helloWorld=new HelloWorldImpl();  
  7.         InvocationHandler handler=new HelloWorldHandler(helloWorld);  
  8.         //建立動態代理物件
  9.         HelloWorld proxy=(HelloWorld)Proxy.newProxyInstance(  
  10.             helloWorld.getClass().getClassLoader(),  
  11.             helloWorld.getClass().getInterfaces(),  
  12.             handler);  
  13.         proxy.sayHelloWorld();  
  14.     }  
  15. }  
package com.ljq.test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;


public class HelloWorldTest {

    public static void main(String[] args) {
        HelloWorld helloWorld=new HelloWorldImpl();
        InvocationHandler handler=new HelloWorldHandler(helloWorld);

        //建立動態代理物件
        HelloWorld proxy=(HelloWorld)Proxy.newProxyInstance(
            helloWorld.getClass().getClassLoader(),
            helloWorld.getClass().getInterfaces(),
            handler);
        proxy.sayHelloWorld();
    }
}
  執行結果為:
[plain] view plain copy print?
  1. before method invoke  
  2. HelloWorld!  
  3. after method invoke  
before method invoke
HelloWorld!
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 print?
  1. public Object intercept(Object object, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable;  
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建立代理。

  1、建立一個簡單的代理

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

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

* Create a proxy using NoOp callback. The target class 
* must have a default zero-argument constructor
* 
* @param targetClass the super class of the proxy 
* @return a new proxy for a target class instance 
*/ 
public Object createProxy(Class targetClass) { 
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(targetClass);
    enhancer.setCallback(NoOp.INSTANCE);
    return enhancer.create();
} 

  返回值是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()就可以了。方法中第一個引數定義了引數的型別,第 二個是引數的值。在引數中,基本型別應被轉化成類的型別。

  2、使用MethodInterceptor建立一個代理

  為了更好的使用代理,我們可以使用自己定義的MethodInterceptor型別回撥(callback)來代替net.sf.cglib.proxy.NoOp回撥。當對代理中所有方法的呼叫時,都會轉向MethodInterceptor型別的攔截(intercept)方法,在攔截方法中再呼叫底層物件相應的方法。下面我們舉個例子,假設你想對目標物件的所有方法呼叫進行許可權的檢查,如果沒有經過授權,就丟擲一個執行時的異常AuthorizationException。其中AuthorizationService.java介面的程式碼如下:

[java] view plain copy print?
  1. package com.lizjason.cglibproxy;   
  2. import java.lang.reflect.Method;   
  3. /**  
  4.  * A simple authorization service for illustration purpose.  
  5.  * @author Jason Zhicheng Li ([email protected] 
  6.  */
  7. publicinterface AuthorizationService {   
  8.     void authorize(Method method);   
  9. }  
package com.lizjason.cglibproxy; 

import java.lang.reflect.Method; 

/** 
 * A simple authorization service for illustration purpose. 
 * @author Jason Zhicheng Li ([email protected]) 
 */ 
public interface AuthorizationService { 
    void authorize(Method method); 
}

  對net.sf.cglib.proxy.MethodInterceptor介面的實現的類AuthorizationInterceptor.java程式碼如下:

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

import com.lizjason.cglibproxy.AuthorizationService;

/**
 * A simple MethodInterceptor implementation to
 * apply authorization checks for proxy method calls.
 */
public class AuthorizationInterceptor implements MethodInterceptor {

    private AuthorizationService authorizationService;

    /**
     * Create a AuthorizationInterceptor with the given AuthorizationService
     */
    public AuthorizationInterceptor (AuthorizationService authorizationService) {
        this.authorizationService = authorizationService;
    }

    /**
     * Intercept the proxy method invocations to inject authorization check. * The original
     * method is invoked through MethodProxy.
     */
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        if (authorizationService != null) {
            //may throw an AuthorizationException if authorization failed
            authorizationService.authorize(method);
        }
        return methodProxy.invokeSuper(object, args);
    }
}

  我們可以看到在攔截方法中,首先進行許可權的檢查,如果通過許可權的檢查,攔截方法再呼叫目標物件的原始方法。由於效能的原因,對原始方法的呼叫我們使用CGLIB的net.sf.cglib.proxy.MethodProxy物件,而不是反射中一般使用java.lang.reflect.Method物件。

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

[java] view plain copy print?
  1. package cglibexample;  
  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. /** 
  7.  * 定義一個HelloWorld類,沒有實現介面 
  8.  * 
  9.  */
  10. class HelloWorld {  
  11.     publicvoid sayHelloWorld() {  
  12.         System.out.println("HelloWorld!");  
  13.     }  
  14. }  
  15. /** 
  16.  * 通過Cglib實現在方法呼叫前後向控制檯輸出兩句字串 
  17.  * 
  18.  */
  19. class CglibProxy implements MethodInterceptor {  
  20.     //要代理的原始物件
  21.     private Object obj;  
  22.     public Object createProxy(Object target) {  
  23.         this.obj = target;  
  24.         Enhancer enhancer = new Enhancer();  
  25.         // 設定要代理的目標類,以擴充套件它的功能
  26.         enhancer.setSuperclass(this.obj.getClass());  
  27.         // 設定單一回調物件,在回撥中攔截對目標方法的呼叫
  28.         enhancer.setCallback(this);  
  29.         //設定類裝載器
  30.         enhancer.setClassLoader(target.getClass().getClassLoader());  
  31.         //建立代理物件
  32.         return enhancer.create();  
  33.     }  
  34.     /** 
  35.      * 回撥方法:在代理例項上攔截並處理目標方法的呼叫,返回結果 
  36.      * 
  37.      * @param proxy 代理類 
  38.      * @param method 被代理的方法 
  39.      * @param params 該方法的引數陣列 
  40.      * @param methodProxy 
  41.      */
  42.     @Override
  43.     public Object intercept(Object proxy, Method method, Object[] params,  
  44.             MethodProxy methodProxy) throws Throwable {  
  45.         Object result = null;  
  46.         // 呼叫之前
  47.         doBefore();  
  48.         // 呼叫目標方法,用methodProxy,
  49.         // 而不是原始的method,以提高效能
  50.         result = methodProxy.invokeSuper(proxy, params);  
  51.         // 呼叫之後
  52.         doAfter();  
  53.         return result;  
  54.     }  
  55.     privatevoid doBefore() {  
  56.         System.out.println("before method invoke");  
  57.     }  
  58.     privatevoid doAfter() {  
  59.         System.out.println("after method invoke");  
  60.     }  
  61. }  
  62. publicclass TestCglib {  
  63.     publicstaticvoid main(String[] args) {  
  64.         CglibProxy cglibProxy = new CglibProxy();  
  65.         HelloWorld hw = (HelloWorld) cglibProxy.createProxy(new HelloWorld());  
  66.         hw.sayHelloWorld();  
  67.     }  
  68. }  
package cglibexample;

import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
 * 定義一個HelloWorld類,沒有實現介面
 *
 */
class HelloWorld {

    public void sayHelloWorld() {
        System.out.println("HelloWorld!");
    }
}

/**
 * 通過Cglib實現在方法呼叫前後向控制檯輸出兩句字串
 *
 */
class CglibProxy implements MethodInterceptor {

    //要代理的原始物件
    private Object obj;

    public Object createProxy(Object target) {
        this.obj = target;
        Enhancer enhancer = new Enhancer();
        // 設定要代理的目標類,以擴充套件它的功能
        enhancer.setSuperclass(this.obj.getClass());
        // 設定單一回調物件,在回撥中攔截對目標方法的呼叫
        enhancer.setCallback(this);
        //設定類裝載器
        enhancer.setClassLoader(target.getClass().getClassLoader());
        //建立代理物件
        return enhancer.create();
    }

    /**
     * 回撥方法:在代理例項上攔截並處理目標方法的呼叫,返回結果
     *
     * @param proxy 代理類
     * @param method 被代理的方法
     * @param params 該方法的引數陣列
     * @param methodProxy
     */
    @Override
    public Object intercept(Object proxy, Method method, Object[] params,
            MethodProxy methodProxy) throws Throwable {
        Object result = null;
        // 呼叫之前
        doBefore();
        // 呼叫目標方法,用methodProxy,
        // 而不是原始的method,以提高效能
        result = methodProxy.invokeSuper(proxy, params);
        // 呼叫之後
        doAfter();
        return result;
    }

    private void doBefore() {
        System.out.println("before method invoke");
    }

    private void doAfter() {
        System.out.println("after method invoke");
    }
}

public class TestCglib {

    public static void main(String[] args) {
        CglibProxy cglibProxy = new CglibProxy();
        HelloWorld hw = (HelloWorld) cglibProxy.createProxy(new HelloWorld());
        hw.sayHelloWorld();
    }
}
  輸出結果: [plain] view plain copy print?
  1. before method invoke  
  2. HelloWorld!  
  3. after method invoke  
before method invoke
HelloWorld!
after method invoke
  基本流程:需要自己寫代理類,它實現MethodInterceptor介面,有一個intercept()回撥方法用於攔截對目標方法的呼叫,裡面使用methodProxy來呼叫目標方法。建立代理物件要用Enhance類,用它設定好代理的目標類、有intercept()回撥的代理類例項、最後用create()建立並返回代理例項。

  3、使用CallbackFilter在方法層設定回撥

  net.sf.cglib.proxy.CallbackFilter允許我們在方法層設定回撥(callback)。假如你有一個PersistenceServiceImpl類,它有兩個方法:save和load,其中方法save需要許可權檢查,而方法load不需要許可權檢查。

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

/**
 * A simple implementation of PersistenceService interface
 */
class PersistenceServiceImpl implements PersistenceService {

    //需要許可權檢查
    public void save(long id, String data) {
        System.out.println(data + " has been saved successfully.");
    }

    //不需要許可權檢查
    public String load(long id) {
        return "Test CGLIB CallBackFilter";
    }
}

/**
 * An implementation of CallbackFilter for PersistenceServiceImpl
 */
public class PersistenceServiceCallbackFilter implements CallbackFilter { 
    //callback index for save method
    private static final int SAVE = 0;
    //callback index for load method
    private static final int LOAD = 1;

    /**
     * Specify which callback to use for the method being invoked. 
     * @param method the method being invoked.
     * @return 
     */
    @Override
    public int accept(Method method) {
        //指定各方法的代理回撥索引
        String name = method.getName();
        if ("save".equals(name)) {
            return SAVE;
        }
        // for other methods, including the load method, use the
        // second callback
        return LOAD;
    }
}

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

[java] view plain copy print?
  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();  //建立代理物件
...
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersistenceServiceImpl.class);
//設定回撥過濾器
CallbackFilter callbackFilter = new PersistenceServiceCallbackFilter();
enhancer.setCallbackFilter(callbackFilter);
//建立各個目標方法的代理回撥
AuthorizationService authorizationService = ...
Callback saveCallback = new AuthorizationInterceptor(authorizationService);
Callback loadCallback = NoOp.INSTANCE;
//順序要與指定的回撥索引一致
Callback[] callbacks = new Callback[]{saveCallback, loadCallback };
enhancer.setCallbacks(callbacks);  //設定回撥
...
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為每一個方法指定一個回撥型別索引。

  4、使用Mixin

  Mixin通過代理方式將多種型別的物件繫結到一個大物件上,這樣對各個目標型別中的方法呼叫可以直接在這個大物件上進行。下面是一個例子。

[java] view plain copy print?
  1. import net.sf.cglib.proxy.Mixin;  
  2. interface MyInterfaceA {  
  3.     publicvoid methodA();  
  4. }  
  5. interface MyInterfaceB {  
  6.     publicvoid methodB();  
  7. }  
  8. class MyInterfaceAImpl implements MyInterfaceA {  
  9.     @Override
  10.     publicvoid methodA() {  
  11.         System.out.println("MyInterfaceAImpl.methodA()");  
  12.     }  
  13. }  
  14. class MyInterfaceBImpl implements MyInterfaceB {  
  15.     @Override
  16.     publicvoid methodB() {  
  17.         System.out.println("MyInterfaceBImpl.methodB()");  
  18.     }  
  19. }  
  20. publicclass Main {  
  21.     publicstaticvoid main(String[] args) {  
  22.         //各個物件對應的型別
  23.         Class[] interfaces = new Class[]{MyInterfaceA.class, MyInterfaceB.class};  
  24.         //各個物件
  25.         Object[] delegates = new Object[]{new MyInterfaceAImpl(), new MyInterfaceBImpl()};  
  26.         //將多個物件繫結到一個大物件上
  27.         Object obj = Mixin.create(interfaces, delegates);  
  28.         //直接在大物件上呼叫各個目標方法
  29.         ((MyInterfaceA)obj).methodA();  
  30.         ((MyInterfaceB)obj).methodB();  
  31.     }  
  32. }  

相關推薦

java動態代理Cglib

JDK動態代理   代理模式是常用的Java設計模式,他的特徵是代理類與委託類有同樣的介面,代理類主要負責為委託類預處理訊息、過濾訊息、把訊息轉發給委託類,以及事後處理訊息等。代理類與委託類之間通常會存在關聯關係,一個代理類的物件與一個委託類的物件關聯,代理類的物件本身並不真正實現服務,而是通過呼叫委託

Java動態代理Cglib應用

轉載:http://blog.csdn.net/zhoudaxia/article/details/30591941JDK動態代理  代理模式是常用的java設計模式,他的特徵是代理類與委託類有同樣的介面,代理類主要負責為委託類預處理訊息、過濾訊息、把訊息轉發給委託類,以及事

Java動態代理CGLIB

1. 靜態代理模式 因為需要對一些函式進行二次處理,或是某些函式不讓外界知道時,可以使用代理模式,通過訪問第三方,間接訪問原函式的方式,達到以上目的,來看一下代理模式的類圖: interface Hosee{     String sayhi(); }

Java動態代理機制詳解(JDK動態代理CGLIB動態代理區別)

代理是一種常用的設計模式,其目的就是為其他物件提供一個代理以控制對某個物件的訪問。代理類負責為委託類預處理訊息,過濾訊息並轉發訊息,以及進行訊息被委託類執行後的後續處理。在講述動態代理前,我們先通過一個例子瞭解一下什麼是靜態代理,這裡以事務控制為例。 1.靜態

JDK動態代理CGLIB動態代理

RR callback arm back 第一個 throw cati proxy clas 一、jdk動態代理 代理目標是 接口實現類的形式 代理的目標對象: 1 public class PersonServiceImpl implements PersonSer

動態代理cglib

art jdk動態代理 super tint pri out eth 通過 ble 代理分為靜態代理,jdk動態代理和cglib 1、首先,說一下靜態代理。 定義一個接口    public interface Interface{ void do

java動態代理CGLIB實現

ssl return 其他 ase ger pac 父類 linked nic 動態代理(CGlib 與連接池的案例) Cglib代理: 針對類來實現代理,對指定目標 產生一個子類 通過方法攔截技術攔截所有父類方法的調用。 我們要使用cglib代理必須引入 cglib的j

JDK動態代理CGLib動態代理相關問題

 導讀: 1、JDK動態代理原理是什麼?為什麼不支援類的代理? 2、JDK動態代理例項 3、CGLib代理原理是什麼? 4、CGLib代理例項 5、JDK動態代理與CGLib代理的區別是什麼? 6、總結     注:閱讀本文之前可以先閱讀

java代理,靜態代理動態代理以及spring aop代理方式,實現原理統一彙總 Spring中AOP的兩種代理方式(Java動態代理CGLIB代理

若代理類在程式執行前就已經存在,那麼這種代理方式被成為 靜態代理 ,這種情況下的代理類通常都是我們在Java程式碼中定義的。 通常情況下, 靜態代理中的代理類和委託類會實現同一介面或是派生自相同的父類。 一、概述1. 什麼是代理我們大家都知道微商代理,簡單地說就是代替廠家賣商品,廠家“委託”代理為

JDK動態代理CGLIB 動態代理

一、JDK動態代理 實現方式:通過反射類獲取目標類的介面實現,進行攔截和擴充套件 優點:通過位元組通過反射獲取目標物件的方法進行攔截 缺點:目標物件類一定要實現介面 示例程式碼: public interface UserService { String get

jdk動態代理cglib程式碼實現--SpringAop底層原理

動態代理分為兩類:基於介面的代理和基於繼承的代理 兩類實現的代表是:JDK代理 與 CGlib代理 cglib實現動態代理: 1、定義目標物件: public class RealSubject { //目標物件RealSubject,cglib不

SpringAOP深入瞭解之jdk動態代理CGlib

理解AOP 一般我們編寫程式的思想是縱向的,也就是一個方法程式碼從該方法第一行開始往下一步一步走,直到走完最後一行程式碼。也就是說很多業務都需要的比如使用者鑑權,資源釋放等我們都要在每個方法裡面重複再去呼叫。 public void doMethodOne() {

視覺化說明jdk動態代理cglib動態代理--InvocationHandler--MethodInterceptor

動態代理是為了實現Aop程式設計,代理的是類物件 【jdk動態代理】被代理的類需要實現介面,針對介面的代理,通過生成一個實現了介面的動態類實現代理 ServiceImpl是被代理類,實現介面Servi

Java動態代理在Android的應用

一、前言 1.1、什麼是代理? 大道理上講代理是一種軟體設計模式,目的地希望能做到程式碼重用。具體上講,代理這種設計模式是通過不直接訪問被代理物件的方式,而訪問被代理物件的方法。這個就好比 A---->B—>C 這種模式。A可以不通過直接與C對話的情

動態代理cglib代理

   spring的aop(面向切面程式設計)思想總結就是八個字“縱向重複,橫向抽取”。其作用體現在很多方面,例如在Filter中設定字元的編碼格式,Intercepter中賦值引數進行登入校驗等等。以下圖示展示了aop思想的一個重要且常見的用途,我們知道在service的實

jdk動態代理CGLib的區別

jdk動態代理與CGLib的區別 2017年09月20日 22:31:10 E_k_in 閱讀數:5151 動態代理proxy與CGLib的區別 標籤: 代理模式 2013-09-03 08:50 19977人閱讀 評論(4)&nbs

Java動態代理反射詳解

首先我得先請大家不要誤會,部落格園說轉載的文章放在文章分類裡,原創的文章用隨筆寫,我開先還以為隨筆是拿來寫抒情文的(滑稽),後來才發現不是這樣的,但是自己所有的文章都在文章分類裡了,又懶得搬運,所以我就用js重定向了一下。所以現在標題欄裡進來的都是文章分類哦,大部分都是自己原創的,轉載會註明轉載的url。 廢

細說java動態代理cglib動態代理

          提到代理,想必大家對設計模式中的靜態代理和動態代理都比較熟悉,小編之前在部落格中對動態和靜態代理進行了對比,這篇博文就只探討java動態代理和cglib動態代理之間的區別; ♚  

JDK動態代理CGLIB動態代理應用及原始碼解析

代理模式 代理模式:為其他物件提供一種代理以控制對這個物件的訪問。 代理模式中有三種角色:Subject抽象主題角色、RealSubject真實主題角色、Proxy代理主題角色。Subject描述了業務行為,RealSubject執行具體的業務邏輯,Prox

2.1 Spring宣告式事務的實現方式選擇(JDK動態代理cglib

1、簡介Spring宣告式事務的具體實現方式是動態決定的,與具體配置、以及事務代理物件是否實現介面等有關。2、使用JDK動態代理的情況在滿足下面兩個條件時,Spring會選擇JDK動態代理作為宣告式事務