CGLIB學習筆記

CGLIB學習筆記

0 概述

CGLIB基於ASM實現。提供比反射更爲強大的動態特性。使用CGLIB可以非常方便的實現的動態代理。

 

0.1 CGLIB包結構

  • net.sf.cglib.core    底層字節碼處理類。

  • net.sf.cglib.transform    該包中的類用於class文件運行時轉換或編譯時轉換。

  • net.sf.cglib.proxy    該包中的類用於創建代理和方法攔截。

  • net.sf.cglib.reflect    該包中的類用於快速反射,並提供了C#風格的委託。

  • net.sf.cglib.util    集合排序工具類。

  • net.sf.cglib.beans    JavaBean工具類。

 

 

 

1 使用CGLIB實現動態代理

1.1 CGLIB代理相關的類

  • net.sf.cglib.proxy.Enhancer    主要的增強類。
  • net.sf.cglib.proxy.MethodInterceptor    主要的方法攔截類,它是Callback接口的子接口,需要用戶實現。
  • net.sf.cglib.proxy.MethodProxy    JDK的java.lang.reflect.Method類的代理類,可以方便的實現對源對象方法的調用。

cglib是通過動態的生成一個子類去覆蓋所要代理類的非final方法,並設置好callback,則原有類的每個方法調用就會轉變成調用用戶定義的攔截方法(interceptors)。

CGLIB代理相關的常用API如下圖所示:

 

net.sf.cglib.proxy.Callback接口在CGLIB包中是一個重要的接口,所有被net.sf.cglib.proxy.Enhancer類調用的回調(callback)接口都要繼承這個接口。

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一樣,不同的是,它可以把代理對象作爲裝載對象方法的一個參數傳遞。

 

 

1.2 CGLIB動態代理的基本原理

CGLIB動態代理的原理就是用Enhancer生成一個原有類的子類,並且設置好callback到proxy, 則原有類的每個方法調用都會轉爲調用實現了MethodInterceptor接口的proxy的intercept() 函數,如圖

在intercept()函數裏,除執行代理類的原因方法,在原有方法前後加入其他需要實現的過程,改變原有方法的參數值,即可以實現對原有類的代理了。這似於AOP中的around advice。

 

 

1.3 使用MethodInterceptor接口實現方法回調

當對代理中所有方法的調用時,都會轉向MethodInterceptor類型的攔截(intercept)方法,在攔截方法中再調用底層對象相應的方法。下面我們舉個例子,假設你想對目標對象的所有方法調用進行權限的檢查,如果沒有經過授權,就拋出一個運行時的異常。

net.sf.cglib.proxy.MethodInterceptor接口是最通用的回調(callback)類型,它經常被基於代理的AOP用 來實現攔截(intercept)方法的調用。

MethodInterceptor接口只定義了一個方法:

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

參數Object object是被代理對象,不會出現死循環的問題。

參數java.lang.reflect.Method method是java.lang.reflect.Method類型的被攔截方法。

參數Object[] args是被被攔截方法的參數。

參數MethodProxy proxy是CGLIB提供的MethodProxy 類型的被攔截方法。

注意:

1、若原方法的參數存在基本類型,則對於第三個參數Object[] args會被轉化成類的類型。如原方法的存在一個參數爲int,則在intercept方法中,對應的會存在一個Integer類型的參數。

2、若原方法爲final方法,則MethodInterceptor接口無法攔截該方法。

 

1.3.1 實現MethodInterceptor接口

class MethodInterceptorImpl implements MethodInterceptor {

    @Override    

    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throwsThrowable {

        System.out.println("Before invoke " + method);

        Object result = proxy.invokeSuper(obj, args);

        System.out.println("After invoke" + method);

        return result;

    }

}

Object result=proxy.invokeSuper(o,args); 表示調用原始類的被攔截到的方法。這個方法的前後添加需要的過程。在這個方法中,我們可以在調用原方法之前或之後注入自己的代碼。 

由於性能的原因,對原始方法的調用使用CGLIB的net.sf.cglib.proxy.MethodProxy對象,而不是反射中一般使用java.lang.reflect.Method對象。

 

 

1.4 使用CGLIB代理最核心類Enhancer生成代理對象

net.sf.cglib.proxy.Enhancer中有幾個常用的方法:

  • void setSuperclass(java.lang.Class superclass) 設置產生的代理對象的父類。
  • void setCallback(Callback callback) 設置CallBack接口的實例。
  • void setCallbacks(Callback[] callbacks) 設置多個CallBack接口的實例。
  • void setCallbackFilter(CallbackFilter filter) 設置方法回調過濾器。
  • Object create() 使用默認無參數的構造函數創建目標對象。
  • Object create(Class[], Object[]) 使用有參數的構造函數創建目標對象。參數Class[] 定義了參數的類型,第二個Object[]是參數的值。

