@Configuration注解源码(一)

975 阅读15分钟

基本用法

关于@Configuration注解的使用,可以参考 spring的官网说明,这里不详细说明了。

前置概念

在分析@Configuration的源码之前,先了解几个源码中涉及到的处理方式。

什么是配置类

我们知道,被@Configuration修饰的类,充当了配置类的角色,实际上除了@Configuration可以指定配置类外,还有以下几种情况下,注入的类,也可以认为是配置类:

  1. 当一个类被@Configuration、@Component、@ComponentScan、@Import、@ImportResource修饰时
  2. 或者类中有@Bean注解修饰的方法时,Spring就认为这个类是一个配置类

ConfigurationClassPostProcessor

@Configuration注解的处理,是通过增强器ConfigurationClassPostProcessor实现的,该Processor是在PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(..)方法中注册进来并执行的。关于该方法的说明,前面的文章有过说明。

看一下该类的声明:

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor和,
		PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
    // ......                
}

通过该类的定义,可以发现,ConfigurationClassPostProcessor同时实现了BeanDefinitionRegistryPostProcessor和PriorityOrdered接口,这在下面的源码分析中会看到。

full和lite

前面说了除了@Configuration注入的类,是一个配置类外,还有其他两种方式,区别在于:

  • @Configuration修饰的配置类,在源码中通过一个属性值full来标识,表示完整的配置类
  • 其他两种方式修饰的配置类,在源码中,则是通过属性值为lite来标识,表示为一个精简版的配置类

源码分析

通过一个测试代码,来跟踪源码,了解@Configuration的实现原理

配置类:MyConfig.java

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfig {
    @Bean
    public Person person() {
        return new Person();
    }
}

注入类:Person.java

public class Person {
    public Person() {
    }
}

测试主类:ApplicationSimpleConfiguration.java

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class ApplicationSimpleConfiguration {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        System.out.println(context.getBean("person"));
    }
}

以上代码即为@Configuration的最基本使用,下面来分析源码

前面说过,@Configuration是由ConfigurationClassPostProcessor处理的,而这个Processor是在PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(..)中注册的,具体是在处理BeanDefinitionRegistryPostProcessor的逻辑块里,第一次调用invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);方法时候,会去处理,代码如下(简化处理):

# PostProcessorRegistrationDelegate.java

public static void invokeBeanFactoryPostProcessors(
                ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

    Set<String> processedBeans = new HashSet<>();

    if (beanFactory instanceof BeanDefinitionRegistry) {
        
        // ... 省略其他代码
        

        // First, invoke ...
        // 在此处找到ConfigurationClassPostProcessor, 并存储到数组中
        // 关于ConfigurationClassPostProcessor什么时候存储起来的,下面会详细分析
        // [1]
        String[] postProcessorNames = 
            beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);  

        for (String ppName : postProcessorNames) {

            if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                // 通过getBean, 实例化了ConfigurationClassPostProcessor到容器中
                currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                processedBeans.add(ppName);
            }
        }
        sortPostProcessors(currentRegistryProcessors, beanFactory);
        registryProcessors.addAll(currentRegistryProcessors);

        // 完成调用处理逻辑
        // 本篇重点说明的地方,对于使用基于@Configuration注解和@bean注入的对象, 此时的processor类型为ConfigurationClassPostprocessor, 且在此处执行
        
        // [2]
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);              

        currentRegistryProcessors.clear();

        // ... 省略其他与本篇内容不相关的代码
    } else {
        // ... 省略其他与本篇内容不相关的代码
    }
    
    // ... 省略其他与本篇内容不相关的代码
}

上面贴出的源码中标注 [1][2] 是需要重点留意的。
先看 [1] 处的这行代码:

String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);

这行代码会获取到名称为:org.springframework.context.annotation.internalConfigurationAnnotationProcessor的增强器。 其实该字符串就是对应了前面说到的ConfigurationClassPostProcessor,那这里获取到的这个字符串值,是在哪里加入进去的?
这个字符串定义在了抽象类AnnotationConfigUtils中,具体代码如下:

public abstract class AnnotationConfigUtils {

    // ... 省略其他代码

    public static final String CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME = "org.springframework.context.annotation.internalConfigurationAnnotationProcessor";

    public static final String AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME =
		"org.springframework.context.annotation.internalAutowiredAnnotationProcessor";

    public static final String COMMON_ANNOTATION_PROCESSOR_BEAN_NAME =
		"org.springframework.context.annotation.internalCommonAnnotationProcessor";

