likes
comments
collection
share

13.postProcessBeanDefinitionRegistry源码(下)

作者站长头像
站长
· 阅读数 24

第一部分主要是讲遍历候选的BeanDefinition,然后根据BeanDefinition的属性configClass判断是否是配置类,如果是配置类加入候选配置类集合,第二部分对候选配置类集合进行解析。

本文重点解析对候选配置类集合进行解析。

步骤5:创建ConfigurationClassParser,解析配置类

 ConfigurationClassParser parser = new ConfigurationClassParser(
                this.metadataReaderFactory, 
                this.problemReporter, 
                this.environment,
                this.resourceLoader, 
                this.componentScanBeanNameGenerator,
                registry);

Spring的工具类ConfigurationClassParser用于分析@Configuration注解的配置类,产生一组ConfigurationClass对象,下文会用到。

    //创建候选配置类集合
    Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
    //已解析配置类集合 
    Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());

实例化两个set:

candidates用于将之前加入的configCandidates去重。

alreadyParsed用于判断是否处理过了。

代码再往下就是开始循环do while 循环解析处理candidates配置类

//解析配置类 candidates里面存放的就是configCandidates,只不过利用了set的特性做了去重。   
parser.parse(candidates);

进入parse方法,开始真正的解析,接下来是重点中的重点。

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    //遍历分析配置类
    for (BeanDefinitionHolder holder : configCandidates) {
        //得到配置类对应的BeanDefinition
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            // 这里根据Bean定义的不同类型走不同的分支,
            // 但是最终都会调用到方法processConfigurationClass(ConfigurationClass configClass)
            // 判断是否是AnnotatedBeanDefinition类型
            // 之前分析过自定义的Config可以通过AnnotatedBeanDefinitionReader解析为        
            // AnnotatedGenericBeanDefinition。
            // AnnotatedBeanDefinition是AnnotatedGenericBeanDefinition的父类

            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");
        }
    }
    // 执行找到的 DeferredImportSelector 
    // DeferredImportSelector 是 ImportSelector 的一个变种。
    // ImportSelector 被设计成其实和@Import注解的类同样的导入效果
    // 但是实现ImportSelector的类可以条件性地决定导入哪些配置。饥渴加载
    // DeferredImportSelector 的设计目的是在所有其他的配置类被处理后才处理。
    // 这也正是该语句被放到本函数最后一行的原因。
    this.deferredImportSelectorHandler.process();
}

先关注第一句代码parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());

参数有两个,一个是BeanDefinition的注解元数据,一个bean的名称,点进去跟进:

protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
    processConfigurationClass(new ConfigurationClass(metadata, beanName));
}

发现最终调用到方法processConfigurationClass(ConfigurationClass configClass);

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
    //根据配置类的@Condition注解 判断是否需要跳过
    if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), 
                                ConfigurationPhase.PARSE_CONFIGURATION)) {
        return;
    }
   
   
     // 第一次进入的时候,configurationClass的size为0,existingClass肯定为null,在此处处理configuration重复import
     // 如果同一个配置类被处理两次,两次都属于被import的则合并导入类,返回,如果配置类不是被导入的,则移除旧的使用新的配置类
    // 原文链接:https://blog.csdn.net/weixin_39459624/article/details/114960057
    ConfigurationClass existingClass = this.configurationClasses.get(configClass);
    if (existingClass != null) {
        if (configClass.isImported()) {
            if (existingClass.isImported()) {
                existingClass.mergeImportedBy(configClass);
            }
            return;
        }
        else {
            this.configurationClasses.remove(configClass);
            this.knownSuperclasses.values().removeIf(configClass::equals);
        }
    }

    // 递归处理配置类以及配置类的超类 包括父类 爷爷类 以及父类 爷爷类的超类 
    // Recursively process the configuration class and its superclass hierarchy.
    SourceClass sourceClass = asSourceClass(configClass);
    do {
        sourceClass = doProcessConfigurationClass(configClass, sourceClass);
    }while (sourceClass != null);

    this.configurationClasses.put(configClass, configClass);
}

步骤5.1:判断配置类是否要跳过:@Conditional

首先将注解元数据和bean名称封装成ConfigurationClass,这个类翻译成中文就是"配置类"。

继续跟进:如果存在@Conditional注解并且条件不成立,那么返回true说明需要跳过,当前配置类不会被解析。

如果不存在@Conditional注解 返回false,代表需要解析。

//获取配置类的@Condition注解 判断是否需要跳过
//返回true直接跳过 即不需要解析
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), 
                            ConfigurationPhase.PARSE_CONFIGURATION)) {
    return;
}
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
    //如果配置类上的注解为空或者配置类上没有@Conditional注解
    //直接返回false也就是不跳过
    if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
        return false;
    }

    //phase是null才会进入判断
    //此处的phase是ConfigurationPhase.PARSE_CONFIGURATION不会进入判断直接跳过
    if (phase == null) {
        if (metadata instanceof AnnotationMetadata &&
                ConfigurationClassUtils
                    .isConfigurationCandidate((AnnotationMetadata) metadata)) {
            return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
        }
        return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
    }

    //获取配置类上所有的@Conditional注解 放入集合
    List<Condition> conditions = new ArrayList<>();
    for (String[] conditionClasses : getConditionClasses(metadata)) {
        for (String conditionClass : conditionClasses) {
            Condition condition = getCondition(conditionClass,this.context.getClassLoader());
            conditions.add(condition);
        }
    }
    
    //排序
    AnnotationAwareOrderComparator.sort(conditions);
    
    //遍历配置类上所有的@Conditional注解
    for (Condition condition : conditions) {
        ConfigurationPhase requiredPhase = null;
        if (condition instanceof ConfigurationCondition) {
            requiredPhase = ((ConfigurationCondition) 
                                        condition).getConfigurationPhase();
        }
        //如果@Condition的match方法返回的是true,代表不会跳过,需要解析
        //如果@Condition的match方法返回的是false,代表要跳过,不需要解析
        if ((requiredPhase == null || requiredPhase == phase) && 
                                !condition.matches(this.context, metadata)) {
            return true;
        }
    }
    return false;
}

processConfigurationClass中的第一行就是检查当前解析的配置bean是否包含Conditional注解,如果不包含则不需要跳过,当前配置类需要被解析。

如果包含了Conditional注解则执行match方法得到匹配结果,match方法返回false则跳过不解析,返回true不跳过需要解析。

我们打个比方: 先生成一个业务类

public class Person {
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person{" + "name='" + name + ''' + ", age=" + age + '}';
    }
}

@Configuration
public class BeanConfig {
    @Bean(name = "bill")
    public Person person1(){
        return new Person("Bill Gates",62);
    }
    @Bean("linus")
    public Person person2(){
        return new Person("Linus",48);
    }
 }
public class TestCondition {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context 
                                = new AnnotationConfigApplicationContext();
        //注册配置类
        context.register(BeanConfig.class);
        context.refresh();
        Map<String, Person> map = context.getBeansOfType(Person.class);
        System.out.println(map);
    }
}
//打印结果:
//{bill=Person{name='Bill Gates', age=62}, linus=Person{name='Linus', age=48}}

问题来了,如果我想根据当前操作系统来注入Person实例,windows下注入bill,linux下注入linus,怎么实现呢?

这就需要我们用到@Conditional注解了。

首先,创建一个WindowsCondition类:

public class WindowsCondition implements Condition {
    /**
     * @param conditionContext:判断条件能使用的上下文环境
     * @param annotatedTypeMetadata:注解所在位置的注释信息
     * */
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        //获取ioc使用的beanFactory
        ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
        //获取类加载器
        ClassLoader classLoader = conditionContext.getClassLoader();
        //获取当前环境信息
        Environment environment = conditionContext.getEnvironment();
        //获取bean定义的注册类
        BeanDefinitionRegistry registry = conditionContext.getRegistry();
        //获得当前系统名
        String property = environment.getProperty("os.name");
        //包含Windows则说明是windows系统,返回true
        if (property.contains("Windows")){
            return true;
        }
        return false;
    }
}

创建LinuxCondition类:

public class LinuxCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        Environment environment = conditionContext.getEnvironment();
        String property = environment.getProperty("os.name");
        if (property.contains("Linux")){
            return true;
        }
        return false;
    }
}

接着就是使用这两个类了,因为此注解可以标注在方法上和类上,所以分开测试:

@Configuration
public class BeanConfig {
    //只有一个类时,大括号可以省略
    //如果WindowsCondition的实现方法返回true,则注入这个bean
    @Conditional({WindowsCondition.class})
    @Bean(name = "bill")
    public Person person1(){
        return new Person("Bill Gates",62);
    }

    //如果LinuxCondition的实现方法返回true,则注入这个bean
    @Conditional({LinuxCondition.class})
    @Bean("linus")
    public Person person2(){
        return new Person("Linus",48);
    }
 }

由于我们的操作系统是windows,所以打印结果:{bill=Person{name='Bill Gates', age=62}}。

注意一个方法只能注入一个bean实例,所以@Conditional标注在方法上只能控制一个bean实例是否注入。 标注在类上:一个类中可以注入很多实例,@Conditional标注在类上就决定了一批bean是否注入。

我们试一下,将BeanConfig改写,这时如果WindowsCondition返回true,则两个Person实例将被注入:

//如果WindowsCondition的实现方法返回true,则注入BeanConfig下的所有@Bean的方法对应的bean
@Conditional({WindowsCondition.class})
@Configuration
public class BeanConfig {
    @Bean(name = "bill")
    public Person person1(){
        return new Person("Bill Gates",62);
    }

    @Bean("linus")
    public Person person2(){
        return new Person("Linus",48);
    }
 }

@Conditional注解传入的是一个Class数组,存在多种条件类的情况。这种情况貌似判断难度加深了,测试一波,新增新的条件类,实现的matches返回false(这种写死返回false的方法纯属测试用,没有实际意义O(∩_∩)O)

public class ObstinateCondition  implements Condition{
    @Override
    public boolean matches(ConditionContext conditionContext, 
                                        AnnotatedTypeMetadata annotatedTypeMetadata) {
        return false;
    }
}
@Conditional({WindowsCondition.class,ObstinateCondition.class})
@Configuration
public class BeanConfig {

    @Bean(name = "bill")
    public Person person1(){
        return new Person("Bill Gates",62);
    }

    @Bean("linus")
    public Person person2(){
        return new Person("Linus",48);
    }
}

请读者自行测试,这个例子就是说明processConfigurationClass中的第一行代码的作用,如果不符合条件注册bean到IOC,否则不继续执行。