注意:在參數中,基本類型應被轉化成類的類型。

基本代碼:

public Object createProxy(Class targetClass) {

    Enhancer enhancer = new Enhancer();

    enhancer.setSuperclass(targetClass);

    enhancer.setCallback(new MethodInterceptorImpl ());

    return enhancer.create();

}

createProxy方法返回值是targetClass的一個實例的代理。

 

 

1.5 使用CGLIB繼進行動態代理示例

例1:使用CGLIB生成代理的基本使用。

import java.lang.reflect.Method;

 

import net.sf.cglib.proxy.Enhancer;

import net.sf.cglib.proxy.MethodInterceptor;

import net.sf.cglib.proxy.MethodProxy;

 

public class TestMain {

    public static void main(String[] args) {

        Enhancer enhancer = new Enhancer();

        enhancer.setSuperclass(Cglib.class);

        enhancer.setCallback(new HelloProxy());

        Cglib cglibProxy = (Cglib)enhancer.create();

        cglibProxy.cglib();

    }

}

 

class Cglib{

    public void cglib(){

        System.out.println("CGLIB");

    }

}

 

class HelloProxy implements MethodInterceptor{

    @Override

    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throwsThrowable {

        System.out.println("Hello");

        Object object = proxy.invokeSuper(obj, args);

        System.out.println("Powerful!");

        return object;

    }

}

 

輸出內容:

Hello

CGLIB

Powerful!

 

 

例2:使用CGLIB創建一個Dao工廠,並展示一些基本特性。

public interface Dao {

    void add(Object o);

    void add(int i);

    void add(String s);

}

 

public class DaoImpl implements Dao {

    @Override

    public void add(Object o) {

        System.out.println("add(Object o)");

    }

 

    @Override

    public void add(int i) {

        System.out.println("add(int i)");

    }

 

    public final void add(String s) {

        System.out.println("add(String s)");

    }

}

 

 

public class Proxy implements MethodInterceptor {

    @Override

    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throwsThrowable {

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

 

        // 輸出參數類型

        for (Object arg : args) {

            System.out.print(arg.getClass() + ";");

        }

        Object result = proxy.invokeSuper(obj, args);

 

        System.out.println("攔截後...");

        return result;

    }

}

 

public class DaoFactory {

    public static Dao create() {

        Enhancer enhancer = new Enhancer();

        enhancer.setSuperclass(DaoImpl.class);

        enhancer.setCallback(new Proxy());

        Dao dao = (Dao) enhancer.create();

        return dao;

    }

}

 

public class TestMain {

    public static void main(String[] args) {

        Dao dao = DaoFactory.create();

        dao.add(new Object());

        dao.add(1);

        dao.add("1");

    }

}

 

輸出內容:

攔截前...

class java.lang.Object;add(Object o)

攔截後...

攔截前...

class java.lang.Integer;add(int i)

攔截後...

add(String s)

 

 

 

2 回調過濾器CallbackFilter

net.sf.cglib.proxy.CallbackFilter有選擇的對一些方法使用回調。

CallbackFilter可以實現不同的方法使用不同的回調方法。所以CallbackFilter稱爲"回調選擇器"更合適一些。

CallbackFilter中的accept方法,根據不同的method返回不同的值i,這個值是在callbacks中callback對象的序號,就是調用了callbacks[i]。

 

import java.lang.reflect.Method;

 

import net.sf.cglib.proxy.Callback;

import net.sf.cglib.proxy.CallbackFilter;

import net.sf.cglib.proxy.Enhancer;

import net.sf.cglib.proxy.MethodInterceptor;

import net.sf.cglib.proxy.MethodProxy;

import net.sf.cglib.proxy.NoOp;

 

public class CallbackFilterDemo {

    public static void main(String[] args) {

        // 回調實例數組

        Callback[] callbacks = new Callback[] { new MethodInterceptorImpl(), NoOp.INSTANCE };

 

        // 使用enhancer,設置相關參數。

        Enhancer enhancer = new Enhancer();

        enhancer.setSuperclass(User.class);

        enhancer.setCallbacks(callbacks);

        enhancer.setCallbackFilter(new CallbackFilterImpl());

 

        // 產生代理對象

        User proxyUser = (User) enhancer.create();

 

        proxyUser.pay(); // 買

        proxyUser.eat(); // 喫

    }

 

