动态代理实现bean Convert工具类(一):cglib实现ProxyUtil

本文详细介绍了CGLib动态代理的工作原理和在Spring中的使用,包括代理模式、动态代理的实现方式如Javassist、JDK动态代理和CGLib。着重讲解了CGLib的核心类Enhancer,以及Callback接口的六种模式,如MethodInterceptor、FixedValue、NoOp、LazyLoader和Dispatcher。最后展示了如何利用CGLib创建自定义的代理工具类ProxyUtil,实现多类型代理和普通代理的生成。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

动态代理实现bean Convert工具类(一):cglib实现ProxyUtil

在进入正题之前先看一个小例子,spring的简单使用:

新建一个spring空工程,新建一个service包,建立两个service:UserService,OrderService

@Service
public class UserService {
	public UserService(){
		System.out.println("userService init");
	}
}

@Service
public class OrderService {
}

然后新建一个config包,建立AppConfig类,并开启包扫描,把刚刚的service包设为javabean包

@ComponentScan("service")
public class AppConfig {
	@Bean
	public UserService userService() {
		return new UserService();
	}
    @Bean
	public OrderService orderService() {
		userService();
		return new OrderService();
	}
}

我们在这个类里初始化加载那两个bean,之后建立测试类

public class ApplicationTest {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfig.class);	System.out.println(annotationConfigApplicationContext.getBean(UserService.class).getClass().getName());
	}
}

运行之后发现会按照原有java正常运行逻辑打印出两个userService init.

之后我们在AppConfig类上加上@Configuration注解,再次运行结果发现这次只打印出了一个userService init,这是因为appConfig的内容被动态代理成立其他类,而里面初始化的实现也被修改了,为了验证这一点,我们将测试类修改如下:

public class ApplicationTest {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
		AppConfig bean = annotationConfigApplicationContext.getBean(AppConfig.class);
System.out.println(annotationConfigApplicationContext.getBean(UserService.class).getClass().getName());
	}
}

在实例化appconfig的那一行加上断点,之后分别在加上@Configuration注解前后debug运行,发现类确实改变了
在这里插入图片描述

未加注解前,类就是其本身
在这里插入图片描述
开启注解后,发现这个类被spring cglib动态代理了!这就引出了我们今天的重点,cglib动态代理,这也是实现bean convert的核心关键

1.理解动态代理

代理模式

代理模式是通过给某一对象提供一个代理对象,并由代理对象控制对原对象的引用实现中介隔离和开闭原则。[开闭原则: 不改变原有封装还的代码来增加功能]

动态代理

实现代理模式有两种,一种是静态代理,但随着业务的庞大,会导致proxy类的粒度越来越大,所以需要动态代理来解决此类问题。

实现动态代理的几种方案

  • javasist
  • jdk动态代理
  • cglib
  • asm(cglib内部也是用asm更改其字节码)

2.cglib

cglib是一套java动态代理实现的框架,cglib被用到spring, app, hibernate等高级业务框架, spring事务在业务实现类未实现接口的情况下也会使用这个技术.

实际上,cglib基于继承实现,这也就意味着final, private相关的method无法被代理。基于asm框架对class字节码编辑的改动,从而到达动态代理的目的, 总之, 被代理类没有实现接口的情况下cglib为首选。

看看官方的一段话:

字节码生成库是用于生成和转换JAVA字节码的高级API。AOP,测试,数据访问框架使用它来生成动态代理对象并拦截字段访问。

总结

简单的来说就是使用字节码处理框架ASM来转换字节码并生成新的需要代理类的子类,其核心就是通过继承这一个面向对象的特点来完成代理,子类需重写被代理的父类中所有非final的方法。同时在子类中采用方法拦截的技术拦截所有父类方法的调用, 顺势织入横切逻辑, 比JDK动态代理要快。但缺点是对于fianl方法无法进行代理。

callbacks简介

这里的callback可以认为是cglib用于生成字节码的实现手段,[作用就是拦截代理对象方法,修改代理对象方法,通过methodProxy代理实现原本的父类] cglib一共实现了6种callback, 用于对代理类目标进行不同手段的代理,非常灵活, 分别为:

  • FixedValue
  • InvovationHandler
  • LazyLoader
  • MethodInterceptor
  • Dispatcher
  • NoOp

