spring探秘之ConfigurationClassPostProcessor(二):处理@Bean注解

123 阅读15分钟

注:本系列源码分析基于spring 5.2.2.RELEASE,本文的分析基于 annotation 注解方式,gitee仓库链接:gitee.com/funcy/sprin….

本文是ConfigurationClassPostProcessor分析的第二篇,主要是分析spring对@Bean注解的处理流程。

3. spring 是如何处理 @Bean 注解的?

承接上文,我们继续分析spring对@Bean注解的处理流程。

3.1 demo 准备

为了说明问题,我们直接上代码:

首先准备两个Bean:

public class BeanObj1 {
    public BeanObj1() {
        System.out.println("调用beanObj1的构造方法");
    }

    @Override
    public String toString() {
        return "BeanObj1{}";
    }
}

public class BeanObj2 {
    public BeanObj2() {
        System.out.println("调用beanObj2的构造方法");
    }

    @Override
    public String toString() {
        return "BeanObj2{}";
    }
}

注意:以上两个类都没有Component@Service等注解。

再准备一个配置类,通过@Bean注解的方法生成两个bean:

@Component
public class BeanConfigs {

    @Bean
    public BeanObj1 beanObj1() {
        return new BeanObj1();
    }

    @Bean
    public BeanObj2 beanObj2() {
        // 这里调用下 beanObj1() 方法
        beanObj1();
        return new BeanObj2();
    }

}

最后是启动类:

@ComponentScan
public class Demo02Main {

    public static void main(String[] args) {
        ApplicationContext context 
                = new AnnotationConfigApplicationContext(Demo02Main.class);
        Object obj1 = context.getBean("beanObj1");
        Object obj2 = context.getBean("beanObj2");
        System.out.println("obj1:" + obj1);
        System.out.println("obj2:" + obj2);
        System.out.println(context.getBean("beanConfigs"));
    }
}

对以上 代码,做以下几点需要说明:

  • BeanConfigs类使用的注解的是@Component,根据上一篇文章的分析,@Component也属于配置类,解析步骤同@Configuration注解;
  • Demo02Main中,传入AnnotationConfigApplicationContext类为Demo02Main,其上有一个注解@ComponentScan,这个注解没指定包扫描路径,根据上一篇文章的分析,不指定包扫描路径,spring会默认扫描配置类所在包;
  • 我们并没有直接把BeanConfigs注册到容器中(像new AnnotationConfigApplicationContext(BeanConfigs.class)这样),从上一篇文章的分析可知,spring会先解析Demo02Main类,处理其上的@Component注解,从而扫描到BeanConfigs类,然后会解析BeanConfigs,处理内部的@Bean方法,这个流程我们接下来也会通过调试的方式进行验证。

运行以上代码,结果如下:

调用beanObj1的构造方法
调用beanObj1的构造方法
调用beanObj2的构造方法
obj1:BeanObj1{}
obj2:BeanObj2{}
org.springframework.learn.explore.demo05.BeanConfigs@2b71e916

接下来,就以这个demo进行分析。

注意:本文是ConfigurationClassPostProcessor分析的第二篇,对与第一篇雷同的代码,本文只一笔带过,不会再进行详细分析。

3.2 处理配置类:ConfigurationClassPostProcessor#processConfigBeanDefinitions

我们直接进入ConfigurationClassPostProcessor#processConfigBeanDefinitions方法,调用链如下:

AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(Class)
 |-AbstractApplicationContext#refresh
  |-AbstractApplicationContext#invokeBeanFactoryPostProcessors
   |-PostProcessorRegistrationDelegate
      #invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory, List)
    |-PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors
     |-ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
      |-ConfigurationClassPostProcessor#processConfigBeanDefinitions

此时的candidates只有一个 元素:

3.2 解析demo02Main:ConfigurationClassParser#doProcessConfigurationClass

这一块就是解析@ComponentScan注解的过程,第一篇已详细分析过,这里只给出调用栈:

AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(Class)
 |-AbstractApplicationContext#refresh
  |-AbstractApplicationContext#invokeBeanFactoryPostProcessors
   |-PostProcessorRegistrationDelegate
      #invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory, List)
    |-PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors
     |-ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
      |-ConfigurationClassPostProcessor#processConfigBeanDefinitions
       |-ConfigurationClassParser#parse(Set<BeanDefinitionHolder>)
        |-ConfigurationClassParser#parse(AnnotationMetadata, String)
          |-ConfigurationClassParser#processConfigurationClass
           |-ConfigurationClassParser#doProcessConfigurationClass

处理Demo02Main@ComponentScan之后,可以看到beanConfigs已经扫描到了:

由于beanConfigs是配置类,因此会对其进行解析:

最终还是会回到ConfigurationClassParser#doProcessConfigurationClass,其中的调用链如下:

AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(Class)
 |-AbstractApplicationContext#refresh
  |-AbstractApplicationContext#invokeBeanFactoryPostProcessors
   |-PostProcessorRegistrationDelegate
      #invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory, List)
    |-PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors
     |-ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
      |-ConfigurationClassPostProcessor#processConfigBeanDefinitions
       |-ConfigurationClassParser#parse(Set<BeanDefinitionHolder>)
        |-ConfigurationClassParser#parse(AnnotationMetadata, String)
          |-ConfigurationClassParser#processConfigurationClass
           |-ConfigurationClassParser#doProcessConfigurationClass
            |-ConfigurationClassParser#parse(String, String)
             |-ConfigurationClassParser#processConfigurationClass
              |-ConfigurationClassParser#doProcessConfigurationClass

此时的ConfigurationClassParser#doProcessConfigurationClass,主角就不再是demo02Main,而是beanConfigs了。

3.3 解析beanConfigs:ConfigurationClassParser#doProcessConfigurationClass

接下来我们来看看ConfigurationClassParser#doProcessConfigurationClass@Bean注解的处理,代码如下:

/**
 * 这个方法才是真正处理解析的方法
 */
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, 
        SourceClass sourceClass) throws IOException {
    // 1. 如果是 @Component 注解,递归处理内部类,本文不关注
    ...

    // 2. 处理@PropertySource注解,本文不关注
    ...

    // 3. 处理 @ComponentScan/@ComponentScans 注解,本文不关注
    ...

    // 4. 处理@Import注解,本文不关注
    ...

    // 5. 处理@ImportResource注解,本文不关注
    ...

    // 6. 处理@Bean的注解
    // 具体的解析代码
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
        // 添加到 configClass 中,后面再处理
        configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }

    // 7. 返回配置类的父类,会在 processConfigurationClass(...) 方法的下一次循环时解析
    ...
    return null;
}

获取@Bean的方法调用的是 retrieveBeanMethodMetadata(...),我们跟进去:

private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {
    AnnotationMetadata original = sourceClass.getMetadata();
    // 获取包含 @Bean 注解的方法
    Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());
    ...
    return beanMethods;
}

再跟进去,最终调用的是StandardAnnotationMetadata#getAnnotatedMethods:

public Set<MethodMetadata> getAnnotatedMethods(String annotationName) {
    Set<MethodMetadata> annotatedMethods = null;
    if (AnnotationUtils.isCandidateClass(getIntrospectedClass(), annotationName)) {
        try {
            // 1. 通过反射类的获取所有的方法
            Method[] methods = ReflectionUtils.getDeclaredMethods(getIntrospectedClass());
            for (Method method : methods) {
                // 2. 判断是否有 @Bean 注解
                if (isAnnotatedMethod(method, annotationName)) {
                    if (annotatedMethods == null) {
                        annotatedMethods = new LinkedHashSet<>(4);
                    }
                    annotatedMethods.add(new StandardMethodMetadata(
                        method, this.nestedAnnotationsAsMap));
                }
            }
        }
        catch (Throwable ex) {
            throw new IllegalStateException(。。。);
        }
    }
    return annotatedMethods != null ? annotatedMethods : Collections.emptySet();
}