测试的结果是返回{},即@Conditional注解上有多个类时,使用每个类的matches方法的返回值做&&操作。

只要有1个@Conditional注解返回false就直接返回true就会跳过 不再解析这个配置类。

读到这里大家都知道,我们的配置类只有Config.java,我们在这个配置类上加一个Condition条件:

@Order(1)
@ComponentScan("com")
//返回的是false 代表不会
@Conditional({LinuxCondition.class})
public class Config {
}

@Conditional({LinuxCondition.class})返回false,上面的shouldSkip方法的返回true,代码直接return。

也就是说你这个配置类无效,spring不会解析这个配置类。

为什么?

spring启动过程中,我们是这样注册配置类的context.register(Config.class);

这个register源码之前讲过,一直跟到doRegisterBean中,有这么两行代码:

//@Conditional装配条件判断是否需要跳过注册
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
    return;
}

也就是说,我们在注册Config配置类的时候spring已经对Condition条件进行判断了。

如果条件不满足不会生成Config对应的BeanDefinition了。

那怎么办?直接手动注册BeanDefinition。

//context.register(Config.class);
AnnotatedGenericBeanDefinition bd = new AnnotatedGenericBeanDefinition(Config.class);
context.registerBeanDefinition("config",bd);
context.refresh();

打断点调试:

13.postProcessBeanDefinitionRegistry源码(下)

好了,shouldSkip这行代码已经讲清楚了。

上面这种直接注册config对应的bd,发现确实会return,Config对应的bd确实被注册上去了。

但是还是有问题,不会作为配置类生效!!!!!

总结一下: 是否作为配置类去解析首先要根据@Conditional判断是否要跳过。

步骤5.2:真正解析:doProcessConfigurationClass

// 第一次进入的时候, configurationClasses size = 0,existingClass 肯定为 null
//在这里处理Configuration重复import
//如果同一个配置类被处理两次,两次都属于被import的则合并导入类,返回。
//如果配置类不是被导入的,则移除旧使用新的配置类
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
    if (configClass.isImported()) {
        if (existingClass.isImported()) {
            //如果要处理的配置类configClass在已经分析处理的配置类记录中已存在,
            //合并二者的importedBy属性
            existingClass.mergeImportedBy(configClass);
        }
        // Otherwise ignore new imported config class;
        // existing non-imported class overrides it.
        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);
    }
}

//递归地处理配置类及其超类层次结构。sourceClass包含了calss文件,后面获取父类用。
SourceClass sourceClass = asSourceClass(configClass);
do {
    // 真正的做解析
    // 首先解析的配置类
    // 返回的是配置类的父类
    // 在doWhile中继续解析配置类的父类
    // 如果配置类的父类还有父类 继续解析配置类的父类的父类 无限递归,直到没有父类。
    sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}while (sourceClass != null);
//加入configurationClasses集合 
//注意这里是和上面的代码this.configurationClasses.get(configClass);呼应
this.configurationClasses.put(configClass, configClass);

第一次调用existingClass 肯定为空,难道还有第二次调用?是的,@Import注解就是引入,先不管这个,

代码会跳过判断,执行SourceClass sourceClass = asSourceClass(configClass);,从当前配置类configClass开始向上沿着类继承结构逐层执行doProcessConfigurationClass,直到遇到的父类是由Java提供的类结束循环。

然后就是真正的解析sourceClass = doProcessConfigurationClass(configClass, sourceClass);,循环处理配置类configClass直到返回的sourceClass变为null。

doProcessConfigurationClass的返回值是其参数configClass的父类,如果该父类是由Java提供的类或者已经处理过,返回null。

doProcessConfigurationClass()会对一个配置类执行真正的处理:

1. 一个配置类的成员类(配置类内定义的内部类)也可能是适配类,先遍历这些成员内部类。
2. 调用processConfigurationClass处理它们。
3. 处理配置类上的注解@PropertySources,@PropertySource。
4. 处理配置类上的注解@ComponentScans,@ComponentScan。
5. 处理配置类上的注解@Import。
6. 处理配置类上的注解@ImportResource。
7. 处理配置类中每个带有@Bean注解的方法。
8. 处理配置类所实现接口的缺省方法。
9. 检查父类是否需要处理,如果父类需要处理返回父类,否则返回null。

返回父类表示当前配置类处理尚未完成,调用者processConfigurationClass会继续处理其父类;返回null才表示该配置类的处理完成。

从这里可以推断一旦一个配置类被processConfigurationClass处理完成,表示其自身,内部嵌套类,各个实现接口以及各级父类都被处理完成。

一起看下processConfigurationClass源码:

步骤5.3:doProcessConfigurationClass

//configClass是配置类 sourceClass是配置类的超类
@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
    //如果配置类带有@Component
    if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
        // 递归处理配置类的内部类
        // 会在processMemberClasses调用processConfigurationClass递归处理
        processMemberClasses(configClass, sourceClass);
    }

    //处理@PropertySource
    //PropertySource注解可以方便和灵活的向Spring的环境容器的org.core.env.Environment Environment
    //注入一些属性,这些属性可以在Bean中使用。
    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
                    sourceClass.getMetadata(), PropertySources.class,
                    org.springframework.context.annotation.PropertySource.class)) {
        if (this.environment instanceof ConfigurableEnvironment) {
                //处理PropertySource
                processPropertySource(propertySource);
        }else {
        //打印日志 省略..
        }
    }

    //获取 @ComponentScan和@ComponentScans注解对应的属性值
    //处理@ComponentScan或者@ComponentScans注解
    //并将扫描包下的所有bean转换成填充后的ConfigurationClass
    //此处就是将自定义的bean加载到IOC容器
    //因为扫描到的类可能也添加了@ComponentScan和@ComponentScans注解,因此需要进行递归解析
    Set<AnnotationAttributes> componentScans = 		
            AnnotationConfigUtils.attributesForRepeatable(
                                        sourceClass.getMetadata(), 
                                        ComponentScans.class, 
                                        ComponentScan.class);
                                        
    //判断扫描出来的@ComponentScan和@ComponentScans的注解对应的属性值不为空
    //并且不需要跳过
    if (!componentScans.isEmpty() &&!this.conditionEvaluator.shouldSkip
                     (sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
             
            //循环遍历所有的@ComponentScan和@ComponentScans的注解对应的属性值
            //它们2个的属性值主要就是basePackages/basePackageClasses这些扫包的路径
            for (AnnotationAttributes componentScan : componentScans) {
                 //调用componentScanParser扫描路径下的bd
                 //底层调用的是ClassPathBeanDefinitionScanner扫描beanDefinition
                Set<BeanDefinitionHolder> scannedBeanDefinitions =
                        this.componentScanParser
                            .parse(componentScan,sourceClass.getMetadata().getClassName());

                //遍历解析的scanner扫描出来的bd
                for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                    //获取源bd 
                    //源bd与bd的区别没研究过
                    BeanDefinition bdCand = holder.getBeanDefinition()
                                                  .getOriginatingBeanDefinition();
                    //获取bd                              
                    if (bdCand == null) {
                        bdCand = holder.getBeanDefinition();
                    }
                    
                    //判断是否是候选配置类 如果是继续递归执行parse方法
                    if (ConfigurationClassUtils.checkConfigurationClassCandidate
                                            (bdCand, this.metadataReaderFactory)) {
                        //解析Scanner扫描出来的BeanDefinition
                        //这里是一个递归处理 又进入了processConfigurationClass方法
                        //也就是说如果我有
                        //带有@ComponentScan的配置类Config 
                        //带有@Component的User类
                        //先解析Config
                        //先通过Config类上面的@ComponentScan构造1个scanner去扫描到User
                        //然后开始解析User
                        //解析User的流程和上面一样先解析内部类...然后解析ComponentScan
                        //注意:等解析完User才会继续解析Config
                        parse(bdCand.getBeanClassName(), holder.getBeanName());
                    }
                }
            }
        }

        // getImports(sourceClass)方法获取所有带@Import注解的类
        // ImportSelector
        processImports(configClass, sourceClass, getImports(sourceClass), true);

        //处理@ImportResource
        AnnotationAttributes importResource = AnnotationConfigUtils
                                .attributesFor(sourceClass.getMetadata(),ImportResource.class);
                                
        if (importResource != null) {
            //获取@ImportResource的locations属性
            String[] resources = importResource.getStringArray("locations");
            Class<? extends BeanDefinitionReader> readerClass =importResource
                                                                .getClass("reader");
            for (String resource : resources) {
                String resolvedResource = 
                        this.environment.resolveRequiredPlaceholders(resource);
                configClass.addImportedResource(resolvedResource, readerClass);
            }
        }

        //处理带有@Bean的方法
        Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
        for (MethodMetadata methodMetadata : beanMethods) {
            configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
        }

        //处理接口里的default方法
        processInterfaces(configClass, sourceClass);

        // 注意这里返回的是配置类的父类
        if (sourceClass.getMetadata().hasSuperClass()) {
            String superclass = sourceClass.getMetadata().getSuperClassName();
            if (superclass != null && !superclass.startsWith("java") &&
                            !this.knownSuperclasses.containsKey(superclass)) {
                this.knownSuperclasses.put(superclass, configClass);
                // 找到父类, 返回继续递归。
                return sourceClass.getSuperClass();
            }
        }

        // No superclass -> processing is complete
        //如果返回的父类为空 说明处理完毕。
        return null;
}

步骤5.3.1:递归解析配置类的内部类

上面的步骤我们挨个分析,进入doProcessConfigurationClass源码,首先处理的就是内部类:

if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
    // Recursively process any member (nested) classes first
    // 递归处理内部类
    processMemberClasses(configClass, sourceClass);
}
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
    //获取配置类的内部类
    Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
    if (!memberClasses.isEmpty()) {
            List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
            for (SourceClass memberClass : memberClasses) {
                 /***
                isConfigurationCandidate方法会判断内部类是不是配置类
                上文讲过了如何判断是不是配置类:
                如果是@Configuration 就是full
                如果加了@Component,@ComponentScan,@Import,@ImportResource这些注解的类 
                或者是带有@Bean方法的类,则为lite。
                不管是full还是lite都属于配置类
                
                checkConfigurationClassCandidate方法判断内部类是不是配置类的同时还会给bd设置属性
                ***/
                if (ConfigurationClassUtils.
                isConfigurationCandidate(memberClass.getMetadata()) &&
                                !memberClass.getMetadata().getClassName().
                        equals(configClass.getMetadata().getClassName())) {
                        //将内部类加入候选配置集合
                        candidates.add(memberClass);
                }
            }
            //对配置类进行排序,上文也讲过了
            OrderComparator.sort(candidates);
            //1)出现配置类循环导入,直接报错
            for (SourceClass candidate : candidates) {
            if (this.importStack.contains(configClass)) {
                    this.problemReporter.error(new CircularImportProblem
                                               (configClass, this.importStack));
            }else {
                //将配置类入栈
                this.importStack.push(configClass);
                try {
                    //递归调用processConfigurationClass方法,也许内部类还有内部类
                    //其实工作中我们不会内部类嵌套内部类再嵌套内部类再嵌套。。。
                    //真有也不这么写 
                    processConfigurationClass(candidate.asConfigClass(configClass));
                }
                finally {
                    //最后处理完出栈
                    this.importStack.pop();
                }
            }
        }
    }
}