    public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
                            BeanDefinitionRegistry registry, @Nullable Object source) {

        DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
        if (beanFactory != null) {
            if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
                    beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
            }
            if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
                    beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
            }
        }

        Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);


        // 注册了一个ConfigurationClassPostProcessor, 支撑@Configuration相关的注解
        // 其实就是将ConfigurationClassPostProcessor包装成BeanDefinition, 并注册到容器中
        // [3]
        if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {   
            RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
        }

        // 注册了一个AutowiredAnnotationBeanPostProcessor, 用来处理@Autowire,@Value注解的
        if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
        }

        // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
        // 这里是支撑JSR-250规范的@Resource、@PostConstruct、@PreDestroy注解的
        if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
        }

        // 这里是支持注解形式的jpa的BeanPostProcessor
        if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition();
            try {
                    def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
                                    AnnotationConfigUtils.class.getClassLoader()));
            }
            catch (ClassNotFoundException ex) {
                    throw new IllegalStateException(
                                    "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
            }
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
        }

        // 支撑spring-event相关注解的processor,对@EventListener的支撑
        if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
        }

        // 支撑spring-event相关注解的processor,对@EventListener的支撑
        if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
        }

        return beanDefs;
    }
}    

在上面的源码中,registerAnnotationConfigProcessors 方法中注册了很多PostProcessor,都是和注解相关的处理器,如本文所说的 @Configuration,以及 @Autowire@Value@Resource 等注解。通过源码,也可以发现 @Autowire@Resource 的底层实现分别基于 AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor

这里插入一下:registerAnnotationConfigProcessors(..)是在AnnotationConfigApplicationContext的构造函数中调用的,具体链路如下(从上往下的调用顺序):

// 入口测试主类
public class ApplicationSimpleConfiguration {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        System.out.println(context.getBean("person"));
    }
}

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
    // this中注册了上面的PostProcessor
    this();
    register(componentClasses);
    refresh();
}

// this()进入
public AnnotationConfigApplicationContext() {
    // 正如前面推测的那样, 基于注解方法使用的AnnotatedBeanDefinitionReader
    // 基于xml方式的注入, 则是使用XmlBeanDefinitionReader进行处理的
    this.reader = new AnnotatedBeanDefinitionReader(this);

    // 将默认的过滤器 AnnotationTypeFilter 注册到 includeFilter 集合中, 
    // 过滤器指定的注解我们关注的主要是 Component, 当然也包括Named等
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}

// 还是通过构造函数进入
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    Assert.notNull(environment, "Environment must not be null");
    this.registry = registry;
    this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);

    // 在这里注册了注解相关的几个PostProcessor
    AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

继续来看上面贴出的AnnotationConfigUtils这个类的源码,注意贴出的源码上面标注的 [3] 处的代码,这就是本篇提到的ConfigurationClassPostProcessor,在 [3] 中 if 块里,创建了一个 BeanDefinition,class类型即为ConfigurationClassPostProcessor,并且会注册到工厂的容器,所以在beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);获取的时候,是可以获取到这个增强器的。

至此,PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(..)代码中 [1] 处的处理已经说完了。下面聊聊 [2] 处的代码,这里是主要的地方:

// [2]
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);     

进入调用逻辑,还是当前类中:

# PostProcessorRegistrationDelegate.java

private static void invokeBeanDefinitionRegistryPostProcessors(
			Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
    for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
        postProcessor.postProcessBeanDefinitionRegistry(registry);
    }
}

这里的postProcessors只包含了上文所说的 ConfigurationClassPostProcessor 一个对象,重点看ConfigurationClassPostProcessor这个类的postProcessBeanDefinitionRegistry(registry)方法:

# ConfigurationClassPostProcessor.java
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    // 标识符, 用来标识防止重复处理, 不重要
    int registryId = System.identityHashCode(registry);
    if (this.registriesPostProcessed.contains(registryId)) {
            throw new IllegalStateException(
                            "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
    }
    if (this.factoriesPostProcessed.contains(registryId)) {
            throw new IllegalStateException(
                            "postProcessBeanFactory already called on this post-processor against " + registry);
    }
    this.registriesPostProcessed.add(registryId);

    // 在这里处理@configure
    processConfigBeanDefinitions(registry);
}

核心就是最后一行代码,继续跟踪:

# ConfigurationClassPostProcessor.java

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    // 用来存储未被处理过的配置类
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    
    // 该数组这里一共有六个元素, 即前面所说的AnnotationConfigUtils中的5个注解支撑类, 以及配置类MyConfig
    String[] candidateNames = registry.getBeanDefinitionNames();
    
    // 从候选对象中, 找出所有的配置类, 如果是配置类, 则加入到配置类候选集合configCandidates中
    for (String beanName : candidateNames) {
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);
        // 如果已经被处理过, 打印一行日志即可, 否则需要检验是不是配置类
        if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
           //打印日志, 略
        }
        // !!!判断当前beanDefinition是不是一个配置类, 如果是, 收集起来, 并且设置beanDef的属性值
        // check方法下面会单独说明
        else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
            // 如果是一个需要处理的配置类, 加入列表
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }
    // 配置类候选集合为空, 说明没有找到配置类, 结束即可
    if (configCandidates.isEmpty()) {
        return;
    }

    // 如果存在排序, 则按照@Order标注的顺序对配置类进行排序
    configCandidates.sort((bd1, bd2) -> {
            int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
            int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
        return Integer.compare(i1, i2);
    });

    // 这里主要是初始化一些工具类, 环境变量之类的
    SingletonBeanRegistry sbr = null;
    if (registry instanceof SingletonBeanRegistry) {
        sbr = (SingletonBeanRegistry) registry;
        if (!this.localBeanNameGeneratorSet) {
            // 命名生成器, 不是很重要
            BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
                                AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
            if (generator != null) {
                this.componentScanBeanNameGenerator = generator;
                this.importBeanNameGenerator = generator;
            }
        }
    }
    if (this.environment == null) {
        this.environment = new StandardEnvironment();
    }

    // 准备解析上面找出的所有的配置类
    
    // 创建ConfigurationClassParser实例, 委托其进行配置类的解析操作
    ConfigurationClassParser parser = new ConfigurationClassParser(
                    this.metadataReaderFactory, this.problemReporter, this.environment,
                    this.resourceLoader, this.componentScanBeanNameGenerator, registry);

    // 转换类型, 从ArrayList —> Set, 可以去重
    // 这里实际上只有一个配置类, 即自定义的MyConfig类
    Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
    // 解析过的, 就存储到该集合中
    Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
    
    // 真正开始解析工作了, 可以发现这是一个循环。实际上,这里面包含了循环和递归, 后面会给出一张流程图,防止绕来绕去,绕晕了。
    do {
        // !!!委托给Parser来解析这些配置类!!!
        // 这里主要是把配置类上的注解信息解析封装成ConfigurationClass对象
        // 如果是配置类导入(import/componentScan等)的普通类(非配置类), 将会在这里生成beanDefinition并注册
        parser.parse(candidates);

        // 校验扫描出来的beanDefinitionu是否合法, 这里其实主要是校验
        // 1.proxyBeanMethods=true的情况下配置类是否可以重写(非final, 需要生成cglib代理类)
        // 2.@Bean修饰的方法是否可以重写(非final,需要生成cglib代理类)
        parser.validate();

        // 获取所有解析出来的配置类
        Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
        // 在注册BeanDefinition前, 先移除掉configClasses中已经处理过的对象
        configClasses.removeAll(alreadyParsed);

        // 初始化一个ConfigurationClassBeanDefinitionReader
        if (this.reader == null) {
            this.reader = new ConfigurationClassBeanDefinitionReader(
                            registry, this.sourceExtractor, this.resourceLoader, this.environment,
            this.importBeanNameGenerator, parser.getImportRegistry());
        }

        // !!!把封装好的ConfigurationClass对象委托给BeanDefinitionReader处理!!!
        // 通过配置类加载注册beanDefinition, 以前的文章有过详细说明, 这里不说了
        this.reader.loadBeanDefinitions(configClasses);

        // 加入到alreadyParsed集合中, 表示处理完了, 避免重复注册
        alreadyParsed.addAll(configClasses);

        candidates.clear();

        // 处理完ConfigurationClass后, 可能会注册新的配置类, 这里就是收集这些新注册的配置类的
        if (registry.getBeanDefinitionCount() > candidateNames.length) {
            String[] newCandidateNames = registry.getBeanDefinitionNames();
            Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
            Set<String> alreadyParsedClasses = new HashSet<>();
            for (ConfigurationClass configurationClass : alreadyParsed) {
                alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
            }
            for (String candidateName : newCandidateNames) {
                // 只有新注册的beanDefinition才需要处理
                if (!oldCandidateNames.contains(candidateName)) {
                    BeanDefinition bd = registry.getBeanDefinition(candidateName);
                    // 是否是配置类且未处理过
                    if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
                                    !alreadyParsedClasses.contains(bd.getBeanClassName())) {
                            candidates.add(new BeanDefinitionHolder(bd, candidateName));
                    }
                }
            }
            candidateNames = newCandidateNames;
        }
    }
    // 循环, 直到没有新增的配置类为止
    while (!candidates.isEmpty());

    // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
    if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
        sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
    }

    if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
        // Clear cache in externally provided MetadataReaderFactory; this is a no-op
        // for a shared cache since it'll be cleared by the ApplicationContext.
        ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
    }
}

