CGLIB动态代理

CGLIB基于ASM实现。提供比反射更为强大的动态特性。使用CGLIB可以非常方便的实现的动态代理。但是由于jdk17+由于java.base模块未向未命名模块开放java.lang包,所以CGLIB无法在jdk17+中运行。在java17+后 需要使用 Byte Buddy,这里cglib就没法测试了,

maven项目需要添加依赖

        <!-- https://mvnrepository.com/artifact/cglib/cglib -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>

1 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实现动态代理

2 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一样,不同的是,它可以把代理对象作为装载对象方法的一个参数传递。

3 1.2 CGLIB动态代理的基本原理

CGLIB动态代理的原理就是用Enhancer生成一个原有类的子类,并且设置好callback到proxy, 则原有类的每个方法调用都会转为调用实现了MethodInterceptor接口的proxy的intercept() 函数,如图

在intercept()函数里,除执行代理类的原因方法,在原有方法前后加入其他需要实现的过程,改变原有方法的参数值,即可以实现对原有类的代理了。这似于AOP中的around advice。

4 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接口无法拦截该方法。

4.1 1.3.1 实现MethodInterceptor接口

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

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;
    }
}

Object result=proxy.invokeSuper(o,args); 表示调用原始类的被拦截到的方法。这个方法的前后添加需要的过程。在这个方法中,我们可以在调用原方法之前或之后注入自己的代码。

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

5 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的一个实例的代理。

6 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) throws Throwable {
        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) throws Throwable {

        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(); // 吃
    }
}
/**
 * 回调过滤器类。
 */
public 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;
    }
}

/**
 * 自定义回调类。
 */
public 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

M ixin ByC GLI B MixinByCGLIB

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.class, false);
        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

B eanG ener ator ByC GLI B BeanGeneratorByCGLIB

1d39cfaa