    /**

     * 回調過濾器類。

     */

    private static class CallbackFilterImpl implements CallbackFilter {

 

        @Override

        public int accept(Method method) {

            String methodName = method.getName();

            if ("eat".equals(methodName)) {

                return 1; // eat()方法使用callbacks[1]對象攔截。

            } else if ("pay".equals(methodName)) {

                return 0; // pay()方法使用callbacks[0]對象攔截。

            }

            return 0;

        }

    }

 

    /**

     * 自定義回調類。

     */

    private static class MethodInterceptorImpl implements MethodInterceptor {

 

        @Override

        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)throws Throwable {

            System.out.println("Before invoke " + method);

            Object result = proxy.invokeSuper(obj, args); // 原方法調用。

            System.out.println("After invoke" + method);

            return result;

        }

    }

}

 

class User {

    public void pay() {

        System.out.println("買東西");

    }

 

    public void eat() {

        System.out.println("喫東西");

    }

}

 

輸出結果:

Before invoke public void sjq.cglib.filter.User.pay()

pay()

After invokepublic void sjq.cglib.filter.User.pay()

eat()

 

 

 

3 CGLIB對Mixin的支持

CGLIB的代理包net.sf.cglib.proxy.Mixin類提供對Minix編程的支持。Minix允許多個對象綁定到一個單個的大對象上。在代理中對方法的調用委託到下面相應的對象中。 這是一種將多個接口混合在一起的方式, 實現了多個接口。

Minix是一種多繼承的替代方案, 很大程度上解決了多繼承的很多問題, 實現和理解起來都比較容易。

import net.sf.cglib.proxy.Mixin;

 

public class MixinDemo {

    public static void main(String[] args) {

        //接口數組

        Class<?>[] interfaces = new Class[] { MyInterfaceA.class, MyInterfaceB.class };

          

        //實例對象數組

        Object[] delegates = new Object[] { new MyInterfaceAImpl(), new MyInterfaceBImpl() };

          

        //Minix組合爲o對象。

        Object o = Mixin.create(interfaces, delegates);

          

        MyInterfaceA a = (MyInterfaceA) o;

        a.methodA();

          

        MyInterfaceB b = (MyInterfaceB) o;

        b.methodB();

          

        System.out.println("\r\n 輸出Mixin對象的結構...");

        Class clazz = o.getClass();

        Method[] methods = clazz.getDeclaredMethods();

        for (int i = 0; i < methods.length; i++) {

            System.out.println(methods[i].getName());

        }

        System.out.println(clazz);

    }

}

 

interface MyInterfaceA {

    public void methodA();

}

 

interface MyInterfaceB {

    public void methodB();

}

 

class MyInterfaceAImpl implements MyInterfaceA {

    @Override

    public void methodA() {

        System.out.println("MyInterfaceAImpl.methodA()");

    }

}

 

class MyInterfaceBImpl implements MyInterfaceB {

    @Override

    public void methodB() {

        System.out.println("MyInterfaceBImpl.methodB()");

    }

}

 

輸出結果:

MyInterfaceAImpl.methodA()

MyInterfaceBImpl.methodB()

 

輸出Mixin對象的結構...

methodA

methodB

newInstance

class sjq.cglib.mixin.MyInterfaceA

MixinByCGLIBMixinByCGLIB

d1f6261a

 

 

 

4 CGLIB用來對象之間拷貝屬性

package sjq.cglib.bean.copy;

 

import net.sf.cglib.beans.BeanCopier;

 

public class PropertyCopyDemo {

    public static void main(String[] args) {

        //兩個對象

        Other other = new Other("test", "1234");

        Myth myth = new Myth();

 

        System.out.println(other);

        System.out.println(myth);

 

        //構建BeanCopier,並copy對象的屬性值。

        BeanCopier copier = BeanCopier.create(Other.class, Myth.classfalse);

        copier.copy(other, myth, null);

 

        System.out.println(other);

        System.out.println(myth);

    }

}

 

class Other {

    private String username;

    private String password;

    private int age;

 

    public String getUsername() {

        return username;

    }

 

    public void setUsername(String username) {

        this.username = username;

    }

 

    public String getPassword() {

        return password;

    }

 

    public void setPassword(String password) {

        this.password = password;

    }

 

    public Other(String username, String password) {

        super();

        this.username = username;

        this.password = password;

    }

 

    @Override

    public String toString() {

        return "Other: " + username + ", " + password + ", " + age;

    }

 