3.cglib核心类:Enhancer

Enhancer是cglib的字节码增群器, 可以方便的对final类进行扩展, 它动态创建了给定类型的子类,但是拦截了所有非final方法。

我们看一下spring5中实现cglib代理中的一些源码

private Class superclass;

private Class[] interfaces;

private Class contextClass;

/**
	 * Set the interfaces to implement. The <code>Factory</code> interface will
	 * always be implemented regardless of what is specified here.
	 * @param interfaces array of interfaces to implement, or null
	 * @see Factory
	 */
public void setInterfaces(Class[] interfaces) {
    this.interfaces = interfaces;
}

/**
	 * Set the class which the generated class will extend. As a convenience,
	 * if the supplied superclass is actually an interface, <code>setInterfaces</code>
	 * will be called with the appropriate argument instead.
	 * A non-interface argument must not be declared as final, and must have an
	 * accessible constructor.
	 * @param superclass class to extend or interface to implement
	 * @see #setInterfaces(Class[])
	 */
public void setSuperclass(Class superclass) {
    if (superclass != null && superclass.isInterface()) {
        setInterfaces(new Class[]{superclass});
        // SPRING PATCH BEGIN
        setContextClass(superclass);
        // SPRING PATCH END
    }
  else if (superclass != null && superclass.equals(Object.class)) {
        // affects choice of ClassLoader
        this.superclass = null;
    }
    else {
        this.superclass = superclass;
        // SPRING PATCH BEGIN
        setContextClass(superclass);
        // SPRING PATCH END
    }
}

// SPRING PATCH BEGIN
public void setContextClass(Class contextClass) {
    this.contextClass = contextClass;
}

/**
	 * Set the single {@link Callback} to use.
	 * Ignored if you use {@link #createClass}.
	 * @param callback the callback to use for all methods
	 * @see #setCallbacks
	 */
public void setCallback(final Callback callback) {
    setCallbacks(new Callback[]{callback});
}

[注]上面的setSuperclass的源码上的注释翻译为:

设置这个类的父类,为了方便起见。如果提供的超类实际上是一个接口的话,调用setInterfaces方法,使用适当的参数代替。非接口参数不能声明为final,并且必须具有可访问的构造参数

supperclass:要继承的超类或要实现的接口

[注]上面的setInterfaces的源码上的注释翻译为:

设置要实现的接口,并且无论这里指定了生么,Factory接口都将始终实现(这个Factory接口是spring源码内部编译实现的,无法在源码中直接看到)

interfaces:要继承或实现的接口数组,或者是null

[注]上面setCallback的源码上的注释为:

设置使用单个Callbacl,但是如果使用ceateClass则会被忽略

callback:用于所有方法的回调

所以我们可以看到setSuperclass方法就是指定需要代理哪个类,如果是接口的话直接调用setInterfaces方法,并设置上下文对象,如果代理的对象为Object本身的话,则不生成代理类,否则设置代理类,为成员变量superclass注入值并设置上下文对象。

而Callback可以理解为我们需要对原始类方法做的增强处理

简单代码实现

下面来看一段代码:

public class CglibTest {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(QService.class);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("my callback");
                methodProxy.invokeSuper(o, objects);
                return o;
            }
        });
        QService qService = (QService) enhancer.create();
        System.out.println("测试test1方法 --------");
        qService.test1();
        System.out.println("测试test2方法---------");
        qService.test2();
        System.out.println("getClass方法---------");
        qService.getClass();
        System.out.println("hashCode方法--------");
        qService.hashCode();
    }
}
class QService {
    public void test1() {
        System.out.println("test1");
    }
    public void test2() {
        System.out.println("test2");
    }
}

测试的结果是只输出了3个my callback,这是Enhancer生成的代理只拦截了test1,test2,hashCode等非final方法,未拦截getClass这个final修饰的方法。

4.Callback

1. Callback增强简单实现