上面的代码比较长,详细的说明,都写在了源码中,下面简单总结这个方法:

  • 从容器中得到所有的候选类
  • 从候选类中, 检测出所有的配置类
  • 遍历所有筛选出来的的配置类,解析、校验、注册BeanDefinition到容器中
  • 处理在执行过程中可能又引入的新的配置类 到此为止,以上所有的代码可以用一张流程图简单概括一下:

未命名文件.jpg

还剩下两个方法需要重点说明,对应图片中红色背景的两个方法。
先看第一个方法 checkConfigurationClassCandidate()

public static boolean checkConfigurationClassCandidate(
			BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
		
    String className = beanDef.getBeanClassName();
    
    // [4]
    // 如果指定了factoryMethodName, 也不算做配置类,下面会解释
    if (className == null || beanDef.getFactoryMethodName() != null) {
        return false;
    }

    // 这个AnnotationMetadata能拿到类上的所有注解的信息就可以了

    // 最后beanDef对应类上的注解信息都会存储到这个metadata中
    AnnotationMetadata metadata;

    // 初始化metadata, 根据不同的BeanDefinition类型, 进入不同的分支
    // 这里MyConfig会进入 if 分支
    if (beanDef instanceof AnnotatedBeanDefinition &&
                className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
        // Can reuse the pre-parsed metadata from the given BeanDefinition...
        // 复用
        metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
    }
    else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
        Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
        // A.isAssignableFrom(B.class), 即 A是不是B的父类或者相同类型
        // 例如Object.class.isAssignableFrom(xxx.getClass())都是true, 因为Object是所有类的父类
        // 如果是这4个类的派生子类, 则也不能算作配置类
        if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||
                BeanPostProcessor.class.isAssignableFrom(beanClass) ||
                AopInfrastructureBean.class.isAssignableFrom(beanClass) ||
                EventListenerFactory.class.isAssignableFrom(beanClass)) {
                return false;
        }
        metadata = AnnotationMetadata.introspect(beanClass);
    }
    else {
        try {
            MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
            metadata = metadataReader.getAnnotationMetadata();
        }
        catch (IOException ex) {
            if (logger.isDebugEnabled()) {
                logger.debug("Could not find class file for introspecting configuration annotations: " +
                                    className, ex);
            }
            return false;
        }
    }

    // 获取beanDef对应类上的@Configuration注解的属性:value和proxyBeanMethods
    Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());

    // 有@Configuration注解且注解的proxyBeanMethods=true(这个是默认值)
    // 这里简单说明一下proxyBeanMethods:是@Configuration的属性, 默认true, 表示使用用CGlib代理@Bean修饰的方法, 否则, Spring不会代理配置类内部@Bean修饰的方法所返回的实例

    // [5]
    // 关于这里if和else的处理区别, 前面已经说明, 这里在简单描述一下:
    // full:如果加了@Configuration,那么对应的BeanDefinition为full;
    // lite:如果加了@Bean,@Component,@ComponentScan,@Import,@ImportResource这些注解,则为lite;
    // 其他则为非配置类
    // 这里该属性值的设置, 可以理解成是一个标志位, 每次判断是否为标志类前, 先确保该属性没有值方可, 否则就是处理过的配置类
    if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
        // 注意这里在beanDefinition中塞入了CONFIGURATION_CLASS_ATTRIBUTE这个属性
        // 外面是通过判断这个是否有属性来确定某个beanDefinition是否是配置类
        beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
    }
    // 有@Configuration 或者 isConfigurationCandidate(metadata)
    else if (config != null || isConfigurationCandidate(metadata)) {
        beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
    }
    else {
        return false;
    }

    // It's a full or lite configuration candidate... Let's determine the order value, if any.
    // 获取排序值
    Integer order = getOrder(metadata);
    if (order != null) {
        beanDef.setAttribute(ORDER_ATTRIBUTE, order);
    }

    return true;
}