注释解释的很清楚,就是判断内部类是否是配置类,上文已经讲过如何判断是否是内部类了,如果是配置类还是要调用processConfigurationClass方法来处理,这是一个递归的过程,一直到没有内部配置类为止。

步骤5.3.2:解析@PropertySources,@PropertySource

配置类内部类处理完,紧接着就是处理配置类上的注解@PropertySources,@PropertySource,如果配置类上有@PropertySource注解,则解析加载properties文件,并将属性添加到Spring上下文中。

源码:

//获取的是配置类的 @PropertySources  @PropertySource的属性值
for (AnnotationAttributes propertySource : 
                    AnnotationConfigUtils.attributesForRepeatable(
                    sourceClass.getMetadata(), PropertySources.class,
            org.springframework.context.annotation.PropertySource.class)) {
    if (this.environment instanceof ConfigurableEnvironment) {
        processPropertySource(propertySource);
    }
    else {
        logger.info("Ignoring @PropertySource annotation on Reason:"
                +"Environment must implement ConfigurableEnvironment");
    }
}
private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
    String name = propertySource.getString("name");
    if (!StringUtils.hasLength(name)) {
        name = null;
    }
    String encoding = propertySource.getString("encoding");
    if (!StringUtils.hasLength(encoding)) {
        encoding = null;
    }
    String[] locations = propertySource.getStringArray("value");
    Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
    boolean ignoreResourceNotFound = 
        propertySource.getBoolean("ignoreResourceNotFound");

    Class<? extends PropertySourceFactory> factoryClass = 
                                propertySource.getClass("factory");
    PropertySourceFactory factory = 
            (factoryClass == PropertySourceFactory.class ?
                        DEFAULT_PROPERTY_SOURCE_FACTORY : 
                            BeanUtils.instantiateClass(factoryClass));

    for (String location : locations) {
        try {
            String resolvedLocation = 
                    this.environment.resolveRequiredPlaceholders(location);
            Resource resource = 
                    this.resourceLoader.getResource(resolvedLocation);
            addPropertySource(factory.createPropertySource(name, new 
                                          EncodedResource(resource, encoding)));
        }catch (IllegalArgumentException | 
               FileNotFoundException | 
               UnknownHostException | 
               SocketException ex) {
             // Placeholders not resolvable or resource not found when trying to open it
            if (ignoreResourceNotFound) {

            }else {
                throw ex;
            }
        }
    }
}

这段源码,可以参考:<blog.csdn.net/qq_36305027… 这段代码的执行结果就是将我们自定义的资源文件添加到spring上下文环境中,我们通过一个例子来说明:首先在resources中添加一个资源文件demo.properties,内容如下:

demo.name=this is my properties

13.postProcessBeanDefinitionRegistry源码(下)

修改下我们的配置类:

@Order(1)
@Configuration
@ComponentScan("com")
@PropertySource({"classpath:demo.properties"})
public class Config {
    @Value("${demo.name}")
    private String name;
    public String getName() {
        return name;
    }
}
public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        //注册配置类
        context.register(Config.class);
        context.refresh();
        System.out.println(context.getBean(Config.class).getName());
    }
}
打印结果:
//demo.name=this is my properties

spring已经将配置文件加入到系统环境中了,在任何地方都可以通过@Value("${demo.name}")方式进行调用!

13.postProcessBeanDefinitionRegistry源码(下)

步骤5.3.3:解析@ComponentScans,@ComponentScan

@ComponentScan 和 @ComponentScans的用法

1. 扫描指定类文件
@ComponentScan(basePackageClasses = Person.class)
2. 扫描指定包,使用默认扫描规则,即被@Component, @Repository, @Service, @Controller或者已经声明过@Component自定义注解标记的组件;
@ComponentScan(value = "com.yibai")
3. 扫描指定包,加载被@Component注解标记的组件和默认规则的扫描(因为useDefaultFilters默认为true)
@ComponentScan(value = "com.yibai", includeFilters = { @Filter(type = FilterType.ANNOTATION, value = Component.class) })
4. 扫描指定包,只加载Person类型的组件
@ComponentScan(value = "com.yibai", includeFilters = { @Filter(type = FilterType.ASSIGNABLE_TYPE, value = Person.class) }, useDefaultFilters = false)
5. 扫描指定包,过滤掉被@Component标记的组件
@ComponentScan(value = "com.yibai", excludeFilters = { @Filter(type = FilterType.ANNOTATION, value = Component.class) })
6. 扫描指定包,自定义过滤规则
@ComponentScan(value = "com.yibai", includeFilters = { @Filter(type = FilterType.CUSTOM, value = ColorBeanLoadFilter.class) }, useDefaultFilters = true)
7. @ComponentScans
@ComponentScans(value = {
@ComponentScan(value = "com.yibai.spring.annotation"),
@ComponentScan(value = "com.yibai.spring.annotation", 
    includeFilters = {@Filter(type = FilterType.CUSTOM, value = ColorBeanLoadFilter.class)}) 
})

源码如下

//处理配置类上的注解@ComponentScans,@ComponentScan。
//第一步先获取@ComponentScans和@ComponentScan注解的属性
Set<AnnotationAttributes> componentScans = 
                 AnnotationConfigUtils.attributesForRepeatable(
                                            sourceClass.getMetadata(), 
                                            ComponentScans.class,
                                            ComponentScan.class);

if (!componentScans.isEmpty() &&
        !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(),                                              ConfigurationPhase.REGISTER_BEAN)) {
    //遍历@ComponentScans和@ComponentScan注解的属性AnnotationAttributes    
    for (AnnotationAttributes componentScan : componentScans) {
        // 带有@ComponentScan注解的配置类会首先被立刻解析
        // 备注:这个方法虽然有返回值但是其实内部都已经把Bean定义信息加入到工厂里面去了
        // 进入componentScanParser的parse方法
        Set<BeanDefinitionHolder> scannedBeanDefinitions =
                    this.componentScanParser.parse(
                                    componentScan,          
                                    sourceClass.getMetadata().getClassName());
/*
parse扫描注册完成后,将生成的BeanDefinition返回,还需要把每个Bean检测一遍。因为Scan出来的Bean,还有可能是@Configuration的,因此需要再次交给parse一遍,防止疏漏,换句话说,扫描后的业务类中还有配置类,我们还需要处理这些配置类。getOriginatingBeanDefinition返回原始的BeanDefinition,没有的话返回 null,链式调用该方法,最终可获取到由用户定义的 BeanDefinition。
*/ 
        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());
            }
        }
    }
}

步骤5.3.3.1:componentScanParser#parse

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
    //为什么还要new一个扫描器?
    //因为每个@ConponentScan的包含过滤器和排除过滤器不一样,是独一无二的
    //需要把每个@ConponentScan的包含过滤器和排除过滤器
    //复制到ClassPathBeanDefinitionScanner扫描器中
    ClassPathBeanDefinitionScanner scanner 
                = new ClassPathBeanDefinitionScanner(this.registry,
                               componentScan.getBoolean("useDefaultFilters"), 
                               this.environment,  
                               this.resourceLoader);                                                                                                      
    //创建名称生成器
    //BeanName的生成器,我们可以单独制定。若不指定(大部分情况下都不指定)
    //那就是默认的AnnotationBeanNameGenerator
    //它的处理方式是:类名首字母小写。这个上面也讲过啦。
    Class<? extends BeanNameGenerator> generatorClass =             
                                componentScan.getClass("nameGenerator");

    boolean useInheritedGenerator = 
                        (BeanNameGenerator.class == generatorClass);

    scanner.setBeanNameGenerator(useInheritedGenerator ? 
                                 this.beanNameGenerator :
                                 BeanUtils.instantiateClass(generatorClass));

    ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
    if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
        scanner.setScopedProxyMode(scopedProxyMode);
    } else {
        Class<? extends ScopeMetadataResolver> resolverClass 
                                = componentScan.getClass("scopeResolver");
        scanner.setScopeMetadataResolver
                                (BeanUtils.instantiateClass(resolverClass));
    }
    //static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
    //控制去扫描哪些.class文件的模版。一般不设置 默认值为:**/*.class 全扫
    scanner.setResourcePattern(componentScan.getString("resourcePattern"));

    //之前讲过,就是扫描过滤器,如果你注解上写了过滤器
    //就复制到ClassPathBeanDefinitionScanner扫描器中
    //获取注解上的includeFilters添加到扫描器
    for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
        for (TypeFilter typeFilter : typeFiltersFor(filter)) {
            scanner.addIncludeFilter(typeFilter);
        }
    }
    //获取注解上的excludeFilters添加到扫描器
    for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
        for (TypeFilter typeFilter : typeFiltersFor(filter)) {
            scanner.addExcludeFilter(typeFilter);
        }
    }

    //判断是否懒加载
    //扫描的Bean,是否需要懒加载啦,默认为false。
    boolean lazyInit = componentScan.getBoolean("lazyInit");
    if (lazyInit) {
        scanner.getBeanDefinitionDefaults().setLazyInit(true);
    }
    
    //获取basePackages属性
    Set<String> basePackages = new LinkedHashSet<>();
    String[] basePackagesArray = componentScan.getStringArray("basePackages");

    //遍历解析basePackages 
    //因为basePackage中可能含有换行符 , ; 等特殊符号
    for (String pkg : basePackagesArray) {
        // Spring在此处有强大的容错处理。虽然他是支持数组的,但是它这里也容错处理。
        //支持 , ; 换行等的符号分隔处理。
        //并且,并且更强大的地方在于:它支持${...}这种占位符的形式,非常的强大。
        //我们可以动态的进行扫包
        //String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
        String[] tokenized = StringUtils.tokenizeToStringArray
            (this.environment.resolvePlaceholders(pkg),
                ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
        //将解析后的basePackage放入basePackages
        Collections.addAll(basePackages, tokenized);
    }
    //获取basePackageClasses属性
    //basePackageClasses有时候也挺好使的。它可以指定class.
    //然后这个class所在的包(含子包)都会被扫描
    //即将basePackageClasses指定的类所在的包以及子包加入到 basePackages
    for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
        basePackages.add(ClassUtils.getPackageName(clazz));
    }

    //如果 basePackages 和 basePackageClasses 属性为空 取当前配置类所在的包以及子包 
    //是不是很熟悉,SpringBoot就是这么干的。
    /*
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(
        excludeFilters = {@Filter(
        type = FilterType.CUSTOM,
        classes = {TypeExcludeFilter.class}
    ), @Filter(
        type = FilterType.CUSTOM,
        classes = {AutoConfigurationExcludeFilter.class}
    )}
    )
    public @interface SpringBootApplication {}
    */
    if (basePackages.isEmpty()) {
        basePackages.add(ClassUtils.getPackageName(declaringClass));
    }

    //这行代码的意思增加一个排他过滤器,配置类本身不需要添加到容器中,他只是用作解析。
    scanner.addExcludeFilter(
        new AbstractTypeHierarchyTraversingFilter(false, false) {
        @Override
        protected boolean matchClassName(String className) {
            return declaringClass.equals(className);
        }
    });
    return scanner.doScan(StringUtils.toStringArray(basePackages));
}