public class CglibTest {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(QService.class);
        Callback[] callbacks = new Callback[2];
        callbacks[0] = NoOp.INSTANCE;
        callbacks[1] = (MethodInterceptor) (o, method, objects, methodProy) -> {
            System.out.println("my callback");
            methodProy.invokeSuper(o, objects);
            return o;
        };
        enhancer.setCallbackFilter(method -> {
            if (method.getName().equals("test1") || method.getName().equals("test2")) {
                return 1;
            }else {
                return 0;
            }
        });
        enhancer.setCallbacks(callbacks);
        QService qService = (QService) enhancer.create();
        System.out.println("测试test1方法 --------");
        qService.test1();
        System.out.println("测试test2方法---------");
        qService.test2();
        System.out.println("getClass方法---------");
        qService.getClass();
        System.out.println("hashCode方法--------");
        qService.hashCode();
    }
}
class QService {
    public void test1() {
        System.out.println("test1");
    }
    public void test2() {
        System.out.println("test2");
    }
}

在这里设置了两个Callback,Enhancer支持多个Callback,实现不同方式的增强,而CallbackFilter的作用就是帮助代理类找到某个方法需要的Callback。

2. 6种Callback的模式
  • MethodInterceptor

是一个功能强大的接口,类似于AOP中的环绕增强,下面看一下它的源码

public interface MethodInterceptor extends Callback {
    Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
}

注意这个类也是spring中内部编译的类,无法直接查看到。可以看到使用这个接口需要实现intercept方法,和字面意思一样,这个方法就会使用到字节码技术去拦截父类方法,该方法分别需要四个参数,其中MethodProxy也是spring内部编译的类,这个类中一般情况下最常用到的方法是invokeSuper和invoke方法,看一下这两个方法的源码

private final Object initLock = new Object();

private void init() {
        if (this.fastClassInfo == null) {
            synchronized(this.initLock) {
                if (this.fastClassInfo == null) {
                    MethodProxy.CreateInfo ci = this.createInfo;
                    MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
                    fci.f1 = helper(ci, ci.c1);
                    fci.f2 = helper(ci, ci.c2);
                    fci.i1 = fci.f1.getIndex(this.sig1);
                    fci.i2 = fci.f2.getIndex(this.sig2);
                    this.fastClassInfo = fci;
                    this.createInfo = null;
                }
            }
        }

    }    

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
    try {
        this.init();
        MethodProxy.FastClassInfo fci = this.fastClassInfo;
        return fci.f2.invoke(fci.i2, obj, args);
    } catch (InvocationTargetException var4) {
        throw var4.getTargetException();
    }
}