这个方法很好理解,关键就两步:

  1. 通过反射获取类中所有方法;
  2. 遍历得到的方法,逐一判断该方法是否有@Bean注解;

到了这里,beanConfigs中的两个方法终于获取到了(保存在ConfigurationClass对象的beanMethods属性):

3.4 将被@Bean标记的方法加载到BeanDefinitionMap

上面获取到的beanMethod此时还只是在ConfigurationClass对象的beanMethods属性中,并没有加载到beanFactoryBeanDefinitionMap中,本小节来探究下它们是何时放入到BeanDefinitionMap中。

还记得ConfigurationClassPostProcessor#processConfigBeanDefinition吗,其中有这么一行代码:

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    ...
    // 处理本次解析的类
    // 把 @Import 引入的类、配置类中带@Bean的方法、@ImportResource 引入的资源等转换成BeanDefinition
    this.reader.loadBeanDefinitions(configClasses);
    ...
}

这就是加载引入的BeanDefinition的地方,即把 @Import 引入的类、配置类中带@Bean的方法、@ImportResource 引入的资源等转换成BeanDefinition,本文重点关注@Bean方法的处理,代码如下:

ConfigurationClassBeanDefinitionReader

    public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
        TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
        // 遍历处理传入的 configurationModel
        for (ConfigurationClass configClass : configurationModel) {
            loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
        }
    }

    /**
     * 这个方法会加载各种 ConfigurationClass 引入的 BeanDefinition
     * 1. @Import 引入的类
     * 2. 配置类中的 @Bean 方法
     * 3. @ImportResource 引入的资源
     * 4. @Import 引入的 ImportBeanDefinitionRegistrar
     */
    private void loadBeanDefinitionsForConfigurationClass(
            ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

        if (trackedConditionEvaluator.shouldSkip(configClass)) {
            ...
        }

        // 处理 @Import 引入的配置类
        if (configClass.isImported()) {
            registerBeanDefinitionForImportedConfigurationClass(configClass);
        }
        // 处理 @Bean 方法
        for (BeanMethod beanMethod : configClass.getBeanMethods()) {
            loadBeanDefinitionsForBeanMethod(beanMethod);
        }
        // 处理 @ImportResource 引入的资源
        loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
        // 处理 @Import 引入的 ImportBeanDefinitionRegistrar
        loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
    }

    /**
     * 处理 @Bean 方法
     * 1. 创建BeanDefinition,beanMethod 使用的是 ConfigurationClassBeanDefinition
     * 2. 处理 @Bean 的各种属性,设置到 BeanDefinition 中
     * 3. 将 BeanDefinition 注册到 beanFactory 中
     */
    private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
        ConfigurationClass configClass = beanMethod.getConfigurationClass();
        MethodMetadata metadata = beanMethod.getMetadata();
        String methodName = metadata.getMethodName();

        ...

        // 1. beanMethod使用的是ConfigurationClassBeanDefinition
        ConfigurationClassBeanDefinition beanDef = 
                new ConfigurationClassBeanDefinition(configClass, metadata);
        beanDef.setResource(configClass.getResource());
        beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));

        // 2. 处理 @Bean 的各种属性
        if (metadata.isStatic()) {
            // 静态 @Bean 方法
            if (configClass.getMetadata() instanceof StandardAnnotationMetadata) {
                beanDef.setBeanClass(
                    ((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass());
            }
            else {
                beanDef.setBeanClassName(configClass.getMetadata().getClassName());
            }
            beanDef.setUniqueFactoryMethodName(methodName);
        }
        else {
            // 普通的 @Bean 方法
            beanDef.setFactoryBeanName(configClass.getBeanName());
            beanDef.setUniqueFactoryMethodName(methodName);
        }

        if (metadata instanceof StandardMethodMetadata) {
            beanDef.setResolvedFactoryMethod(
                ((StandardMethodMetadata) metadata).getIntrospectedMethod());
        }

        beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
        beanDef.setAttribute(
                org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor.
                SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);

        AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);

        Autowire autowire = bean.getEnum("autowire");
        if (autowire.isAutowire()) {
            beanDef.setAutowireMode(autowire.value());
        }

        boolean autowireCandidate = bean.getBoolean("autowireCandidate");
        if (!autowireCandidate) {
            beanDef.setAutowireCandidate(false);
        }

        String initMethodName = bean.getString("initMethod");
        if (StringUtils.hasText(initMethodName)) {
            beanDef.setInitMethodName(initMethodName);
        }

        String destroyMethodName = bean.getString("destroyMethod");
        beanDef.setDestroyMethodName(destroyMethodName);

        ...

        // 3. 将BeanDefinition注册到beanFactory中
        this.registry.registerBeanDefinition(beanName, beanDefToRegister);
    }