步骤5.3.3.2:ClassPathBeanDefinitionScanner#doScan

构造方法

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,Environment environment, @Nullable ResourceLoader resourceLoader) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    this.registry = registry;
    if (useDefaultFilters) {
        //添加默认的包含过滤器
        registerDefaultFilters();
    }
    setEnvironment(environment);
    setResourceLoader(resourceLoader);
}

主要是看registerDefaultFilters

protected void registerDefaultFilters() {
    /*
     *注册了@Component过滤器到includeFiters
     *相当于同时注册了所有被@Component注释的注解,
     *包括@Service ,@Repository,@Controller
     *同时也支持java EE6 的javax.annotation.ManagedBean
     *和JSR-330的@Named 注解。
     *这就是为什么@Service @Controller @Repostory @Component 能够起作用的原因。
     */
    this.includeFilters.add(new AnnotationTypeFilter(Component.class));
    //类加载器,这个没啥好解释的
    ClassLoader cl =        
            ClassPathScanningCandidateComponentProvider.class.getClassLoader();
    try {
        // 添加ManagedBean 注解过滤器
        this.includeFilters.add(
           new AnnotationTypeFilter(((Class<? extends Annotation>)           
            ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
    }
    catch (ClassNotFoundException ex) {

    }
    try {
        // 添加Named 注解过滤器
        this.includeFilters.add(
            new AnnotationTypeFilter(((Class<? extends Annotation>)         
                       ClassUtils.forName("javax.inject.Named", cl)), false));

    }
    catch (ClassNotFoundException ex) {   
    }
}

首先这里的includeFilters大家熟悉吗,还有个excludeFilters,先看一下属性

//包含的Filters
private final List<TypeFilter> includeFilters = new LinkedList<>();
//排除的Filters
private final List<TypeFilter> excludeFilters = new LinkedList<>();

这里提前往includeFilters里面添加需要扫描的特定注解

0.添加元注解@Component,需要注意的是@Repository、@Service、@Controller里面都标注了@Component。很好理解,扫描的时候用includeFilters 去过滤时,会找到并处理这4个注解的类。

1.上面源码中的两个注解@ManagedBean、@Named需要有对应的jar包,否则(也就是说把这个方法走完),includeFilters里面只会有一个元素。其实按照spring的加载流程,ClassPathBeanDefinitionScanner到这里的作用就结束,里面的很多重要方法是在流程加载后面用到的,但是既然都是一个类里面的方法,就在这里先讲一下吧。

scan扫描

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new 		
                                            AnnotationConfigApplicationContext();
        //注册配置类
        context.register(Config.class);
        context.scan("com");
        context.refresh();
    }
}

scan方法是开始扫描的入口context.scan("com")->this.scanner.scan(basePackages); 这个scanner就是默认构造函数初始化的ClassPathBeanDefinitionScanner,只有手动调用context.scan("com"); 这个初始化的scanner才有发挥的作用,这个类起始就是为程序员手动扫描用的,spring内部自动扫描会再生成一个ClassPathBeanDefinitionScanner对象完成扫描,从代码角度来讲完成的功能一模一样。

    	/**
         * 扫描给定的包路径,生成BeanDefinition并注册到注册器
         */
        public int scan(String... basePackages) {
            ////获取注册器中已注册的bean数量
            int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
            //通过doScan给定包路径并生成BeanDefinition注册到registry中
            doScan(basePackages);
            // Register annotation config processors, if necessary.
            if (this.includeAnnotationConfig) {
                AnnotationConfigUtils.
                    		registerAnnotationConfigProcessors(this.registry);
            }
            //返回本次扫描并注册到IOC容器中的类
            return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
        }

看源码可知,真正完成扫描的是doScan方法:

/**
 * 扫描给定的包路径,生成BeanDefinition并注册到注册器
 */
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages,"At least one base package must be specified");
    
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    /**
     * 扫描basePackage路径下的java文件
     * 先全部转为Resource,然后再判断拿出符合条件的bd
     */
    for (String basePackage : basePackages) {
        // 调用findCandidateComponents扫描包组装BeanDefinition集合
        //findCandidateComponents方法是从父类继承的方法
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        //遍历BeanDefinition,根据条件将BeanDefinition注册到注册中心
        for (BeanDefinition candidate : candidates) {
            //解析scope属性
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver
                                              .resolveScopeMetadata(candidate);

            candidate.setScope(scopeMetadata.getScopeName());
             //获取beanName,先判断注解上有没有显示设置beanName
             //没有的话,就以类名小写为beanName
            String beanName = this.beanNameGenerator
                                    .generateBeanName(candidate, this.registry);
            if (candidate instanceof AbstractBeanDefinition) {
                /**
                 * 如果这个类是AbstractBeanDefinition类型
                 * 则为他设置默认值,比如lazy/init/destroy
                 * 通过扫描出来的bd是ScannedGenericBeanDefinition
                 * 继承了AbstractBeanDefinition
                 */
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            if (candidate instanceof AnnotatedBeanDefinition) {
                /**
                 * 处理加了注解的类
                 * 把常用注解设置到AnnotationBeanDefinition中
                 */
                AnnotationConfigUtils.processCommonDefinitionAnnotations
                                                        ((AnnotatedBeanDefinition) candidate);
            }
            if (checkCandidate(beanName, candidate)) {
                BeanDefinitionHolder definitionHolder = new 	
                                        BeanDefinitionHolder(candidate, beanName);
                //这个地方主要是解析Scope中的ProxyMode属性,默认为no,不生成代理对象
                definitionHolder =AnnotationConfigUtils
                                    .applyScopedProxyMode
                                        (scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                //注册bd到容器中,将bd放到了beanFactory中的一个map里(beanDefinitionMap)
                // key为beanName,value为definitionHolder
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    //返回扫描到的所有的beanDefinitions
    return beanDefinitions;
}

先关注findCandidateComponents(basePackage);方法

进入到父类ClassPathScanningCandidateComponentProvider。

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
        return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
    } else {
        //完成真正的扫描
        return scanCandidateComponents(basePackage);
    }
}

根据方法名顾名思义,找到候选组件,在指定的包中找到候选组件。

进入scanCandidateComponents(basePackage);

/**
 *  扫描包,生成BeanDefinition集合
 */
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    try {
        // //根据包名组装包扫描路径
        // 如,classpath*:com/**/*.class
        String packageSearchPath = 	
                ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                resolveBasePackage(basePackage) + '/' + this.resourcePattern;
        /*
         resourcePatternResolver(资源加载器)根据匹配规则获取Resource[]
         Resource数组中每一个对象都是对应一个Class文件
         Spring用Resource定位资源,封装了资源的IO操作。
         这里的Resource实际类型是FileSystemResource。资源加载器其实就是容器本身
        */
        Resource[] resources = 
            getResourcePatternResolver().getResources(packageSearchPath);
        boolean traceEnabled = logger.isTraceEnabled();
        boolean debugEnabled = logger.isDebugEnabled();
        //循环处理每一个resource,相当于循环处理每一个class文件
        for (Resource resource : resources) {
            if (traceEnabled) {
                logger.trace("Scanning " + resource);
            }
            if (resource.isReadable()) {
                try {
                    /*
                     *读取类的注解信息和类信息,信息储存到MetadataReader
                     *meteDataFactory根据Resouce获取到MetadataReader对象
                     *MetadataReader提供了获取一个Class文件的
                     *ClassMetadata和AnnotationMetadata的操作。
                     */
                    MetadataReader metadataReader = 		
                       getMetadataReaderFactory().getMetadataReader(resource);
                    /*
                     *判断元数据是否需要组装成BeanDefinition
                     *此处判断当前class是否需要注册到spring的IOC容器中通过IOC容器管理。
                     *spring默认对Component注解的类进行动态注册到IOC容器
                     *通过includeFilters与excludeFilters来判定匹配。
                     */
                    if (isCandidateComponent(metadataReader)) {
                        // //把符合条件的 类转换成 BeanDefinition
                        ScannedGenericBeanDefinition sbd = new 
                                ScannedGenericBeanDefinition(metadataReader);
                        sbd.setSource(resource);
                        // 再次判断 如果是实体类 返回true
                        //如果是抽象类,但是抽象方法 被 @Lookup 注解注释返回true
                        if (isCandidateComponent(sbd)) {
                            if (debugEnabled) {
                                logger.debug
                                    ("Identified candidate component class");
                            }
                            //返回BeanDefinition 注册到 BeanFactory
                            candidates.add(sbd);
                        }
                        else {
                            if (debugEnabled) {
                                logger.debug
                                         ("Ignored,not a concrete top-level-class");
                            }
                        }
                    }
                    else {
                        if (traceEnabled) {
                        logger.trace
                                ("Ignored because not matching anyfilter");
                        }
                    }
                }
                catch (Throwable ex) {
                    throw new BeanDefinitionStoreException
                        ("Failed to read candidate component class: ");
                }
            }
            else {
                if (traceEnabled) {
                    logger.trace("Ignored because not readable: " + resource);
                }
            }
        }
    }
    catch (IOException ex) {
        throw new BeanDefinitionStoreException
                                        ("I/O failure during classpath scanning", ex);
    }
    return candidates;
}

上面有一个组件判断if (isCandidateComponent(metadataReader)) 就是判断当前class文件符不符合扫描过滤器includeFilters与excludeFilters中的定义。最后返回一个符合条件的Set<BeanDefinition>。

    protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
        	//看下是否在排除的filter内 如果是返回false
    		for (TypeFilter tf : this.excludeFilters) {
    			if (tf.match(metadataReader, getMetadataReaderFactory())) {
    				return false;
    			}
    		}
        	//看下是否在包含的filter内 如果是返回true
    		for (TypeFilter tf : this.includeFilters) {
    			if (tf.match(metadataReader, getMetadataReaderFactory())) {
    				return isConditionMatch(metadataReader);
    			}
    		}
    		return false;
    	}