public Object invoke(Object obj, Object[] args) throws Throwable {
        try {
            this.init();
            MethodProxy.FastClassInfo fci = this.fastClassInfo;
            return fci.f1.invoke(fci.i1, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        } catch (IllegalArgumentException var5) {
            if (this.fastClassInfo.i1 < 0) {
                throw new IllegalArgumentException("Protected method: " + this.sig1);
            } else {
                throw var5;
            }
        }
    }

private static class CreateInfo {
    Class c1;
    Class c2;
    NamingPolicy namingPolicy;
    GeneratorStrategy strategy;
    boolean attemptLoad;

    public CreateInfo(Class c1, Class c2) {
        this.c1 = c1;
        this.c2 = c2;
        AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
        if (fromEnhancer != null) {
            this.namingPolicy = fromEnhancer.getNamingPolicy();
            this.strategy = fromEnhancer.getStrategy();
            this.attemptLoad = fromEnhancer.getAttemptLoad();
        }

    }
}

private static FastClass helper(MethodProxy.CreateInfo ci, Class type) {
    Generator g = new Generator();
    g.setType(type);
    g.setContextClass(type);
    g.setClassLoader(ci.c2.getClassLoader());
    g.setNamingPolicy(ci.namingPolicy);
    g.setStrategy(ci.strategy);
    g.setAttemptLoad(ci.attemptLoad);
    return g.create();
}

private static class FastClassInfo {
    FastClass f1;
    FastClass f2;
    int i1;
    int i2;

    private FastClassInfo() {
    }
}

可以看到当调用MethodfProxy中的invoke方法时,会先初始化Proxy对象,创建一个FastClassInfo对象,这个对象是实现字节码技术的关键。之后为FastClassInfo中的两个成员变量f1,f2赋值,并将这个FastClassInfo实例赋值给MethodProxy的成员变量fastClassInfo,最后实际上是调用了FastClass中的invoke方法,作用就是代理执行父类方法,可以整体理解为intercept方法为拦截父类方法,在用invokeSuper方法放行父类的方法。invoke和invokeSuper的区别是一个执行f1的invoke,另一个执行的是f2的,通常情况下使用invokeSuper,使用invoke不慎会出现死循环的情况导致堆栈溢出。最后看一下使用MethodInterceptor的具体实现

public class CglibTest {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(QService.class);
        Callback[] callbacks = new Callback[3];
        callbacks[0] = new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("default callback");
                methodProxy.invokeSuper(o, objects);
                return o;
            }
        };
        callbacks[1] = new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("callback1");
                methodProxy.invokeSuper(o, objects);
                return o;
            }
        };
        callbacks[2] = new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("callback2");
                methodProxy.invokeSuper(o, objects);
                return o;
            }
        };
        enhancer.setCallbacks(callbacks);
        enhancer.setCallbackFilter(new CallbackFilter() {
            @Override
            public int accept(Method method) {
                if (method.getName().equals("test1")) {
                    return 1;
                }else if (method.getName().equals("test2")) {
                    return 2;
                }
                return 0;
            }
        });
        QService qService = (QService) enhancer.create();
        System.out.println("测试test1方法 --------");
        qService.test1();
        System.out.println("测试test2方法---------");
        qService.test2("wang", 18);
    }
}
class QService {
    public void test1() {
        System.out.println("test1");
    }
    public void test2(String name, int age) {
        System.out.println("test2");
    }
}
  • FixedValue

    FixedValue的作用是可以让我们很方便的替换掉方法的返回值

    public class CglibTest {
        public static void main(String[] args) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(QService.class);
            enhancer.setCallback(new FixedValue() {
                @Override
                public Object loadObject() throws Exception {
                    return "new test1";
                }
            });
            QService qService = (QService) enhancer.create();
            System.out.println("测试test1方法 --------");
            qService.test1();
        }
    }
    
    class QService {
        public void test1() {
            System.out.println("test1");
        }
    }
    

    结果已经替换成了 new test1

  • NoOp

    就和字面意思一样啥都不干只是调用了原来的方法,进入这个类的源码

    public interface NoOp extends Callback {
        NoOp INSTANCE = new NoOp() {
        };
    }
    

    可以看到这个类什么都没做,只是实例化了一个实现这个接口的匿名类。所以这个类作为Callback的实现也非常简单

    public class CglibTest {
    
        public static void main(String[] args) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(QService.class);
            enhancer.setCallback(NoOp.INSTANCE);
            QService qService = (QService) enhancer.create();
            System.out.println("测试test1方法 --------");
           	qService.test1();
        }
    }
    class QService {
        public void test1() {
            System.out.println("test1");
        }
    }
    

    结果自然就是执行了原有方法。

  • LazyLoader

    就和名字一样,提供了一种懒加载模式,这个类提供的loadObject方法就是实现这种懒加载模式,它会在第一次被调用的时候触发,返回一个代理类的实例,这个实例会被存储起来然后负责所有被代理类方法调用,如果代理类的对象创建比较麻烦且不能确定是否会被使用的时候可以使用这个模式来延迟生成代理。这个懒加载模式会在我们之后试下bean convert的时候大量使用。

    public class CglibTest {
        public static void main(String[] args) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(QService.class);
            enhancer.setCallback((LazyLoader) () -> {
                try {
                    System.out.println("before lazyloader");
                    return new QService("arg");
                }finally {
                    System.out.println("after lazyloader");
                }
            });
            QService qService = (QService) enhancer.create();
            System.out.println(qService.getArg());
        }
    }
    class QService {
        private String arg;
        public QService(){}
        public QService(String arg) {
            this.arg = arg;
        }
        public String getArg() {
            return arg;
        }
    }
    

    可以看出代理类就是我们实例化的这个类。但如果我们将getArg()方法注释掉,会发现Qservice类不会实例化,这就是LazyLoader模式的特点,会在被调用时加载。

  • Dispatcher

    这个模式和LazyLoader一样,也提供了loadObject方法,这个方法同业也是返回一个实例代理对象。但是不同之处在于Dispatcher的loadObject方法在每次发生对原方法的调用时都会被调用并返回一个代理对象来调用原方法。也就是Dispatcher的loadObject方法返回的对象并不会存储起来,每次调用都会重写再生成新的代理类。可以简单的理解为两种模式的scope域不同,LazyLoader是Singleton单例,Dispatcher是Prototype

    public class CglibTest {
        public static void main(String[] args) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(QService.class);
            enhancer.setCallback((Dispatcher) () -> {
                try {
                    System.out.println("before dispatcher");
                    return new QService("arg");
                }finally {
                    System.out.println("after dispatcher");
                }
            });
            QService qService = (QService) enhancer.create();
            System.out.println(qService);
        }
    }
    class QService {
        private String arg;
        public QService(){}
        public QService(String arg) {
            this.arg = arg;
        }
        public String getArg() {
            return arg;
        }
    }
    

    可以看到具体实现也和LazyLoader一样。

