使用CGLIB動態代理

wangjianme 12年前發布 | 73K 次閱讀 proxy 動態代理 CGLIB Java開發 Java

11、使用CGLIB動態代理

    Cglib是一個優秀的動態代理框架,它的底層使用ASM在內存中動態的生成被代理類的子類。使用CGLIB即使被代理類沒有實現任何接口也可以實現動態代理功能。CGLIB具有簡單易用,它的運行速度要遠遠快于JDKProxy動態代理:

使用CGLIB需要導入以下兩個jar文件:

    asm.jar – CGLIB的底層實現。

    cglib.jar – CGLIB的核心jar包。

CGLIB的核心類:

    net.sf.cglib.proxy.Enhancer – 主要的增強類

net.sf.cglib.proxy.MethodInterceptor – 主要的方法攔截類,它是Callback接口的子接口,需要用戶實現

net.sf.cglib.proxy.MethodProxy – JDKjava.lang.reflect.Method類的代理類,可以方便的實現對源對象方法的調用,如使用:

    Object o = methodProxy.invokeSuper(proxy, args);//雖然第一個參數是被代理對象,也不會出現死循環的問題。

費話少說,上代碼:

1、使用CGLIB的代理:

以下測試代理一個沒有實現任何接口的Person類:

    @Test

    public void testProxy1() throws Exception {

       final Person p1 = new Person();             //Person類沒有實現任何接口

       Enhancer en = new Enhancer();               //聲明增加類實例

       en.setSuperclass(Person.class);             //設置被代理類字節碼,CGLIB根據字節碼生成被代理類的子類

       en.setCallback(new MethodInterceptor() {    //設置回調函數,即一個方法攔截

           public Object intercept(Object target, Method method,

                  Object[] args, MethodProxy proxy) throws Throwable {

              Object o = method.invoke(p1,args);    //注意參數p1,仍然為外部聲明的源對象,且MethodJDKMethod反射

              System.err.println("After...");

              return o;

           }

       });

       Person p = (Person) en.create();             //通過create方法返回Person類的代理

       System.err.println(p.getClass());//被代理的對象

       p.sayHi("Hello");

    }

2、以下測試代理一個擁有接口的類:

IAnimal是接口,Dog是實現類,具體代碼如下:

    @Test

    public void testProxy2() throws Exception {

       final Dog dog = new Dog();             //聲明被代理對象

       Enhancer en = new Enhancer();          //聲明CGLIB增強類

       en.setSuperclass(IAnimal.class);       //設置接口類,也可以設置成dog實現類,會影響create返回的對象

       en.setCallback(new MethodInterceptor() {

           public Object intercept(Object target, Method method,

                  Object[] args, MethodProxy proxy) throws Throwable {

              System.err.println("Before...");

              Object o = method.invoke(dog, args);

              System.err.println("After...");

              return o;

           }

       });

       //Dog dog2 = (Dog) en.create();//必須轉型為接口,否則拋出ClassCastException

       IAnimal dog2 = (IAnimal)en.create();

       dog2.eat();

    }

說明:

由于上例中,設置了en.setSuperclass(IAnimal.class),所以en.create()方法,返回的對象,必須要轉換成IAnimal接口。如果轉換成Dog則會拋出ClassCastException

3、將CGLIB再做一個簡單的包裝:

 

class CglibProxy implements MethodInterceptor{

    private Object srcTarget;

    private CglibProxy(Object o){

       this.srcTarget = o;

    }

    @SuppressWarnings("unchecked")

    public static <T>T proxyTarget(T t){

       Enhancer en = new Enhancer();

       en.setSuperclass(t.getClass());

       en.setCallback(new CglibProxy(t));

       T tt = (T) en.create();

       return tt;

    }

    @Override

    public Object intercept(Object obj, Method method, Object[] args,

           MethodProxy proxy) throws Throwable {

       System.err.println("攔截前...");

       Object o = method.invoke(srcTarget, args);

       System.err.println("攔截后....");

       return o;

    }

}

包裝以后的調用代碼如下,主要是快速的實現獲取被代理類:

        Person p = CglibProxy.proxyTarget(new Person());

       p.sayHi("HJello");

       IAnimal dog = CglibProxy.proxyTarget(new Dog());

       dog.eat();

4、使用靜態方法代理一個沒有接口的對象

以下代碼,包含在一個測試方法或是main方法中運行:

       final Person src = new Person();

        //直接使用靜態方法代理一個對象

       Person p = (Person) Enhancer.create(Person.class,new MethodInterceptor(){

           public Object intercept(Object proxyedObj, Method method, Object[] args,

                  MethodProxy proxy) throws Throwable {

              System.err.println("Hello");

              //使用原生的方法調用,注意里面的src

              //Object oo = method.invoke(src, args);

              //使用MethodProxy調用父類的代碼,同樣有效

              Object oo = proxy.invokeSuper(proxyedObj, args);

              return oo;

           }

       });

       System.err.println(p.getClass());

       p.abc();

 本文由用戶 wangjianme 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!