再回到之前的doScan(String... basePackages)findCandidateComponents下面的方法 for (BeanDefinition candidate : candidates),开始循环,处理注解,设置beanDefinition属性最后执行 registerBeanDefinition(definitionHolder, this.registry);注册beanDefinition

跟进此方法:registerBeanDefinition(definitionHolder, this.registry);

BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);

public static void registerBeanDefinition(
       BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
       throws BeanDefinitionStoreException {
   /**
    * 这里的registerBeanDefinition是由父类GenericApplicationContext实现的
    * 跟踪源码可知,是在父类中调用
    * this.beanFactory.registerBeanDefinition(beanName, beanDefinition)
    * 而这个beanFactory是AnnotationConfigApplicationContext
    * 在执行自己的构造方法this()时
    * 先去执行了父类GenericApplicationContext的构造方法
    * 完成了this.beanFactory = new DefaultListableBeanFactory()
    * 所以,最终将beanDefinition注册到了DefaultListableBeanFactory中
    * */
   // Register bean definition under primary name.
   String beanName = definitionHolder.getBeanName();
        //注册beanDefinition
   registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

   // Register aliases for bean name, if any.
   String[] aliases = definitionHolder.getAliases();
   if (aliases != null) {
       for (String alias : aliases) {
           registry.registerAlias(beanName, alias);
       }
   }
}

parse扫描注册完成后,将生成的BeanDefinition返回,还需要把每个Bean检测一遍。因为Scan出来的Bean,还有可能是@Configuration的,因此需要再次交给parse一遍,防止疏漏,换句话说,扫描后的业务类中还有配置类,我们还需要处理这些配置类。getOriginatingBeanDefinition返回原始的 BeanDefinition,没有的话返回 null,链式调用该方法,最终可获取到由用户定义的 BeanDefinition。

步骤5.4:递归调用处理@Import、@ImportSeletor、@DeferredSeletor、@ImportBeanDefinitionRegistrar

注解@ComponentScans,@ComponentScan讲完了,下一步看注解@Import的处理。

//getImports(sourceClass)获取的是@Import(User.class)中的User.class
 processImports(configClass, sourceClass, getImports(sourceClass), true);

processImports方法一共4个参数,congigclas和sourceclass是Config,true:是否检查循环依赖。 那么重点就在getImports(sourceClass);

所以接下来我们先看:getImports(sourceClass);的源码

private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
   Set<SourceClass> imports = new LinkedHashSet<>();
   Set<SourceClass> visited = new LinkedHashSet<>();
   collectImports(sourceClass, imports, visited);
   return imports;
}

初始化了2个set,imports和visited。

见名知意:imports肯定是用来存放@Import注解导入的class的,visited是干嘛的呢?

往下看

private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited) throws IOException {
   if (visited.add(sourceClass)) {
      for (SourceClass annotation : sourceClass.getAnnotations()) {
         String annName = annotation.getMetadata().getClassName();
         if (!annName.equals(Import.class.getName())) {
            collectImports(annotation, imports, visited);
         }
      }
      imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
   }
}

首先可以看出来collectImports是1个递归的方法。

第一步:把Config加入visited集合
第二步:获取Config的所有注解
第三步:遍历Config的所有注解,判断不是@import,递归执行`collectImports();`
第四步:处理sourceClass上的@import注解

以Config上存在@Imoport(User.class)为例

1. 把Config加入visited集合
2. 获取Config的所有注解 @Conifguration 和 @Import
3. 首先处理@Conifguration,判断不是@import,递归执行`collectImports();`
4. 把@Conifguration加入visited集合
5. 获取@Conifguration的所有注解@Component
6. 判断@Component不是@import,递归执行`collectImports();`
7. 把@Component加入visited集合
8. 获取@Component的所有注解@Indexed
9. 判断@Indexed不是@import,递归执行`collectImports();`
10. 把@Indexed加入visited集合
11. 获取@Component的所有注解是个空集合
12. 处理sourceClass上的@import注解即@Indexed上的@import注解的value加入imports
递归结束方法栈...以此类推,最后结果如下:

visited:size = 4
 0 = "imports.Config"
 1 = "org.springframework.context.annotation.Configuration"
 2 = "org.springframework.stereotype.Component"
 3 = "org.springframework.stereotype.Indexed"

imports:size = 1
 0 = "imports.User"

imports就是getImports(sourceClass);的返回值,下面开始处理imports

//importCandidates
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
    // importCandidates为空直接返回 目前importCandidates里面就只有1个User.class
    if (importCandidates.isEmpty()) {
        return;
    }
    //检查循环依赖
    if (checkForCircularImports && isChainedImportOnStack(configClass)) {
        this.problemReporter.error (new CircularImportProblem(configClass, this.importStack));
    }else {
        //入栈
        this.importStack.push(configClass);

        try {
            //遍历循环配置类上的@Import注解上的value属性的值即User.class
            for (SourceClass candidate : importCandidates) {
                if (candidate.isAssignable(ImportSelector.class)) {
                    //第一种情况:candidate是ImportSelector接口类型
                    //比如@Import("User.class")
                    //User实现了ImportSelector接口或者DeferredImportSelector 
                    // 获取ImportSelector对应的class
                    Class<?> candidateClass = candidate.loadClass();
                    // 反射调用ImportSelector的构造方法实例化
                    ImportSelector selector = BeanUtils.instantiateClass
                    (candidateClass, ImportSelector.class);
                    //是否有实现相关Aware接口,如果有,调用相关方法
                    ParserStrategyUtils.invokeAwareMethods(selector, 
                                                this.environment, 
                                                this.resourceLoader, 
                                                this.registry);

                    
                    if (selector instanceof DeferredImportSelector) {
                        //1.如果ImportSelector是DeferredImportSelector 
                        //即延迟加载的selector
                        this.deferredImportSelectorHandler
                            .handle(configClass, (DeferredImportSelector) selector);
                    }else {
                        //2.如果是普通的ImportSelector 
                        //即立即加载的selector,执行其selectImports方法
                        //获取需要导入的类的全限定类名数组
                        String[] importClassNames = selector.selectImports
                        (currentSourceClass.getMetadata());
                        //获取需要导入类的class
                        Collection<SourceClass> importSourceClasses = 
                                asSourceClasses(importClassNames);
                        // 使用导入的类作为配置类 递归调用 processImports方法
                        processImports(configClass,
                                        currentSourceClass,
                                                importSourceClasses, false);
                    }
                }else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                    //第二种情况:
                    //candidate是ImportBeanDefinitionRegistrar接口的实现类
                    //比如@Import("User.class")
                    //User实现了ImportBeanDefinitionRegistrar接口
                    //ImportBeanDefinitionRegistrar的作用是注册一个beanDefinition
                    //获取 ImportBeanDefinitionRegistrar实现类对应的class
                    Class<?> candidateClass = candidate.loadClass();
                    //反射实例化ImportBeanDefinitionRegistrar的实现类
                    ImportBeanDefinitionRegistrar registrar=BeanUtils
                         .instantiateClass(candidateClass,ImportBeanDefinitionRegistrar.class);
                    //是否有实现相关Aware接口,如果有,调用相关方法
                    ParserStrategyUtils
                        .invokeAwareMethods(registrar, this.environment,
                                                this.resourceLoader,this.registry);
                    //将实例化好的ImportBeanDefinitionRegistrar
                    //放入configClass的importBeanDefinitionRegistrars属性
                    //importBeanDefinitionRegistrars是1个map
                    //key是registrar,value是currentSourceClass.getMetadata()
                    //注册BeanDefinition
                    configClass.addImportBeanDefinitionRegistrar
                        (registrar, currentSourceClass.getMetadata());
                }else {
                    //第三种情况
                    //既没有实现ImportSelector接口
                    //也没有实现ImportBeanDefinitionRegistrar接口
                    //candidate是就是1个普通类 上面没有任何注解
                    //比如@Import("User.class")
                    //就算是一个普通的class最后也会被当做带有@Configuration的class去解析
                    //这里是放入了ConfigurationClassParser的importStack等待后续处理
                    this.importStack.registerImport(
                                    currentSourceClass.getMetadata(), 
                                        candidate.getMetadata().getClassName());
                   // 继续解析导入的@Configuration class
                   // 因为解析的已经是一个class 
                   // 所以将class转换为ConfigurationClass继续解析就可以了
                   // 所以递归调用重新进入processConfigurationClass
                   // 解析配置类调用链
                   //ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry                            //ConfigurationClassPostProcessor#processConfigBeanDefinitions
                   //进入ConfigurationClassParser#parse(Set<BeanDefinitionHolder>candidates)
                   //ConfigurationClassParser#parse(AnnotationMetadata,String)
                   //ConfigurationClassParser#processConfigurationClass(ConfigurationClass)
                   processConfigurationClass (candidate.asConfigClass(configClass));
                }
            }
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                            "Failed to process import candidates " +
                                                            "for configuration class" + ex);
        }
        finally {
            //刚才放入了ConfigurationClassParser的importStack等待后续处理
            //现在解析完出栈即可
            this.importStack.pop();
        }
    }
}

@Import注解场景

那下面我们来罗列有哪些添加组件的方式?

1、通过@CompentScan @Controller @Service @Respository @compent

适合场景:我们自己写的组件可以通过这种方式来进行加载到容器中。

2、通过@Bean的方式来导入组件

适合场景: 适用于导入第三方组件的类

3、通过@Import 来导入组件

适合场景:导入组件的id为全路径,用处最多的是其他框架整个Spring时,使用@Import注解导入整合类。

processImports方法负责对@Import注解进行解析。 configClass是配置类,sourceClass又是通过configClass获取的。 getImports(sourceClass)从sourceClass获取@Import注解导入的所有的类,然后调用ConfigurationClassParser#processImports做处理

在应用中,有时没有把某个类注入到IOC容器中,但在运用的时候需要获取该类对应的bean,比如第三方的组件

此时就需要用到@Import注解。