    public int getAge() {

        return age;

    }

 

    public void setAge(int age) {

        this.age = age;

    }

}

 

class Myth {

    private String username;

    private String password;

    private String remark;

 

    public String getUsername() {

        return username;

    }

 

    public void setUsername(String username) {

        this.username = username;

    }

 

    public String getPassword() {

        return password;

    }

 

    public void setPassword(String password) {

        this.password = password;

    }

 

    @Override

    public String toString() {

        return "Myth: " + username + ", " + password + ", " + remark;

    }

 

    public void setRemark(String remark) {

        this.remark = remark;

    }

 

    public String getRemark() {

        return remark;

    }

}

 

運行結果如下:

Other: test, 1234, 0

Myth: null, null, null

Other: test, 1234, 0

Myth: test, 1234, null

 

 

 

5 使用CGLIB動態生成Bean

import java.util.Iterator;

import java.util.Map;

import java.util.Set;

import net.sf.cglib.beans.BeanGenerator;

import net.sf.cglib.beans.BeanMap;

 

/**

*動態實體bean

*/

public class CglibBean {

    /**

     * 實體Object

     */

    public Object object = null;

    

    /**

     * 屬性map

     */

    public BeanMap beanMap = null;

 

    public CglibBean() {

        super();

    }

 

    @SuppressWarnings("unchecked")

    public CglibBean(Map<String, Class> propertyMap) {

        this.object = generateBean(propertyMap);

        this.beanMap = BeanMap.create(this.object);

    }

 

    /**

     * 給bean屬性賦值

     * @param property屬性名

     * @param value值

     */

    public void setValue(String property, Object value) {

        beanMap.put(property, value);

    }

 

    /**

     * 通過屬性名得到屬性值

     * @param property屬性名

     */

    public Object getValue(String property) {

        return beanMap.get(property);

    }

 

    /**

     * 得到該實體bean對象。

     */

    public Object getObject() {

        return this.object;

    }

 

    /**

     * 生成Bean

     * @param propertyMap

     * @return

     */

    @SuppressWarnings("unchecked")

    private Object generateBean(Map<String, Class> propertyMap) {

        BeanGenerator generator = new BeanGenerator();

        Set keySet = propertyMap.keySet();

        for (Iterator i = keySet.iterator(); i.hasNext();) {

            String key = (String) i.next();

            generator.addProperty(key, (Class) propertyMap.get(key));

        }

        return generator.create();

    }

}

 

 

測試並使用動態Bean

 

import java.lang.reflect.Method;

import java.util.HashMap;

 

/**

* Cglib測試類

*/

public class CglibTest {

    @SuppressWarnings("unchecked")

    public static void main(String[] args) throws ClassNotFoundException {

 

        // 設置類成員屬性

        HashMap<String, Class> propertyMap = new HashMap<String, Class>();

        propertyMap.put("id", Class.forName("java.lang.Integer"));

        propertyMap.put("name", Class.forName("java.lang.String"));

        propertyMap.put("address", Class.forName("java.lang.String"));

 

        // 生成動態Bean

        CglibBean bean = new CglibBean(propertyMap);

 

        // 給Bean設置值

        bean.setValue("id", new Integer(123));

        bean.setValue("name", "454");

        bean.setValue("address", "789");

 

        // 從Bean中獲取值,當然了獲得值的類型是Object

        System.out.println(">>id=" + bean.getValue("id"));

        System.out.println(">>name=" + bean.getValue("name"));

        System.out.println(">>address=" + bean.getValue("address"));// 獲得bean的實體

        Object object = bean.getObject();

 

        // 通過反射查看所有方法名

        Class clazz = object.getClass();

        Method[] methods = clazz.getDeclaredMethods();

        for (int i = 0; i < methods.length; i++) {

            System.out.println(methods[i].getName());

        }

    }

}

 

 

 

輸出:

>>id=123

>>name=454

>>address=789

setId

getAddress

getName

getId

setName

setAddress

class net.sf.cglib.empty.Object

BeanGeneratorByCGLIBBeanGeneratorByCGLIB

1d39cfaa

 

 

 

 

 

 

本篇學習文檔參考於:

http://jnb.ociweb.com/jnb/jnbNov2005.html

http://wenku.baidu.com/view/3f92297c27284b73f24250b9.html

http://www.cnblogs.com/icejoywoo/

http://www.blogjava.net/calvin/archive/2005/11/28/21741.html

http://m635674608.iteye.com/blog/1435221
等其他高手博客提供的資料。

 

 

 

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