上面代码标注了 [4][5] 两处代码,下面详细分析:

[4] 处需要留意一下factoryMethodName的用法:
这里总结一下Spring中实例化对象的几种方式:

  • 直接在xml配置文件中,通过bean标签创建对象;
  • 通过注解如@Bean、@Service等创建对象;
  • 通过FactoryBean工厂类创建对象
  • 通过factory-method属性创建对象 关于后面两种用法,可以参考Spring的官方说明,点击链接地址即可查看Spring通过factory-method实例化对象的方式

[5] 处的proxyBeanMethods放在文末专门举例说明,这里先不写。
简单总结下[5]处的代码:

  • 如果被@Configuration修饰, 且属性proxyBeanMethods的值为true, 则设置属性标志为full, 即完整版配置类
  • 如果被@Configuration修饰, 或者符合isConfigurationCandidate过滤条件, 则设置属性标志为lite, 即前面说的精简版的配置类
  • 都不符合,说明该类不是配置类, 直接结束

在[5]处有一个方法isConfigurationCandidate()用来判断是不是精简版的配置类,来看一下该方法:

abstract class ConfigurationClassUtils {
    // 完整版和精简版配置类的属性值
    public static final String CONFIGURATION_CLASS_FULL = "full";
    public static final String CONFIGURATION_CLASS_LITE = "lite";
    
    // 该集合存储了能够作为精简配置类的注解, 一共有4个
    // 也就是说一个类上, 如果添加了以下任意一个注解, 则该类也是个配置类
    private static final Set<String> candidateIndicators = new HashSet<>(8);

    static {
            candidateIndicators.add(Component.class.getName());
            candidateIndicators.add(ComponentScan.class.getName());
            candidateIndicators.add(Import.class.getName());
            candidateIndicators.add(ImportResource.class.getName());
    }


    public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {

        // 如果是个接口或注解, 也不能算作配置类, 直接返回false
        if (metadata.isInterface()) {
            return false;
        }
        // 遍历匹配在上面static块中初始化的四个注解, 只要有一个, 则符合配置类要求, 直接返回true
        for (String indicator : candidateIndicators) {
            if (metadata.isAnnotated(indicator)) {
                return true;
            }
        }

        try {
            // 如果不包含上面的四个注解, 再验证该类中是否包含@Bean注解修饰的方法,
            // 如果包含, 则也是个配置类, 返回true
            return metadata.hasAnnotatedMethods(Bean.class.getName());
        }
        catch (Throwable ex) {
            // 打印日志, 略
            return false;
        }
    }
}

至此流程图中的checkConfigurationClassCandidate()方法已经很详细的解释了, 我们也已经找到了所有定义了的配置类,下一步就要进行遍历解析了,对应图中的parse方法。
源码如下:

# ConfigurationClassParser.java

/**
 * 解析配置类!!!
 *  这里主要是把配置类上的注解信息解析封装成ConfigurationClass对象
 * 	如果是配置类导入(import/componentScan)的普通类(非配置类), 将会在这里生成beanDefinition并注册
 * @param configCandidates    待解析的配置类集合
 */
public void parse(Set<BeanDefinitionHolder> configCandidates) {
    // 遍历所有的配置类定义信息
    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            // 此时的bd的实际类型为AnnotatedGenericBeanDefinition, 是AnnotatedBeanDefinition的实现子类
            if (bd instanceof AnnotatedBeanDefinition) {
                parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
            }
            else if (bd instanceof AbstractBeanDefinition 
                        && ((AbstractBeanDefinition) bd).hasBeanClass()) {
                parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
            }
            else {
                parse(bd.getBeanClassName(), holder.getBeanName());
            }
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                  "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
        }
    }

    // 延迟处理, 本篇不说明
    this.deferredImportSelectorHandler.process();
}

上面一共有三个分支, 根据配置类对应的BeanDefinition不同,进入不同的分支,这里实际进入的是第一个if块,继续跟踪:

# ConfigurationClassParser.java

protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
    // 最后一个形参类型为:Predicate<String> filter, java8新语法, 表示过滤某些参数,
    // 实参为常量DEFAULT_EXCLUSION_FILTER, 指定以java.lang.annotation
    //     或者org.springframework.stereotype开头的类需要进行过滤
    processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}

再继续跟踪进去:

# ConfigurationClassParser.java

protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {

    // 这里是判断@Condition那些,看是否需要跳过
    if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
        return;
    }

    // 看一下这个配置类是否已经解析过了,configurationClasses是一个LinkedHashMap结构
    // 这里相当于是通过配置类的类名去获取配置类的封装信息的
    // 解析过
    ConfigurationClass existingClass = this.configurationClasses.get(configClass);
    if (existingClass != null) {
        // 分两种情况
        if (configClass.isImported()) {
            if (existingClass.isImported()) {
                // 如果已经解析过,会做一些处理
                existingClass.mergeImportedBy(configClass);
            }
            return;
        }
        else {
            // Explicit bean definition found, probably replacing an import.
            // Let's remove the old one and go with the new one.
            this.configurationClasses.remove(configClass);
            this.knownSuperclasses.values().removeIf(configClass::equals);
        }
    }

    // 这里把configClass又包装成了一个SourceClass, 
    //   这个filter的值默认是 DEFAULT_EXCLUSION_FILTER, 意思就是这两个包内的类在解析的时候会被排除
    //   在本文前面的Predicate<String>中提到过
    SourceClass sourceClass = asSourceClass(configClass, filter);

    do {
        // 处理配置类的逻辑, 配置类中的信息将会被封装到configClass
        // 这里返回sourceClass覆盖上一次的sourceClass, 区别在于新的sourceClass值是旧的sourceClass的父类,
        // 这里循环处理到当前配置类的最顶层父类才会结束
        sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
    } while (sourceClass != null);

    // 把处理完成的配置类加入到该Map中
    this.configurationClasses.put(configClass, configClass);
}

到这里,先总结一下parse()相关的处理流程,如下图:

parse.jpg

再看 doProcessConfigurationClass 这个方法,源码如下:

# ConfigurationClassParser.java

protected final SourceClass doProcessConfigurationClass(
            ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
            throws IOException {

    // [6]
    // 注意因为@Configuration注解上实际上也声明了@Component, 所以这里的if是会进去的
    if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
        // 如果配置类被@Component修饰,先处理内部类
        processMemberClasses(configClass, sourceClass, filter);
    }

    // Process any @PropertySource annotations
    // 这里是处理配置类上的@PropertySources注解的
    // 简单来说就是把properties文件中的内容加载到内存中的Environment中了
    // 我们不细看
    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
                    sourceClass.getMetadata(), PropertySources.class,
                    org.springframework.context.annotation.PropertySource.class)) {
        if (this.environment instanceof ConfigurableEnvironment) {
            processPropertySource(propertySource);
        }
        else {
           // 打印日志, 略...
        }
    }

    // 这里开始是处理类上的@ComponentScan注解的逻辑
    Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
                                                        sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
    if (!componentScans.isEmpty() &&
            !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {

        // 循环处理每一个注解(可以用@ComponentScans包装多个注解-jdk<java8,或者直接打上多个@ComponentScan注解-jdk>=java8)
        for (AnnotationAttributes componentScan : componentScans) {
            // 委托给componentScanParser处理,这里处理完之后返回了一批已注册的BeanDefinition
            // 这里的parse逻辑其实就是创建了一个扫描器并且进行扫描
            Set<BeanDefinitionHolder> scannedBeanDefinitions =
                            this.componentScanParser
                                .parse(componentScan, sourceClass.getMetadata().getClassName());
            for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                if (bdCand == null) {
                    bdCand = holder.getBeanDefinition();
                }
                if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                    // 如果扫描出来的类是配置类,需要走一遍解析配置类的逻辑
                    // 实际上是一个递归
                    parse(bdCand.getBeanClassName(), holder.getBeanName());
                }
            }
        }
    }

    // 处理类上的@Import注解
    processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

    // 处理@ImportResource
    // 用来导入spring的xml配置文件
    AnnotationAttributes importResource =
                    AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
    if (importResource != null) {
        String[] resources = importResource.getStringArray("locations");

        // 这个值默认是 BeanDefinitionReader.class
        Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
        for (String resource : resources) {
            String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);

            // 把@ImportResource注解上的信息封装到configClass
            configClass.addImportedResource(resolvedResource, readerClass);
        }
    }

    // [7]
    // 收集当前类里所有有@bean注解的方法
    // 一般通过反射就能拿到@Bean注解修饰的方法, spring为了确定这些方法处理的顺序, 使用了asm字节码技术来获取方法在类中的声明顺序, 有兴趣可以了解一下
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
        // 封装成BeanMethod并且也放入configClass
        configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }



    // 这里会找到所有接口中被@Bean修饰的非抽象的方法,也封装成BeanMethod放入configClass
    // 因为java8之后接口也可以有默认方法(default修饰的方法), 所以这里也进行了处理
    processInterfaces(configClass, sourceClass);


    // Process superclass, if any
    // 如果传入的参数sourceClass, 有父类的话,会返回父类的sourceClass,然后在外层循环中继续调用doProcessConfigurationClass()方法进行解析
    // 并且把解析的信息封装到当前这个configClass
    if (sourceClass.getMetadata().hasSuperClass()) {
        String superclass = sourceClass.getMetadata().getSuperClassName();
        if (superclass != null && !superclass.startsWith("java") &&
                !this.knownSuperclasses.containsKey(superclass)) {
            this.knownSuperclasses.put(superclass, configClass);
            // Superclass found, return its annotation metadata and recurse
            return sourceClass.getSuperClass();
        }
    }

    // 没有父类了就处理完了
    return null;
}