spring首先把import进来的类当作配置类处理,然后递归处理该配置类引入的其他类。

继续打比方: 首先创建一个业务类,且不要放到扫描包下,是不会扫描到IOC容器中的:

public class User {

}

然后@import到配置类上

@Order(1)
@Configuration
@PropertySource({"classpath:demo.properties"})
@ComponentScan(value = "com,net",basePackageClasses = Person.class)
@Import(User.class)
public class Config {
    @Value("${demo.name}")
    private String name;

    public String getName() {
        return name;
    }
}

我们看下processImports的源码怎么处理注解@Import的

if (importCandidates.isEmpty()) {
    return;
}

这行代码的意思是如果没有@Import注解,就返回不做任何处理。

if (checkForCircularImports && isChainedImportOnStack(configClass)) {
     this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}else {
    //三种情况
}

if这行判断是解决循环引用的,暂不关心,以后会详细将循环引用问题。

第一次的话执行else语句,然后拿出@Import注解中的所有类进行循环处理,这里只有User。

if语句中有三种处理情况:

1.实现ImportSelector接口

2.实现ImportBeanDefinitionRegistrar接口

3.既没有实现ImportSelector接口也没有实现ImportBeanDefinitionRegistrar接口

1.实现了ImportSelector接口

ImportSelector接口:ImportSelector接口只定义了一个selectImports(),用于指定需要注册为bean的Class名称。当在@Configuration标注的Class上使用@Import引入了一个ImportSelector实现类后,会把实现类中返回的Class名称(是1个字符串数组) 都定义为bean。

来看一个简单的示例,假设现在有一个接口HelloService,需要把所有它的实现类都定义为bean,而且它的实现类上是没有加Spring默认会扫描的注解的,比如@Component、@Service等。 首先让上文的User类实现ImportSelector接口,然后在方法中返回User2全限定名,这样spring就会将User2添加到容器中。

public class User2 {
}
//因为@Import(User.class)会加载Uuser
//加载User时发现User 实现了 ImportSelector
//会将selectImports 返回的bean都加入到工厂中
public class User implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[] {User2.class.getName()};
    }
}

递归调用processImports方法解析ImportSelector接口导入的类

为什么要递归调用,因为你导入的类可能还有Import注解啊,那也要处理的!

递归调用processImports,看看ImportSelector接口导入的Class是不是也导入了新的类。

//如果实现了ImportSelector接口
if (candidate.isAssignable(ImportSelector.class)) {

    Class<?> candidateClass = candidate.loadClass();
    // 反射创建这个类的实例对象
    ImportSelector selector = 
            BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
    //是否有实现相关Aware接口,如果有,调用相关方法
    ParserStrategyUtils
            .invokeAwareMethods
                    (selector, this.environment, this.resourceLoader, this.registry);
    // 延迟加载的ImportSelector
    if (selector instanceof DeferredImportSelector) {
    //  延迟加载的ImportSelector先放到List中,延迟加载
    this.deferredImportSelectorHandler
                    .handle(configClass, (DeferredImportSelector) selector);
    }else {
    // 普通的ImportSelector ,执行其selectImports方法,获取需要导入的类的全限定类名数组
    String[] importClassNames = selector.selectImports
                                                                    (currentSourceClass.getMetadata());
    Collection<SourceClass> importSourceClasses = 			
                                                                    asSourceClasses(importClassNames);
    // 递归调用processImports 看看User导入的Class是不是有导入了新的类
    processImports(configClass, currentSourceClass, importSourceClasses, false);
    }
}

代码很简单,相信大家能看懂,但是有这么行代码

    ParserStrategyUtils.invokeAwareMethods(
                                    selector, 
                                    this.environment, 
                                    this.resourceLoader, 
                                    this.registry);

判断是否有实现相关Aware接口,如果有,这调用相关方法。Aware,是感应和感知的意思。当bean实现了对应的Aware接口时,BeanFactory会在生产bean时根据它所实现的Aware接口,给bean注入对应的属性,从而让bean获取外界的信息。Aware是顶级接口,它有很多子接口:

13.postProcessBeanDefinitionRegistry源码(下)

如果我们被Import的User类实现了Aware接口,就会在上面invokeAwareMethods源码内调用实现该接口的方法,但是在此处不是所有有Aware接口都可以,invokeAwareMethods源码:

public static void invokeAwareMethods(Object parserStrategyBean, Environment 	
environment,ResourceLoader resourceLoader, BeanDefinitionRegistry registry) {

    if (parserStrategyBean instanceof Aware) {
        if (parserStrategyBean instanceof BeanClassLoaderAware) {
            ClassLoader classLoader =
                        (registry instanceof ConfigurableBeanFactory ?
                    ((ConfigurableBeanFactory) registry).getBeanClassLoader() 						  : 
                    resourceLoader.getClassLoader());
            if (classLoader != null) {
                ((BeanClassLoaderAware) parserStrategyBean)
                                         .setBeanClassLoader(classLoader);
            }
        }

        if (parserStrategyBean instanceof BeanFactoryAware &&
                                                        registry instanceof BeanFactory) {
            ((BeanFactoryAware) parserStrategyBean)
                                        .setBeanFactory((BeanFactory) registry);
        }
        if (parserStrategyBean instanceof EnvironmentAware) {
            ((EnvironmentAware) parserStrategyBean)
                                        .setEnvironment(environment);
        }
        if (parserStrategyBean instanceof ResourceLoaderAware) {
            ((ResourceLoaderAware) parserStrategyBean)
                                        .setResourceLoader(resourceLoader);
        }
    }
}

只有BeanClassLoaderAware、BeanFactoryAware 、EnvironmentAware、ResourceLoaderAware接口才能被调用,其他Aware接口啥时候调用以后再说。举个例子:

public class User implements ImportSelector, BeanFactoryAware {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[] {User2.class.getName()};
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("获取到了bean工厂");
    }
}

也就是说我们能在User实现接口的方法内操作以下方法:

13.postProcessBeanDefinitionRegistry源码(下)

Aware接口处理完后,就是判断User是否是延迟加载:

if (selector instanceof DeferredImportSelector)

显然,我们的User实现的ImportSelector接口,没有实现DeferredImportSelector接口,所以就不是延迟加载。

如果不是延迟加载的话,if条件不执行,执行else中的语句:

// 普通的ImportSelector ,执行其selectImports方法,获取需要导入的类的全限定类名数组
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
//selectImports返回的类的全限定类名数组对应的Class
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
// 递归调用
processImports(configClass, currentSourceClass, importSourceClasses, false);

为什么要递归调用,因为你导入的类可能还有Import注解啊,那也要处理的!

2.实现了DeferredImportSelector

DeferredImportSelector是ImportSelector的子接口。

如果我们要实现延迟加载功能,让User实现DeferredImportSelector接口即可:

public class User implements DeferredImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        System.out.println("延迟加载User2");
        return new String[] {User2.class.getName()};
    }
}

// 反射创建实现DeferredImportSelector这个类的实例对象
ImportSelector selector = 
        BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
//是否有实现相关Aware接口,如果有,调用相关方法
ParserStrategyUtils
        .invokeAwareMethods
                (selector, this.environment, this.resourceLoader, this.registry);
if (selector instanceof DeferredImportSelector) {
        this.deferredImportSelectorHandler.handle
                          (configClass, (DeferredImportSelector) selector);
}

启动spring,就会进入上面的if判断里的

this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);

handle方法内会将selector再封装成DeferredImportSelectorHolder类型的对象,然后保存在数组中等以后调用。

调用时机是在parse(Set<BeanDefinitionHolder> configCandidates)方法的最后一行

/****
@Nullable
private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();
***/
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
    //封装成DeferredImportSelectorHolder类型的对象
    DeferredImportSelectorHolder holder 
        = new DeferredImportSelectorHolder(configClass, importSelector);
    if (this.deferredImportSelectors == null) {
        DeferredImportSelectorGroupingHandler handler 
                        = new DeferredImportSelectorGroupingHandler();
        handler.register(holder);
        handler.processGroupImports();
    }
    else {
        //加入ConfigurationClassParser的属性deferredImportSelectors
        this.deferredImportSelectors.add(holder);
    }
}

3.实现了ImportBeanDefinitionRegistrar接口:加入集合

ImportBeanDefinitionRegistrar接口是也是spring的扩展点之一,它可以支持我们自己写的代码封装成BeanDefinition对象;实现此接口的类会回调postProcessBeanDefinitionRegistry方法,注册到spring容器中。把bean注入到spring容器不止有 @Service @Component等注解方式;还可以实现此接口。

比如:

public class User implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        BeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClassName(User2.class.getName());
        MutablePropertyValues values = beanDefinition.getPropertyValues();
        values.addPropertyValue("id", 1);
        values.addPropertyValue("name", "源码之路");
        //这里注册beanDefinition
        registry.registerBeanDefinition("testBean", beanDefinition );
    }
}

else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
    // Candidate class is an ImportBeanDefinitionRegistrar ->
    // delegate to it to register additional bean definitions
    Class<?> candidateClass = candidate.loadClass();
    //实例化
    ImportBeanDefinitionRegistrar registrar 
        = BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
    //调用Aware接口
    ParserStrategyUtils.invokeAwareMethods(
                    registrar, this.environment, this.resourceLoader, this.registry);
    //加入ConfigurationClassParser的属性importBeanDefinitionRegistrars
    configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}

4.@Import导入的普通类

最后,既没有实现ImportSelector接口也没有实现ImportBeanDefinitionRegistrar接口:

会被作为普通的带有@Configuration的类解析,但是并不会加入BeanDefinitionMap

会被作为普通的带有@Configuration的类解析,但是并不会加入BeanDefinitionMap

会被作为普通的带有@Configuration的类解析,但是并不会加入BeanDefinitionMap

 this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());

// 解析@Import导入的类
// 会被作为普通的带有@Configuration的类解析
// 注意processImports和processConfigurationClass的区别!
//  processImports最后会被作为bd注册
//  processConfigurationClass则不会
processConfigurationClass(candidate.asConfigClass(configClass));

// 继续解析@Import导入的类
// 因为解析的已经是一个class 所以将class转换为ConfigurationClass继续解析就可以了
// 所以递归调用重新进入processConfigurationClass


//解析配置类调用链
//ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry                 //ConfigurationClassPostProcessor#processConfigBeanDefinitions
//进入ConfigurationClassParser#parse(Set<BeanDefinitionHolder> configCandidates)
//ConfigurationClassParser#parse(AnnotationMetadata,String)
//ConfigurationClassParser#processConfigurationClass(ConfigurationClass)

