一、前置知识

打印JDK和CGLib动态代理类。

1. Main方法添加代码:

  1. System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\class");

2. IDEA添加如下代码

-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true

image.png

3. 检验配置是否成功

配置的是D盘目录class文件下,查看是否有代码文件生成。
image.png

二、简单使用CGLib

Spring源码阅读之配置类解析 - 图3

CGLib不能对final方法、类进行代理。 CGLib与动态代理区别:

  1. Java动态代理 只能够对 接口 进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机制不允许多重继承);CGLIB能够代理普通类;
  2. Java动态代理使用Java原生的反射API进行操作,在生成类上比较高效;CGLIB使用 ASM 框架直接对字节码进行操作,在类的执行过程中比较高效。

引用出处

2.1 创建要被代理对象

public class Target {
    public void sayHello() {
        System.out.println("hello");
    }
}

2.2 创建回调方法

public class MyTargetInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println(String.format("日志记录:当前类:%s,当前方法名称:%s, 参数:%s", obj.getClass(), method.getName(), args));
        return methodProxy.invokeSuper(obj, args);
    }
}

2.3 创建代理对象

public class EnHanceDemo {
    public static void main(String[] args) {
        // #1 创建Enhancer对象
        Enhancer enhancer = new Enhancer();
        // #2 设置目标类型
        enhancer.setSuperclass(Target.class);
        // #3 设置回调
        enhancer.setCallback(new MyTargetInterceptor());

        Target target = (Target) enhancer.create();
        target.sayHello();
    }
}

// OUTPUT
// 日志记录:当前类:class com.clarencezero.c1_beanfactory.cglib.Target$$EnhancerByCGLIB$$ca012000,当前方法名称:sayHello, 参数:[Ljava.lang.Object;@579bb367
// hello
  1. 使用InvocationHandler 可能会导致 死循环 ,因为invoce中调用的任何原代理方法,均会重新代理到invode方法中。
  2. 可以使用 MethodInterceptor 避免上述的 死循环 发生。
  3. 当我们只对特定方法进行拦截,对其他方法放行时,只需要添加 CallbackFilter 即可:
  4. 提供 BeanGenerator 在运行时动态创建Bean对象。

CallBackFilter

public class Target {
    public String sayHello() {
        System.out.println("hello");
        return "hello";
    }
}
Enhancer enhancer = new Enhancer();
// 设置CallBackFilter
CallbackHelper callbackHelper = new CallbackHelper(Target.class, new Class[0]) {
    @Override
    protected Object getCallback(Method method) {
        if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class){
            return (FixedValue) () -> "Hello cglib";
        }else{
            return NoOp.INSTANCE;
        }
    }
};
enhancer.setSuperclass(Target.class);
enhancer.setCallbackFilter(callbackHelper);
enhancer.setCallbacks(callbackHelper.getCallbacks());
Target proxy = (Target) enhancer.create();
System.out.println(proxy.sayHello());
System.out.println(proxy.toString());
System.out.println(proxy.hashCode());

// Hello cglib
// com.clarencezero.c1_beanfactory.cglib.Target$$EnhancerByCGLIB$$3cea8bfa@4dcbadb4
// 1305193908

引用出处

三、Spring解析配置类示意图

下图简单描述Spring解析配置类流程,蓝色表示Spring对配置类对象进行CGLib代理,下面将着重分析这一步骤。

image.png
image.png
重点是第6步及第7步,在这里对带有注解 @Configuration 的配置类进行 CGLib 提升。

四、源码分析

4.1 回调ConfigurationClassPostProcessor#postProcessBeanFactory方法

    public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
        Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
        // #1 循环遍历BeanDefinitionNames,获取全配置注解(通过判断属性configurationClass值是否为full)
        // 若为全配置类,则添加到configBeanDefs集合中,供后续进行CGLib代理
        for (String beanName : beanFactory.getBeanDefinitionNames()) {
            BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
            Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
            MethodMetadata methodMetadata = null;
            if (beanDef instanceof AnnotatedBeanDefinition) {
                methodMetadata = ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();
            }
            if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
                // Configuration class (full or lite) or a configuration-derived @Bean method
                // -> resolve bean class at this point...
                AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
                if (!abd.hasBeanClass()) {
                    try {
                        abd.resolveBeanClass(this.beanClassLoader);
                    }
                    catch (Throwable ex) {
                        throw new IllegalStateException(
                                "Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
                    }
                }
            }
            if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
                if (!(beanDef instanceof AbstractBeanDefinition)) {
                    throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
                            beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
                }
                else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
                    logger.info("Cannot enhance @Configuration bean definition '" + beanName +
                            "' since its singleton instance has been created too early. The typical cause " +
                            "is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
                            "return type: Consider declaring such methods as 'static'.");
                }
                configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
            }
        }
        if (configBeanDefs.isEmpty()) {
            // nothing to enhance -> return immediately
            return;
        }
        if (IN_NATIVE_IMAGE) {
            throw new BeanDefinitionStoreException("@Configuration classes need to be marked as proxyBeanMethods=false. Found: " + configBeanDefs.keySet());
        }

        // #2 完成CGLib代理
        ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
        for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
            AbstractBeanDefinition beanDef = entry.getValue();
            beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
            Class<?> configClass = beanDef.getBeanClass();
            // △2-1:完成对全注解的CGLib动态代理
            Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
            if (configClass != enhancedClass) {
                if (logger.isTraceEnabled()) {
                    logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
                            "enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
                }
        // 2-2: 设置生成的代理对象类型
                beanDef.setBeanClass(enhancedClass);
            }
        }
    }