上面的一大段代码,源码中已经给了详细的说明,再总结一下:

  • 处理内部的@Component,对应processMemberClasses()方法,下面会单独说明
  • 处理@PropertySource
  • 处理@ComponentScan
  • 处理@Import
  • 处理@ImportResource
  • 处理@Bean修饰的方法
  • 处理java8中接口里的默认实现方法
  • 查找传入参数sourceClass的父类,并返回给doProcessConfigurationClass()方法 我们再给出doProcessConfigurationClass()方法的一次循环中的处理流程图:

doProcessConfigurationClass.jpg

接下来会说明processMemberClasses()和@Bean,分别对应源码中标注的 [6][7] 这两处的处理逻辑, 而其他的几个注解的处理,比较简单,以后如果有时间可以再聊聊。

由于@Import等这几个注解,会在处理的过程中,又引入了新的类,所以这个过程会涉及到很多的递归,下面来聊聊processMemberClasses()方法:

# ConfigurationClassParser.java

/**
 * Register member (nested) classes that happen to be configuration classes themselves.
 * 处理配置类上的@Component注解,实际上就是获取到配置类中的所有内部类,并且解析其中的配置类,即调用processConfigurationClass方法
 */
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,
                Predicate<String> filter) throws IOException {
    // 获取所有的内部类
    Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
    if (!memberClasses.isEmpty()) {
        List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
        
        // 循环处理每个内部类
        for (SourceClass memberClass : memberClasses) {
            // 如果内部类也是一个配置类,且内部类与当前类不同
            // 则加入待处理的配置类候选集合中
            if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
                        !memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
                candidates.add(memberClass);
            }
        }

        // 对待处理的配置类排序
        OrderComparator.sort(candidates);
        for (SourceClass candidate : candidates) {
            if (this.importStack.contains(configClass)) {
                this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
            }
            else {
                this.importStack.push(configClass);
                try {
                    // 把每一个内部类也需要处理一下,这里其实又回到上层了,是一个递归
                    // 可以说, 处理配置类上的@Component注解,实际上就是获取到配置类中的所有内部类,并且解析其中的配置类,即调用processConfigurationClass方法。
                    processConfigurationClass(candidate.asConfigClass(configClass), filter);
                }
                finally {
                    this.importStack.pop();
                }
            }
        }
    }
}

总结上面的代码:

  • 获取所有的内部类
  • 判断如果也是一个配置类,则加入到配置类候选集合candidates中
  • 对候选配置类排序
  • 遍历,并调用processConfigurationClass()方法进行解析。该方法就是本篇前面描述过的方法,所以这里其实又是个递归处理。

再来说明[7]处对@Bean修饰方法的收集处理逻辑,代码如下:

# ConfigurationClassParser.java