按普通配置类处理~

@Import注解处理暂时先告一段落,下面继续看@ImportResource注解的处理。

步骤5.5@ImportResource

@ImportResource用来导入Spring 的配置文件,如核心配置文件 "beans.xml",从而让配置文件里面的内容生效。

讲道理,大家平时都不用xml配置文件来做开发了吧,注解方式差不多完全替代XML方式了。这个也很简单,不讲了,这篇文章还能坚持看到这里的读者,想必有能力分析这块的源码了。

注意,这块源码的处理并没有解析配置文件,而是先保存在map中,后续解析。先记着,整个方法分析完了你就知道了。

步骤5.5:递归调用处理@Bean注解的方法

    //找到含有@Bean注解的方法
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
        //先添加到配置类的beanMethods集合中
        configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }
    //下一步,处理配置类所实现接口的缺省方法:
    // java8中,新增加了一个特性,接口中可以有默认实现方法:
    // Process default methods on interfaces

    //找到配置类的所有接口,遍历接口
    //找到所有含有@Bean注解的默认方法
    //过滤抽象方法
    //最后把符合条件的也加入到配置类的beanMethods集合中
    processInterfaces(configClass,sourceClass);

步骤5.5.1: 找到含有@Bean注解的方法放入集合

private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {
    AnnotationMetadata original = sourceClass.getMetadata();
    //获取带有@Bean注解的方法
    //将@Bean方法转化为BeanMethod对象,保存在集合中,后续再用。
    Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());
    //遍历beanMethods
    if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) {
        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()) {
                        beanMethods = selectedMethods;
                }
            }
        }catch (IOException ex) {
            logger.debug("Failed to read class file via ASM "+ 
                                    "for determining@Bean method order", ex);
        }
    }
    return beanMethods;
}

将@Bean方法转化为BeanMethod对象,保存在集合中,后续再用。

步骤5.5.2: 递归获取配置类及父类接口中默认方法加入集合

下一步,处理配置类所实现接口的缺省方法:java8中,新增加了一个特性,接口中可以有默认实现方法:

public interface UserInterface {
    /*
     * 默认方法,有方法体
     * 任何一个实现了UserInterface接口的类都会向动继承isEmpty的实现
     */
    @Bean
    default User2 getUser2() {
        return new User2();
    }
}

默认方法,有方法体,任何一个实现了Sized接口的类都会向动继承isEmpty的实现,这个默认方法加一个@Bean注解的话,且返回一个对象,spring会将这个对象注册到spring容器中。

源码:

processInterfaces(configClass, sourceClass);就是完成这个功能的。这个方法的源码也比较简单:

private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
    //找到配置类的所有接口,遍历接口
    for (SourceClass ifc : sourceClass.getInterfaces()) {
        //找到含有@Bean注解的默认方法
        Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(ifc);

        for (MethodMetadata methodMetadata : beanMethods) {
            //过滤抽象方法
            if (!methodMetadata.isAbstract()) {
                //添加到集合
                configClass.addBeanMethod
                            (new BeanMethod(methodMetadata, configClass));
            }
        }
        //递归,因为接口可能有父接口
        processInterfaces(configClass, ifc);
    }
}

最后,最后,最后一步,如果有父类,则解析父类。坚持下!

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

如果有父类则返回父类Class对象,继续调用该方法。直到返回null,外层循环结束。怎么讲?doProcessConfigurationClass此时结束了,我们看调用此方法的上一层方法processConfigurationClass。

以上省略
do {
    // 真正的做解析
    //返回的 sourceClass 是刚才解析的configClass的父类
    sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}while (sourceClass != null);

就是说,如果你有父类,此时返回来的sourceClass 是父类,继续递归调用doProcessConfigurationClass方法,直到返回的是null后while循环才结束。

如果没有父类,则执行doProcessConfigurationClass方法的最后一行代码return null;,此时的while循环也结束了,继续执行最后一行代码:this.configurationClasses.put(configClass, configClass);

这行代码的意思是,需要被处理的配置类configClass已经被分析处理,将它记录到已处理配置类记录。

至此,我们的processConfigurationClass->doProcessConfigurationClass方法分析完了,往回返,一直到入口处,即ConfigurationClassPostProcessor.processConfigBeanDefinitions方法中parser.parse(candidates);代码处,对吧,上面讲了那么多,就是讲了parse方法的处理。

步骤5.5.3处理DeferredImportSelector

deferredImportSelectorHandler.process()

入口:this.deferredImportSelectorHandler.process();


public void process() {
        //在处理@Import注解时
        //DeferredImportSelector的实现类已经实例化
        //并封装为DeferredImportSelectorHolder
        //放入deferredImportSelectors集合
        List<DeferredImportSelectorHolder> deferredImports
                                                = this.deferredImportSelectors;
        this.deferredImportSelectors = null;
        try {
            if (deferredImports != null) {
                DeferredImportSelectorGroupingHandler handler
                 = new DeferredImportSelectorGroupingHandler();
                deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
                deferredImports.forEach(handler::register);
                //进入这里
                handler.processGroupImports();
            }
        }
        finally {
            this.deferredImportSelectors = new ArrayList<>();
        }
    }
}

processGroupImports

public void processGroupImports() {
    //groupings是什么?
    for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
        //调用getImports
        grouping.getImports().forEach(entry -> {
            ConfigurationClass configurationClass = 
                        this.configurationClasses.get(entry.getMetadata());
            try {
                //调用processImports方法
                //也就是5.4步的processImports方法
                //将这些被DeferredSelectImporter接口导入的类
                //作为普通的@Configuration的Class被解析
                processImports(configurationClass, asSourceClass(configurationClass),
                                       asSourceClasses(entry.getImportClassName()), false);
            }
            catch (BeanDefinitionStoreException ex) {
                throw ex;
            }
            catch (Throwable ex) {
                throw new BeanDefinitionStoreException
                        ("Failed to process import candidates"for configuration class ", ex);
            }
        });
    }
}

getImports
public Iterable<Group.Entry> getImports() {
    for (DeferredImportSelectorHolder deferredImport :  this.deferredImports) {
        //调用process方法
        this.group.process(deferredImport.getConfigurationClass().getMetadata(),
                                                deferredImport.getImportSelector());
    }
    //返回的是调用selector的selectImports
    return this.group.selectImports();
}

@Override
public void process(AnnotationMetadata metadata,DeferredImportSelector selector) {
    //调用selector的selectImports
    for (String importClassName : selector.selectImports(metadata)) {
        this.imports.add(new Entry(metadata, importClassName));
    }
}

步骤5.6:校验配置类

5.6.1配置类不能为final

5.6.2校验是否可以被重写

我们继续分析parse的后续源码,坚持住,马上看到曙光了。

public void validate() {
//遍历所有的 configurationClasses
for (ConfigurationClass configClass : this.configurationClasses.keySet()) {
        configClass.validate(this.problemReporter);
    }
}

校验所有的configurationClasses以及校验所有的configurationClasses的@Bean方法是否可以被重写

public void validate(ProblemReporter problemReporter) {
    //配置类不能为final (CGLIB limitation)
    if (getMetadata().isAnnotated(Configuration.class.getName())) {
        if (getMetadata().isFinal()) {
                problemReporter.error(new FinalConfigurationProblem());
        }
    }
    //获取configurationClass的所有的@Bean修饰的方法
    for (BeanMethod beanMethod : this.beanMethods) {
            beanMethod.validate(problemReporter);
    }
}

@Bean修饰的方法主要包括两方面的校验:

public void validate(ProblemReporter problemReporter) {
    if (getMetadata().isStatic()) {
        // @Bean修饰的方法是静态的可以直接返回
        return;
    }
    //如果配置类被加了@Configuration注解
    if (this.configurationClass.getMetadata().isAnnotated(Configuration.class.getName())) {
        //如果不可以被重写,那么不能被CGLIB
        //就报告错误
        if (!getMetadata().isOverridable()) {
            // instance @Bean methods within @Configuration classes 
            // must be overridable to accommodate CGLIB
            problemReporter.error(new NonOverridableMethodError());
        }
    }
}

@Override
public boolean isOverridable() {
    //非静态
    //非final
    //非私有
    return (!isStatic() && !isFinal() && ((this.access & Opcodes.ACC_PRIVATE) == 0));
}

0.配置类不能是final(CGLIB限制)

1.@Configuration配置类中的 @Bean方法必须可重写(非final&& 非静态 && 非私有) 以容纳CGLIB。

步骤5.7:loadBeanDefinitions

5.7.1加载ImportedResource导入的bd

5.7.2加载ImportBeanDefinitionRegistrar导入的bd

入口:this.reader.loadBeanDefinitions(configClasses);


public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
    TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
    //遍历所有的配置类
    for (ConfigurationClass configClass : configurationModel) {
        loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
    }
}

    private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

    if (trackedConditionEvaluator.shouldSkip(configClass)) {
        String beanName = configClass.getBeanName();
        if (StringUtils.hasLength(beanName)&&this.registry.containsBeanDefinition(beanName)) {
            this.registry.removeBeanDefinition(beanName);
        }
        this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
        return;
    }
    //判断配置类是否被@Import注解导入的类
    if (configClass.isImported()) {
        //如果是 注入@Import注解导入的类对应的bd
        //默认是用的类的全限定名
        registerBeanDefinitionForImportedConfigurationClass(configClass);
    }
    //遍历配置类中带有@Bean的方法
    for (BeanMethod beanMethod : configClass.getBeanMethods()) {
        //注入@Bean的方法对应的bd
        loadBeanDefinitionsForBeanMethod(beanMethod);
    }
    //从ImportedResource加载bd
    loadBeanDefinitionsFromImportedResources
            (configClass.getImportedResources());
    //实现了ImportBeanDefinitionRegistrar接口的类
    //会被加入集合importBeanDefinitionRegistrars
    //configClass.getImportBeanDefinitionRegistrars()就是这个集合
    //遍历importBeanDefinitionRegistrars集合
    //从ImportBeanDefinitionRegistrar加载bd
    loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

在这里统一处理没有注册的进行注册,将上一步parser解析出的ConfigurationClass类加载成BeanDefinition,实际上经过上一步的parse()后,解析出来的bean已经放入到BeanDefinition中了,但是由于这些bean可能会引入新的bean。

例如实现了ImportBeanDefinitionRegistrar或者实现Import接口的bean,或者bean中存在被@Bean注解的方法,因此需要执行一次loadBeanDefinition()。

这样就会执行ImportBeanDefinitionRegistrar或者ImportSelector接口的方法或者@Bean注释的方法。