ConfigurationClassBeanDefinitionReader#loadBeanDefinitions 又调用了两个方法,最终处理是在ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod,这部分逻辑如下:

  1. 遍历传入的configClass集合,对每个configClass进行Definitions加载处理;
  2. configClass进行Definitions加载处理时,会逐一处理@Import/@Bean/@ImportResource等注解,本小节我们仅关注@Bean的处理;
  3. 处理 @Bean 方法时,先创建BeanDefinition(@Bean方法对应的BeanDefinitionConfigurationClassBeanDefinition),然后解析@Bean的属性,设置到BeanDefinition中,最终是把BeanDefinition注册到beanFactory中.

好 了,执行完ConfigurationClassBeanDefinitionReader#loadBeanDefinitions后,BeanDefinition就加载到beanFactory了,对应的BeanDefinition类型是ConfigurationClassBeanDefinition.

3.5 @Bean 创建实例

实例的创建流程同普通的@Component类一致,不同的是普通的@Component类调用的是构造方法,而 @Bean使用的是factoryMethod,代码如下:

AbstractAutowireCapableBeanFactory#createBeanInstance

/**
 * 实例的创建方式
 * 1. 使用 instanceSupplier,Supplier是java8提供的类,可以传入一个lambda表达式
 * 2. 使用工厂方法,如 @Bean 注解对应的方法
 * 3. 使用的是构造方法注入,即构造方法上有 @Autowired 注解
 * 4. 构造方法注入,可以是无参构造,也可以是有参构造
 *
 */
protected BeanWrapper createBeanInstance(String beanName, 
        RootBeanDefinition mbd, @Nullable Object[] args) {
    // 确保已经加载了此 class
    Class<?> beanClass = resolveBeanClass(mbd, beanName);
    ...
    
    // 是否设置了bean创建的Supplier,Supplier是java8提供的类,可以传入一个lambda表达式
    // 调用 AbstractBeanDefinition#setInstanceSupplier 指定
    Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
    if (instanceSupplier != null) {
        return obtainFromSupplier(instanceSupplier, beanName);
    }
    if (mbd.getFactoryMethodName() != null) {
        // 采用工厂方法实例化
        return instantiateUsingFactoryMethod(beanName, mbd, args);
    }
    // 是否第一次
    boolean resolved = false;
    // 是否采用构造函数注入
    boolean autowireNecessary = false;
    ...
    if (resolved) {
        if (autowireNecessary) {
            return autowireConstructor(beanName, mbd, null, null);
        }
        else {
            // 无参构造函数
            return instantiateBean(beanName, mbd);
        }
    }
    // Candidate constructors for autowiring?
    // 判断是否采用有参构造函数
    Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(
            beanClass, beanName);
    if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
            mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
        return autowireConstructor(beanName, mbd, ctors, args);
    }
    ctors = mbd.getPreferredConstructors();
    if (ctors != null) {
        // 构造函数依赖注入
        return autowireConstructor(beanName, mbd, ctors, null);
    }
    // 调用无参构造函数
    return instantiateBean(beanName, mbd);
}