image.png

4.2 ConfigurationClassEnhancer#newEnhancer

/**
 * 创建一个新的CGLIB {@link Enhancer}实例。
 */
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
    Enhancer enhancer = new Enhancer();
    // #1 设置当前类为父类
    enhancer.setSuperclass(configSuperClass);

    // #2 添加EnhancedConfiguration接口(EnhancedConfiguration接口继承BeanFactoryAware意识,可以得到BeanFactory)
    // 有了BeanFactory工厂实例 配合动态代理就可以改变全配置类的方法的默认行为。全配置类@Bean注解,如果另一个@Bean在非全配置类中会实例化两个Bean,
    // 但在全配置类中,通过动态代理,只会返回单例的Bean实例对象。
    enhancer.setInterfaces(new Class<?>[]{EnhancedConfiguration.class});
    enhancer.setUseFactory(false);

    // #3 设置命名策略
    enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
    // #4 通过CGLibAPI生成BeanFactory成员变量,同时设置setBeanFactory方法。相当于组装成员变量与方法行为。
    enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));

    // #5 配置过滤器
    //         new BeanMethodInterceptor(), 方法过滤器
    //         new BeanFactoryAwareMethodInterceptor()
    enhancer.setCallbackFilter(CALLBACK_FILTER);
    enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
    return enhancer;
}

4.2.1 BeanMethodInterceptor

这个方法拦截配置类中被 @Bean 标注的方法,当第一次实例该对象时,拦截器(BeanMethodInterceptor)判断此时为第一次调用,所以会调用原方法(invokeSuper)实例化Bean对象,并完成正常的Spring生命周期流程。当第二次被其他@Bean注解的方法调用时,拦截器判断该方法已被调用过,所以会直接通过BeanFactory工厂取回。
这么做的目的是 确保正确处理语义 ,比如@Bean注解默认表示为单例,如果没有代理,则调用此注解的方法会返回多个实例,这样就违背了@Bean设计原则。


/**
 * 增强 {@link Bean @Bean} 方法以检查提供的BeanFactory是否存在此bean对象。
 */
@Override
@Nullable
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
                        MethodProxy cglibMethodProxy) throws Throwable {

    // #1 从代理对象获取BeanFactory
    ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);

    // #2 从method中推断bean名称,为方法名称
    String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);

    // #3 确定此bean是否为作用域代理
    if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
        String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
        if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
            beanName = scopedBeanName;
        }
    }

    // #4 处理FactoryBean
    // 要处理bean间方法引用的情况,我们必须显式检查。
    // 首先,检查所请求的bean是否为FactoryBean。
    // 如果是这样,创建一个子类代理,以拦截对getObject()的调用并返回所有缓存的bean实例。
    // 这确保了从@Bean方法中调用FactoryBean的语义与在XML中引用FactoryBean的语义相同。 请参阅SPR-6602。
    if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
        factoryContainsBean(beanFactory, beanName)) {
        // 从FactoryBean工厂中获取FactoryBean自身对象
        Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
        if (factoryBean instanceof ScopedProxyFactoryBean) {
            // Scoped proxy factory beans are a special case and should not be further proxied
        } else {
            // It is a candidate FactoryBean - go ahead with enhancement
            return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
        }
    }

    // #5 检查给定的方法是否对应容器的当前调用的工厂方法
    if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
        // 5-1 (第一次)调用父类方法(原对象方法)
        return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
    }

    // 5-2 从BeanFactory工厂获取该方法所对应的实例
    return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}

五、总结

简单总结了CBLib的API用法,理清使用CGLibAPI的脉络,至于细节,等有需要的时候再去深究。
本文简单总结了Spring解析 配置类 的流程,也了解CGLib在何时对配置类进行代理。

六、附录

CGLib生成的配置类

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.clarencezero.c1_beanfactory.beanfactorypostprocessortest;

import java.lang.reflect.Method;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.cglib.core.Signature;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.cglib.proxy.NoOp;
import org.springframework.context.annotation.ConfigurationClassEnhancer.EnhancedConfiguration;

public class MyConfiguration$$EnhancerBySpringCGLIB$$88165706 extends MyConfiguration implements EnhancedConfiguration {
    private boolean CGLIB$BOUND;
    public static Object CGLIB$FACTORY_DATA;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private MethodInterceptor CGLIB$CALLBACK_1;
    private NoOp CGLIB$CALLBACK_2;
    private static Object CGLIB$CALLBACK_FILTER;
    private static final Method CGLIB$b$0$Method;
    private static final MethodProxy CGLIB$b$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$a$1$Method;
    private static final MethodProxy CGLIB$a$1$Proxy;
    private static final Method CGLIB$setBeanFactory$6$Method;
    private static final MethodProxy CGLIB$setBeanFactory$6$Proxy;
    public BeanFactory $$beanFactory;