private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {
    // 获取所有被@Bean修饰的方法
    AnnotationMetadata original = sourceClass.getMetadata();
    Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());
    if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) {
        // Try reading the class file via ASM for deterministic declaration order...
        // Unfortunately, the JVM's standard reflection returns methods in arbitrary
        // order, even between different runs of the same application on the same JVM.
        // 这里注释是说由于jvm返回的方法列表顺序不能保证,这里尝试使用asm字节码技术拿到方法在类中的声明顺序,以此来为这些被@Bean修饰的方法排序
        try {
            AnnotationMetadata asm =
                            this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata();
            Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Bean.class.getName());

            // 排序
            if (asmMethods.size() >= beanMethods.size()) {
                Set<MethodMetadata> selectedMethods = new LinkedHashSet<>(asmMethods.size());
                for (MethodMetadata asmMethod : asmMethods) {
                    for (MethodMetadata beanMethod : beanMethods) {
                        if (beanMethod.getMethodName().equals(asmMethod.getMethodName())) {
                            selectedMethods.add(beanMethod);
                            break;
                        }
                    }
                }
                if (selectedMethods.size() == beanMethods.size()) {
                    // All reflection-detected methods found in ASM method set -> proceed
                    beanMethods = selectedMethods;
                }
            }
        }
        catch (IOException ex) {
           // 抛出异常, 省略...
        }
    }
    return beanMethods;
}

该方法了解即可,知道最终返回的是配置类中被@Bean修饰的方法集合即可。

总结

上文已经详细说明了配置类的收集、以及将其BeanDefinition信息注册到bean工厂的流程,图片如下:

full.jpg

本篇着重介绍了@Configuration相关的配置类的收集过程,还有关于其动态代理的源码,本篇暂时不讲AOP相关的内容。这里提一下,其入口处是在PostProcessorRegistrationDelegate类的invokeBeanFactoryPostProcessors()方法中,具体是在处理BeanDefinitionRegistryPostProcessor类型的调用处,会调用 enhanceConfigurationClasses(beanFactory) 方法,代码如下:

public static void invokeBeanFactoryPostProcessors(
			ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

    Set<String> processedBeans = new HashSet<>();

    if (beanFactory instanceof BeanDefinitionRegistry) {
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;

        List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
        List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

        for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
            if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
                BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor;
                registryProcessor.postProcessBeanDefinitionRegistry(registry);
                registryProcessors.add(registryProcessor);
            }
            else {
                regularPostProcessors.add(postProcessor);
            }
        }

        List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

        // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
        String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);

        for (String ppName : postProcessorNames) {
            if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                processedBeans.add(ppName);
            }
        }

        sortPostProcessors(currentRegistryProcessors, beanFactory);
        registryProcessors.addAll(currentRegistryProcessors);

        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);

        currentRegistryProcessors.clear();

        // Next, invoke the BeanDefinitionRegistryPostProcessors that implement 
        postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);

        for (String ppName : postProcessorNames) {
            if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
                currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                processedBeans.add(ppName);
            }
        }
        sortPostProcessors(currentRegistryProcessors, beanFactory);
        registryProcessors.addAll(currentRegistryProcessors);

        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
        currentRegistryProcessors.clear();

        // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
        boolean reiterate = true;
        while (reiterate) {
            reiterate = false;
            postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            for (String ppName : postProcessorNames) {
                if (!processedBeans.contains(ppName)) {
                        currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                        processedBeans.add(ppName);
                        reiterate = true;
                }
            }
            registryProcessors.addAll(currentRegistryProcessors);
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
            currentRegistryProcessors.clear();
        }
        // Now, invoke the postProcessBeanFactory callback of all processors handled so far.
        // 这里其实会调用enhanceConfigurationClasses()方法,进行CGLIB的代理处理, 本文不说, 后续到AOP的时候, 再来详谈这块的内容
        invoeBeanFactoryPostProcessors(registryProcessors, beanFactory);
        invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
    }
    else {
        // ....
    }
    
    // .......
}

文末

前面提到@Configuration注解的proxyBeanMethods属性, 这里在前面调试代码的基础上,做一下改动

新增一个对象类:Car.java

public class Car {

}

修改Person的构造函数

public class Person {
    public Person(Car car) {
        System.out.println("内部:" + car);
    }
}

配置类做如下修改:MyConfig.java

@Configuration
public class MyConfig {
    @Bean
    public Person person() {
        // @Bean方法调用另一个@Bean方法
        return new Person(car());
    }
    @Bean
    public Car car() {
        return new Car();
    }
}

测试类:

public class ApplicationSimpleConfiguration {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);

        System.out.println("car = " + context.getBean("car"));
        System.out.println("person = " + context.getBean("person"));

    }
}

观察打印结果:可以发现两个car是一致的 image.png

但如果修改配置类:删除@Configuration注解或者设置其属性proxyBeanMethods = false

@Configuration
public class MyConfig {
    @Bean
    public Person person() {
        // @Bean方法调用另一个@Bean方法
        return new Person(car());
    }
    @Bean
    public Car car() {
        return new Car();
    }
}

观察结果:

image.png