从代码上来看,spring实例对象的方式有4种:

  1. 使用 instanceSupplierSupplierjava8提供的类,可以传入一个lambda表达式
  2. 使用工厂方法,如 @Bean 注解对应的方法
  3. 使用的是构造方法注入,即构造方法上有 @Autowired 注解
  4. 构造方法注入,可以是无参构造,也可以是有参构造

这里我们主要关注@Bean的实例方式,也就是工厂方法实例化方式,我们进去看下:

public BeanWrapper instantiateUsingFactoryMethod(String beanName, 
             RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
    BeanWrapperImpl bw = new BeanWrapperImpl();
    this.beanFactory.initBeanWrapper(bw);
    Object factoryBean;
    Class<?> factoryClass;
    boolean isStatic;
    String factoryBeanName = mbd.getFactoryBeanName();
    if (factoryBeanName != null) {
        factoryBean = this.beanFactory.getBean(factoryBeanName);
        factoryClass = factoryBean.getClass();
        isStatic = false;
    }
    ...

    Method factoryMethodToUse = null;
    ArgumentsHolder argsHolderToUse = null;
    Object[] argsToUse = null;
    // 处理 factoryMethod 的参数
    if (explicitArgs != null) {
        argsToUse = explicitArgs;
    }
    else {
        ...
    }

    if (factoryMethodToUse == null || argsToUse == null) {
        factoryClass = ClassUtils.getUserClass(factoryClass);
        List<Method> candidates = null;
        if (mbd.isFactoryMethodUnique) {
            if (factoryMethodToUse == null) {
                factoryMethodToUse = mbd.getResolvedFactoryMethod();
            }
            if (factoryMethodToUse != null) {
                candidates = Collections.singletonList(factoryMethodToUse);
            }
        }
        // 省略了好多代码
        ...
    }
    bw.setBeanInstance(instantiate(beanName, mbd, 
            factoryBean, factoryMethodToUse, argsToUse));
    return bw;
}

以上方法是经精简后的代码,原本方法代码比较多,大部分代码是在在处理argsToUsefactoryMethodToUse参数,细节非常多,就不展开分析了,这里我们主要关注以下几个变量:

  1. factoryBean@Bean方法所在类的实例,这里是beanConfig;
  2. factoryMethodToUse: 实例化所用的方法,也就是被 @Bean 注解的方法,这里是BeanConfigs#beanObj1;
  3. argsToUse:被 @Bean 注解的方法要用的参数,由于BeanConfigs#beanObj1没有指定参数,这里是null;

这三个变量是用来进行实例化的变量,实例化方式大致也能想到了,实例、方法及方法参数都有了,接下来就是调用反射进行实例化了:

ConstructorResolver#instantiate(...)

private Object instantiate(String beanName, RootBeanDefinition mbd,
        @Nullable Object factoryBean, Method factoryMethod, Object[] args) {
    try {
        
        return this.beanFactory.getInstantiationStrategy().instantiate(
                mbd, beanName, this.beanFactory, factoryBean, factoryMethod, args);
    }
    catch (Throwable ex) {
        ...
    }
}

SimpleInstantiationStrategy#instantiate(...)

@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
        @Nullable Object factoryBean, final Method factoryMethod, Object... args) {
    try {
        ...
        try {
            currentlyInvokedFactoryMethod.set(factoryMethod);
            // 这里就相当于调用 beanConfigs.beanObj1()
            Object result = factoryMethod.invoke(factoryBean, args);
            if (result == null) {
                result = new NullBean();
            }
            return result;
        }
        finally {
            ...
        }
    }
    catch (...) {
        ...
    }
}

最终是在SimpleInstantiationStrategy#instantiate(...)方法中进行反射实例化了!

实例化完成后,后面依赖注入、初始化等都与普通的spring bean一致,这里就不再分析了。

3.6 @Configuration@Bean 组合使用

上面介绍了@Component@Bean使用时的代码分析,即