在这里统一处理没有注册的进行注册,将上一步parser解析出的ConfigurationClass类加载成BeanDefinition,实际上经过上一步的parse()后,解析出来的bean已经放入到BeanDefinition中了,但是由于这些bean可能会引入新的bean,例如实现了ImportBeanDefinitionRegistrar或者ImportSelector接口的bean,或者bean中存在被@Bean注解的方法,因此需要执行一次loadBeanDefinition(),这样就会执行ImportBeanDefinitionRegistrar或者ImportSelector接口的方法或者@Bean注释的方法。是不是蒙蔽了这里,其实之前的源码都经历过了,建议大家收藏这篇博文,对照着源码反复看。 再有20多行代码就分析完了,加油!

do {
    parser.parse(candidates);
    parser.validate();

    Set<ConfigurationClass> configClasses =
            new LinkedHashSet<>(parser.getConfigurationClasses());
    configClasses.removeAll(alreadyParsed);
    // Read the model and create bean definitions based on its content
    if (this.reader == null) {
        this.reader = new ConfigurationClassBeanDefinitionReader
            (registry, 
             this.sourceExtractor, 
             this.resourceLoader, 
             this.environment,
             this.importBeanNameGenerator, 
             parser.getImportRegistry());
    }
    this.reader.loadBeanDefinitions(configClasses);
    alreadyParsed.addAll(configClasses);

    candidates.clear();
    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());
        }
        // 如果有未解析的类,则将其添加到candidates中
        //这样candidates不为空,就会进入到下一次的while的循环中
        for (String candidateName : newCandidateNames) {
            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();
    }
}

这里判断registry.getBeanDefinitionCount() > candidateNames.length的目的是为了知道reader.loadBeanDefinitions(configClasses)这一步有没有向BeanDefinitionMap中添加新的BeanDefinition,实际上就是看配置类, 如果有,registry.getBeanDefinitionCount()就会大于candidateNames.length ,这样就需要再次遍历新加入的BeanDefinition,并判断这些bean是否已经被解析过了,如果未解析,需要重新进行解析,这里的Config类向容器中添加的bean,实际上在parser.parse()这一步已经全部被解析了,所以为什么还需要做这个判断,目前没看懂,似乎没有任何意义。 好了,到此为止,ConfigurationClassPostProcessor 中最牛逼的postProcessBeanDefinitionRegistry的方法就分析完了,spring启动过程中,会自动调用改处理器的postProcessBeanDefinitionRegistry方法完成扫描与注册。

用框图总结一下!

示例代码:processImports

package parse;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import({User.class,Person.class,People.class,Normal.class})
public class Config {
    public Config() {
        System.out.println("------------------------Config实例化了");
    }
}
package parse;
public class ImportedByPerson {
}
package parse;

public class ImportedByUser {
    public ImportedByUser() {
        System.out.println("ImportedByUser");
    }
}
package parse;

public class Normal {
}

package parse;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.context.annotation.Primary;
import org.springframework.core.type.AnnotationMetadata;
@Primary
public class People implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        System.out.println(importingClassMetadata);
        GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
        genericBeanDefinition.setBeanClass(People.class);
        registry.registerBeanDefinition("people", genericBeanDefinition);
    }
}

package parse;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

public class Person implements ImportSelector {
    private int age =31;
    private String name;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        String name = ImportedByPerson.class.getName();
        return new String[]{name};
    }
}

package parse;

import org.springframework.context.annotation.DeferredImportSelector;
import org.springframework.context.annotation.Description;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.stereotype.Component;
//为了方便测试可以先注释掉
//@Component
@Description("业务类")
public class User implements DeferredImportSelector {
    private int age =31;
    private String name;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        String name = ImportedByUser.class.getName();
        return new String[]{name};
    }
}


package parse;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;

public class ParseTest {
    public static void main(String[] args) throws InterruptedException {
        start1();
    }

     public static void start1() {
        //其中Config.class可以指定为
        //包扫描启动:@ComponentScan("tyrant")
        //使用xml文件启动:@ImportResource("applicationContext.xml")
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
         //注册配置类
        context.register(Config.class);
        context.refresh();
         Object user = context.getBean("user");
         System.out.println(user);
     }
    public static void start2() {
        //其中Config.class可以指定为
        //包扫描启动:@ComponentScan("tyrant")
        //使用xml文件启动:@ImportResource("applicationContext.xml") 
        AnnotationConfigApplicationContext context = new            
                                                AnnotationConfigApplicationContext(Config.class);
        //加载或者刷新当前的配置信息
        context.refresh();
    }
}

processImports逻辑梳理

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
        Collection<SourceClass> importCandidates, boolean checkForCircularImports) {

    if (importCandidates.isEmpty()) {
        return;
    }
    //检测Import循环依赖
    if (checkForCircularImports && isChainedImportOnStack(configClass)) {
        this.problemReporter.error
                (new CircularImportProblem(configClass, this.importStack));
    }else {
        this.importStack.push(configClass);
        try {
            //遍历的importCandidates 是@Import导入的类集合
            // result = {LinkedHashSet@1112}  size = 3
            // 0 = {ConfigurationClassParser$SourceClass@1192} "parse.User"
            // 1 = {ConfigurationClassParser$SourceClass@1193} "parse.Person"
            // 2 = {ConfigurationClassParser$SourceClass@1194} "parse.People"
            for (SourceClass candidate : importCandidates) {
                //如果导入的类是实现了ImportSelector接口
                if (candidate.isAssignable(ImportSelector.class)) {
                    // 处理 ImportSelector
                    Class<?> candidateClass = candidate.loadClass();
                    //实例化ImportSelector
                    ImportSelector selector
                            = BeanUtils.instantiateClass
                                (candidateClass, ImportSelector.class);
                    //处理Aware接口
                    ParserStrategyUtils.invokeAwareMethods(
                        selector,
                        this.environment, 
                        this.resourceLoader, 
                        this.registry);
                    //判断是否是DeferredImportSelector 延迟加载的selector
                    if (selector instanceof DeferredImportSelector) {
                        this.deferredImportSelectorHandler
                            .handle(configClass, 
                                   (DeferredImportSelector) selector);
                    }
                    //ImportSelector 立即加载的selector
                    else {
                        //普通的ImportSelector 
                        //执行其selectImports方法,获取需要导入的类的全限定类名数组
                        String[] importClassNames 
                                = selector.selectImports
                                    (currentSourceClass.getMetadata());
                        //遍历selectImports方法导入的类名
                        Collection<SourceClass> importSourceClasses
                                        = asSourceClasses(importClassNames);
                        // 递归调用
                        processImports(configClass, 
                                        currentSourceClass, 
                                        importSourceClasses, 
                                        false);
                    }
                }
                //处理实现ImportBeanDefinitionRegistrar接口的类
                else if (candidate.isAssignable
                         (ImportBeanDefinitionRegistrar.class)) {

                    Class<?> candidateClass = candidate.loadClass();
                    ImportBeanDefinitionRegistrar registrar =
                    BeanUtils.instantiateClass
                       (candidateClass, ImportBeanDefinitionRegistrar.class);
                    ParserStrategyUtils.invokeAwareMethods(
                            registrar, 
                            this.environment, 
                            this.resourceLoader, 
                            this.registry);
                    configClass.addImportBeanDefinitionRegistrar
                            (registrar, currentSourceClass.getMetadata());
                }else {
                    //@Import导入的类既没有实现ImportSelector接口
                    //也没有实现ImportBeanDefinitionRegistrar接口
                    //也就是最后@Import导入的类一定会被当做普通的配置类解析
                    // 普通 @Configuration class
                    this.importStack.registerImport(
                        currentSourceClass.getMetadata(), 
                        candidate.getMetadata().getClassName());
                    // 递归解析导入的@Configuration class
                    processConfigurationClass
                        (candidate.asConfigClass(configClass));
                }
            }
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                    "Failed to process import candidates for configuration class [" +
                    configClass.getMetadata().getClassName() + "]", ex);
        }
        finally {
            this.importStack.pop();
        }
    }
}

OK 最后总结一下

步骤1:解析Config获取Config的@Import导入的几个类
public class User implements DeferredImportSelector
public class Person implements ImportSelector
public class People implements ImportBeanDefinitionRegistrar 
public class Normal

步骤2:先处理User,User实现了DeferredImportSelector接口。将User对应的bd封装为DeferredImportSelectorHolder并加入集合
this.deferredImportSelectors.add(holder);
注意User对应的bd没有被注册!

步骤3:处理Person,Person实现了ImportSelector接口,调用Person的selectorImports方法,并递归调用processImports方法解析Person的selectorImports方法返回的类ImportedByPerson。
ImportedByPerson没有实现任何接口会被当做普通的配置类处理!加入配置类集合。
this.configurationClasses.put(configClass, configClass);
注意Person对应的bd没有被注册!

步骤4:处理People,People实现了ImportBeanDefinitionRegistrar接口。会把实例化Peole并加入Config的集合importBeanDefinitionRegistrars。

步骤5:处理Normal,Normal什么都没有。作为普通类处理!加入配置类集合。
this.configurationClasses.put(configClass, configClass);

步骤6:this.deferredImportSelectorHandler.process();
调用User的selectorImports方法,并递归调用processImports方法解析Person的selectorImports方法返回的类ImportedByUser。
ImportedByUser没有实现任何接口会被当做普通的配置类处理!加入配置类集合。
this.configurationClasses.put(configClass, configClass);
注意User对应的bd没有被注册!

步骤7:this.reader.loadBeanDefinitions(configClasses);configClasses有以下几个
ImportedByPerson
Normal
Config
ImportedByUser
主要对上面的配置类作如下操作
//判断配置类是否被@Import注解导入的,如果是那么注入对应的bd
//遍历配置类中带有@Bean的方法注入bd
//从ImportedResource加载bd
//从ImportBeanDefinitionRegistrar加载bd

步骤8:
ImportedByPerson被 ImportSelector注解导入,注册ImportedByPerson对应的bd

步骤:9
Normal被@Import注解导入,注册Normal对应的bd

步骤10:Config作为配置类被解析,People实现了ImportBeanDefinitionRegistrar接口。会把Peole加入Config的集合importBeanDefinitionRegistrars。
在这里会从importBeanDefinitionRegistrars取出实现了ImportBeanDefinitionRegistrar接口的实例。调用People的
registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry)

importingClassMetadata是导入People的类上面的注解信息,也就是Config上的注解信息!

步骤11:ImportedByPerson被DeferredImportSelector导入,注册ImportedByPerson对应的bd!