    static void CGLIB$STATICHOOK3() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("com.clarencezero.c1_beanfactory.beanfactorypostprocessortest.MyConfiguration$$EnhancerBySpringCGLIB$$88165706");
        Class var1;
        CGLIB$setBeanFactory$6$Method = ReflectUtils.findMethods(new String[]{"setBeanFactory", "(Lorg/springframework/beans/factory/BeanFactory;)V"}, (var1 = Class.forName("org.springframework.beans.factory.BeanFactoryAware")).getDeclaredMethods())[0];
        CGLIB$setBeanFactory$6$Proxy = MethodProxy.create(var1, var0, "(Lorg/springframework/beans/factory/BeanFactory;)V", "setBeanFactory", "CGLIB$setBeanFactory$6");
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"b", "()Lcom/clarencezero/c1_beanfactory/beanfactorypostprocessortest/B;", "a", "()Lcom/clarencezero/c1_beanfactory/beanfactorypostprocessortest/A;"}, (var1 = Class.forName("com.clarencezero.c1_beanfactory.beanfactorypostprocessortest.MyConfiguration")).getDeclaredMethods());
        CGLIB$b$0$Method = var10000[0];
        CGLIB$b$0$Proxy = MethodProxy.create(var1, var0, "()Lcom/clarencezero/c1_beanfactory/beanfactorypostprocessortest/B;", "b", "CGLIB$b$0");
        CGLIB$a$1$Method = var10000[1];
        CGLIB$a$1$Proxy = MethodProxy.create(var1, var0, "()Lcom/clarencezero/c1_beanfactory/beanfactorypostprocessortest/A;", "a", "CGLIB$a$1");
    }

    final B CGLIB$b$0() {
        return super.b();
    }

    public final B b() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        return var10000 != null ? (B)var10000.intercept(this, CGLIB$b$0$Method, CGLIB$emptyArgs, CGLIB$b$0$Proxy) : super.b();
    }

    final A CGLIB$a$1() {
        return super.a();
    }

    public final A a() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        return var10000 != null ? (A)var10000.intercept(this, CGLIB$a$1$Method, CGLIB$emptyArgs, CGLIB$a$1$Proxy) : super.a();
    }

    final void CGLIB$setBeanFactory$6(BeanFactory var1) throws BeansException {
        super.setBeanFactory(var1);
    }

    public final void setBeanFactory(BeanFactory var1) throws BeansException {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_1;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_1;
        }

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$setBeanFactory$6$Method, new Object[]{var1}, CGLIB$setBeanFactory$6$Proxy);
        } else {
            super.setBeanFactory(var1);
        }
    }

    public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
        String var10000 = var0.toString();
        switch(var10000.hashCode()) {
        case -2136804029:
            if (var10000.equals("b()Lcom/clarencezero/c1_beanfactory/beanfactorypostprocessortest/B;")) {
                return CGLIB$b$0$Proxy;
            }
            break;
        case -1528727197:
            if (var10000.equals("a()Lcom/clarencezero/c1_beanfactory/beanfactorypostprocessortest/A;")) {
                return CGLIB$a$1$Proxy;
            }
            break;
        case 2095635076:
            if (var10000.equals("setBeanFactory(Lorg/springframework/beans/factory/BeanFactory;)V")) {
                return CGLIB$setBeanFactory$6$Proxy;
            }
        }

        return null;
    }

    public MyConfiguration$$EnhancerBySpringCGLIB$$88165706() {
        CGLIB$BIND_CALLBACKS(this);
    }

    public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
        CGLIB$THREAD_CALLBACKS.set(var0);
    }

    public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
        CGLIB$STATIC_CALLBACKS = var0;
    }

    private static final void CGLIB$BIND_CALLBACKS(Object var0) {
        MyConfiguration$$EnhancerBySpringCGLIB$$88165706 var1 = (MyConfiguration$$EnhancerBySpringCGLIB$$88165706)var0;
        if (!var1.CGLIB$BOUND) {
            var1.CGLIB$BOUND = true;
            Object var10000 = CGLIB$THREAD_CALLBACKS.get();
            if (var10000 == null) {
                var10000 = CGLIB$STATIC_CALLBACKS;
                if (var10000 == null) {
                    return;
                }
            }

            Callback[] var10001 = (Callback[])var10000;
            var1.CGLIB$CALLBACK_2 = (NoOp)((Callback[])var10000)[2];
            var1.CGLIB$CALLBACK_1 = (MethodInterceptor)var10001[1];
            var1.CGLIB$CALLBACK_0 = (MethodInterceptor)var10001[0];
        }

    }

    static {
        CGLIB$STATICHOOK4();
        CGLIB$STATICHOOK3();
    }

    static void CGLIB$STATICHOOK4() {
    }
}