@Component
public class BeanConfigs {
    @Bean
    public Xxx xxx() {
        ...
    }
}

实际上,大多数情况下我们使用的是@Configuration@Bean的组合:

@Configuration
public class BeanConfigs {
    @Bean
    public Xxx xxx() {
        ...
    }
}

这与我们前面使用的@Component有何差别呢?本节我们就来分析下。

1. demo 准备

demo准备:

//@Component
@Configuration
public class BeanConfigs {

    @Bean
    public BeanObj1 beanObj1() {
        return new BeanObj1();
    }

    @Bean
    public BeanObj2 beanObj2() {
        // 这里调用下 beanObj1() 方法
        beanObj1();
        return new BeanObj2();
    }

}

这个demo仅仅只是将@Component替换为@Configuration,执行下,结果如下:

调用beanObj1的构造方法
调用beanObj2的构造方法
obj1:BeanObj1{}
obj2:BeanObj2{}
org.springframework.learn.explore.demo02.BeanConfigs$$EnhancerBySpringCGLIB$$dca1c55b@75c072cb

看出区别了吗,我们将之前的执行也放在这里:

经过比较,发现有两处不同:

  1. beanObj1的构造方法仅调用了一次;
  2. beanConfigs对应的类是BeanConfigs$$EnhancerBySpringCGLIB$$dca1c55b@75c072cb,说明它是一个代理对象,使用了cglib代理。

实际上,以上两个不同点都可以归结为一个原因:spring对beanConfigs进行了代理,调用BeanConfigs#beanObj1实际调用的是代理方法,即spring会对被@Configuration标记的类进行cglib代理

那么代理是怎么创建及运行的呢?我们继续探究。

2. 代理类的创建:ConfigurationClassPostProcessor#enhanceConfigurationClasses

上一篇文章的开篇,我们就提到过ConfigurationClassPostProcessor在执行时会调用到两个方法:processConfigBeanDefinitions(...)enhanceConfigurationClasses(...)processConfigBeanDefinitions(...) 引起了spring对@Import@Configuration等注解的解析,前面已经分析过了;而enhanceConfigurationClasses(...)就是被@Configuration标记的类代理产生的关键所在!

enhanceConfigurationClasses(...) 方法代码如下:

ConfigurationClassPostProcessor#enhanceConfigurationClasses

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
    Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
    for (String beanName : beanFactory.getBeanDefinitionNames()) {
        BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
        Object configClassAttr = beanDef.getAttribute(
            ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
        ...
        // 1. 判断是否为一个全配置类
        if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
            ...
            configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
        }
    }
    // 全配置类:处理代理
    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);
        // 处理 BeanClass
        Class<?> configClass = beanDef.getBeanClass();
        // 2. 生成 enhancedClass
        Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
        if (configClass != enhancedClass) {
            // 3. 设置 BeanClass,值为enhancedClass
            beanDef.setBeanClass(enhancedClass);
        }
    }
}

这个方法比较简单,步骤如下:

  1. 判断配置类是否为全配置类,在在上一篇文章文章中,我们提到spring会把带有 @Configuration 注解且 proxyBeanMethods != false 的类标记为Full配置类,这里正是根据前面的标记来判断否为全配置类,很明显,此时的beanConfigs就是一个全配置类;
  2. 对全配置类,会根据其configClass生成对应的enhancedClass
  3. 将生成的enhancedClass设置到beanDefinitionbeanClass中。

执行完此方法后,beanConfigs 对应的beanDefinitionbeanClass就是代理类了:

后面创建的beanConfigs就是这个代理类的实例了。

3. 执行代理对象的方法

生成代理对象后,代理方法是如何执行的呢?即spring是如何执行beanConfigs.beanObj1()的呢?说起这个,就需要谈到cglib代理对象的方法执行了。我们直接来看代理的生成,进入enhancer.enhance(configClass, this.beanClassLoader)

public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
    if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {
        return configClass;
    }
    // newEnhancer(...) 方法才是关键
    Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
    return enhancedClass;
}