5.手写ProxyUtil

1.InvocaionHandler

首先我们需要先了解一下JDK规定的代理实现方式的接口,进入源码看一下

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

其中和之前学到的methodProxy一样有一个invoke方法,作用是通过代理类来处理方法并返回结果,我们之后会经常用到这个接口以及它的实现类

2. 定义生成代理类的方法
  • 多类型代理

    考虑被代理的可能是一堆类型的情况,可以使用cglib循环设置代理类,然后通过提供的InvocaionHandler参数执行invoke方法

    public static <T> T newProxyInstance(final InvocationHandler invocationHandler, Class<?>... types) {
        Enhancer enhancer = new Enhancer();
        if (ArrayUtils.isNotEmpty(types)) {
            List<Class<?>> interfaces = Lists.newArrayList();
            // 循环加载被代理类
            for (Class<?> type : types) {
                // 判断是否为接口类型
                if (type.isInterface()) {
                    interfaces.add(type);
                } else {
                    enhancer.setSuperclass(type);
                }
            }
    		// 被代理的类是接口的话,可以直接调用setInterfaces方法,这样更方便
            if (interfaces.size() > 0) {
                enhancer.setInterfaces(interfaces.toArray(new Class[0]));
            }
    
        }
    	// 设置Callback,使用MethodInterceptor模式
        enhancer.setCallback(new MethodInterceptor() {
            // 匿名实现类,intercept方法会拦截父类中所有的非final方法
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                // 执行我们刚刚获取的invocationHandler参数
                return invocationHandler.invoke(o, method, objects);
            }
        });
    
        return (T) enhancer.create();
    }
    

    我们没有将需增强的功能写死再生成代理方法里面,而是使用多态的特效,动态注入不同的invocationHandler来实现不同的增强方法,减低了代码的耦合性

  • 生成普通代理

    生成一个普通类型的代理类,和上面方法一样,只不过不需要再循环添加被代理对象了,另外我们这里不再使用匿名类的方式,而是将实现类抽离出来方便其他代码调用

    public static <T> T newProxyInstance(Class<?> targetClass, final InvocationHandler invocationHandler, final CallbackFilter filter) {
        if (targetClass == null) {
            return null;
        }
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);
        enhancer.setCallback(new SimpleMethodInterceptor(invocationHandler, filter));
        return (T) enhancer.create();
    }
    // 抽离出来的实现类
    private static class SimpleMethodInterceptor implements MethodInterceptor, Serializable {
        // transient关键字用于关闭序列化
        private transient InvocationHandler invocationHandler;
        // 实例化时需要注入这个自定义的成员变量,其作用是判断是否符合断言,具体实现会在之后的篇章讲到
        private transient CallbackFilter filter;
        public SimpleMethodInterceptor(InvocationHandler invocationHandler, CallbackFilter filter) {
            this.invocationHandler = invocationHandler;
            this.filter = filter;
        }
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            // 这里会有一个断言判断, 如何符合我们需要拦截的方法,则使用动态注入的invocationHandler的invoke方法
            if (filter.accept(method)) {
                return invocationHandler.invoke(o, method, objects);
            }
            // 这个方法我们之前学过,作用是代理执行父类的原有方法不做任何增强,相当于直接放行不做拦截。
            return methodProxy.invokeSuper(o, objects);
        }
    }
    public static interface CallbackFilter {
        boolean accept(Method method);
    }
    

至此我们已经学完了实现Convert工具类的基础,代理类

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值