继续进入:

private static final Callback[] CALLBACKS = new Callback[] {
        // 这个类用来保证 @Bean 方法的单例
        new BeanMethodInterceptor(),
        new BeanFactoryAwareMethodInterceptor(),
        NoOp.INSTANCE
};

// 生成 CallbackFilter,传入的对象为Callback
private static final ConditionalCallbackFilter CALLBACK_FILTER 
        = new ConditionalCallbackFilter(CALLBACKS);

// 生成cglib增强
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(configSuperClass);
    enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
    enhancer.setUseFactory(false);
    enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
    enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
    // 代理部分在callbackFilter
    enhancer.setCallbackFilter(CALLBACK_FILTER);
    enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
    return enhancer;
}

关于cglib代理的内容,在spring aop之cglib 代理一文中已详细分析过了,这不再分析,我们直接说结论:cglib 执行代理方法时,执行的是 EnhancercallbackFilter 属性的MethodInterceptor#intercept方法,即CALLBACKS数组中的BeanMethodInterceptor,下面我们就来看下它的内容:

ConfigurationClassEnhancer.BeanMethodInterceptor

private static class BeanMethodInterceptor implements MethodInterceptor, ConditionalCallback {
    @Override
    @Nullable
    public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
                MethodProxy cglibMethodProxy) throws Throwable {
        ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
        String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
        ...
        // 如果是调用当前的 factoryMethod 方法,直接调用父类的方法
        if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
            // 调用父类的方法,也就是目标方法
            return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
        }
        // 否则直接获取 beanFactory中已有的对象
        return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
    }

    private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,
            ConfigurableBeanFactory beanFactory, String beanName) {
        try {
            ...
            // 调用的的是 beanFactory.getBean(...) 方法,这个方法我们已经非常熟悉了
            Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) :
                    beanFactory.getBean(beanName));
            ...
            return beanInstance;
        }
        finally {
            ...
        }
    }

    /**
     * 判断能否执行当前 MethodInterceptor
     */
    @Override
    public boolean isMatch(Method candidateMethod) {
        return (candidateMethod.getDeclaringClass() != Object.class &&
                !BeanFactoryAwareMethodInterceptor.isSetBeanFactory(candidateMethod) &&
                BeanAnnotationHelper.isBeanAnnotated(candidateMethod));
    }
}

BeanMethodInterceptor 实现了 MethodInterceptorConditionalCallbackConditionalCallback#isMatch用来判断当前MethodInterceptor能否执行,MethodInterceptor#intercept就是执行的方法内容,执行逻辑为:

  1. 如果是直接调用当前的 factoryMethod 方法,直接调用父类的方法,也就是beanConfigs.beanObj1(),这个过程会实例化beanObj1时被调用;
  2. 如果不是直接调用当前的 factoryMethod 方法(比如在别的方法中调用),则调用beanFactory.getBean(...)获取bean,这个过程会在实例化beanObj1时会 被调用。

以上就是为什么beanObj1的构造方法只调用了一次,以及为什么beanConfigs是代理类的原因所在了。

最后再提一句,@Configuration 提供了proxyBeanMethods()方法来让我们选择是否开启配置类的代理,默认值是true,如果像这样设置:

@Configuration(proxyBeanMethods=false)
public class BeanConfigs {
    ...
}

BeanConfigs就不会进行代理了,运行结果同@Component注解一样,这里就不展示了。

4. 几个小问题

1. 内部方法调用也能被代理吗?

在示例中,我们是这样调用的:

    @Bean
    public BeanObj2 beanObj2() {
        // 这里调用下 beanObj1() 方法
        beanObj1();
        return new BeanObj2();
    }

即在beanObj2()中调用了beanObj1(),这明显是内部方法调用,beanObj1()也能被代理吗?

回答:cglib代理的调用方法有两种:

@Override
public Object intercept(Object proxyObj, Method method, Object[] objects, 
            MethodProxy proxy) throws Throwable {
    // 方案1: 使用目标对象,直接调用目标对象的方法
    // return proxy.invoke(target, objects);
    // 方案2: 使用代理对象,调用其父类的方法
    return proxy.invokeSuper(proxyObj, objects);
}

beanObj2()的调用使用是方案2,也就是使用代理对象调用beanObj2()beanObj2()this为代理对象:

因此在beanObj2()中直接调用beanObj1(),就相当于使用代理对象调用beanObj1(),当然能被代理了。

在我们印象中,方法的内部调用不能被代理,那是因为spring在处理Aop时,使用的是方案1的调用方式,此时的 this 为原始对象,当然不能被代理了。

2. 私有属性如何注入?

比如,我们现在有一个BeanObj3:

@Component
public class BeanObj3 {

    public BeanObj3() {
        System.out.println("调用beanObj3的构造方法");
    }

    @Override
    public String toString() {
        return "BeanObj3{}";
    }
}

然后在BeanConfigs中注入:

@Configuration
public class BeanConfigs {

    @Autowired
    private BeanObj3 beanObj3;

    @Bean
    public BeanObj1 beanObj1() {
        return new BeanObj1();
    }

    @Bean
    public BeanObj2 beanObj2() {
        // 这里调用下 beanObj1() 方法
        beanObj1();
        System.out.println("beanObj3:" + this.beanObj3);
        return new BeanObj2();
    }

}

BeanConfigs中自动注入了beanObj3属性,然后在beanObj2()中又打印了beanObj3属性。运行,结果如下:

可以看到,注入的beanObj3也能获取到了。这里就有个问题了:beanObj3是属于目标对象的,而this是代理对象,难不成代理对象能拿到目标对象的私有属性?

首先,添加到beanFactorybeanDefinitionMap中的类是BeanConfigs$$EnhancerBySpringCGLIB$$Xxx类(代理类),而不是BeanConfigs,spring在进行属性注入时,会查找当前类及其父类的所有等注入属性进行注入,因此,虽然添加到spring容器中的 是 BeanConfigs$$EnhancerBySpringCGLIB$$Xxx类,但BeanConfigs中的beanObj3一样会被注入,至于原因嘛,由于cglib的代理关系,BeanConfigsBeanConfigs$$EnhancerBySpringCGLIB$$Xxx的父类。

BeanConfigs$$EnhancerBySpringCGLIB$$Xxx会继承beanObj3属性吗?这里直接看运行结果吧:

这是最终得到的beanConfigs对象,可以看到,它里面就有一个beanObj3属性,并且还有值。

3.7 总结

本文主要分析了ConfigurationClassPostProcessor处理 @Bean注解的过程,总结如下:

  1. 解析配置类,通过反射获取配置类里所有被@Bean标记的方法;
  2. 遍历这些方法,将其封装成一个个BeanDefinition注册到beanFactory中,对应BeanDefinition具体类型为ConfigurationClassBeanDefinition
  3. 如果配置类是全配置类,会对配置类进行cglib代理;
  4. 实例化时,使用反射调用对应的方法生成实例(得到实例后,spring会再对其进行依赖注入、初始化等);
  5. 在别的@Bean方法中调用当前@Bean方法时,如果当前@Bean方法所在的类是全配置类,则会去beanFactory中查找对应的bean(查找的过程是,找到则返回,找不到则创建再返回,返回的bean有完整的spring bean的生命周期),这个操作是由cglib代理完成;如果当前@Bean方法所在的类不是全配置类,则会按照普通的方法调用,生成bean的实例返回(返回的bean没有完整的spring bean的生命周期)。

本文的分析就到这里了,接下来我们继续分析ConfigurationClassPostProcessor处理其他注解的流程。


本文原文链接:my.oschina.net/funcy/blog/… ,限于作者个人水平,文中难免有错误之处,欢迎指正!原创不易,商业转载请联系作者获得授权,非商业转载请注明出处。

本系列的其他文章

【spring源码分析】spring源码分析系列目录