likes
comments
collection
share

13.postProcessBeanDefinitionRegistry源码(下)

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

第一部分主要是讲遍历候选的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   
//其实就是1个
parser.parse(candidates);
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));
}
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
    //获取配置类的@Condition注解 判断是否需要跳过
    if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), 
                                ConfigurationPhase.PARSE_CONFIGURATION)) {
        return;
    }

    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了,那怎么办?不让spring做判断,手动注册BeanDefinition。

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

打断点调试:

13.postProcessBeanDefinitionRegistry源码(下)

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

发现确实会return,Config对应的bd确实被注册上去了。但是还是有问题,不会作为配置类生效!!!!!

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

返回父类表示当前配置类处理尚未完成,调用者processConfigurationClass会继续处理其父类;返回null才表示该配置类的处理完成。从这里可以推断一旦一个配置类被processConfigurationClass处理完成,表示其自身,内部嵌套类,各个实现接口以及各级父类都被处理完成。

步骤5.3:doProcessConfigurationClass

	
	@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 annotations
		for (AnnotationAttributes propertySource : 
             	AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
            
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}else {
                //打印日志
			}
        }

		//获取 @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 
			for (AnnotationAttributes componentScan : componentScans) {
				 //调用componentScanParser扫描bd
                 //底层调用的是ClassPathBeanDefinitionScanner
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
					this.componentScanParser.parse 	
                    	(componentScan,sourceClass.getMetadata().getClassName());
                   
 				//遍历解析的scanner扫描出来的bd
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = 
                        holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils
                        .checkConfigurationClassCandidate
                        	(bdCand, this.metadataReaderFactory)) {
                        //解析Scanner扫描出来的BeanDefinition
						//这里是一个递归处理 又进入了processConfigurationClass方法
                        //也就是说如果我有
                        //带有@ComponentScan的配置类Config 
                        //带有@Component的User类
                        //先解析Config
                        //先通过Config类上面的@ComponentScan构造1个scanner去扫描到User
                        //然后开始解析User
                        //流程和上面1一样先解析内部类
                        //假设这里User已经被解析完毕继续往下走
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

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

		// Process any @ImportResource annotations
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor
            		(sourceClass.getMetadata(), ImportResource.class);
		if (importResource != null) {
			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);
			}
		}

		// Process individual @Bean methods
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

		// Process default methods on interfaces
		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) {
    				//判断内部类是不是配置类,上文讲过了如何判断是不是配置类
                    //如果是@Configuration 就是full
    				//如果是下面这些注解或者是带有@Bean的方法 就是Lite
    				/*
                    candidateIndicators.add(Component.class.getName());
                    candidateIndicators.add(ComponentScan.class.getName());
                    candidateIndicators.add(Import.class.getName());
                    candidateIndicators.add(ImportResource.class.getName());
                    */
    				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;
                    }
                }
            }
        }

这段源码,小编没有分析过,我就不误人子弟了,但是这段代码的执行结果就是将我们自定义的资源文件添加到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的用法

    扫描指定类文件
       @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)
     

    @ComponentScans(value = {
            @ComponentScan(value = "com.yibai.spring.annotation"),
            @ComponentScan(value = "com.yibai.spring.annotation", 
                           includeFilters = {@Filter(type = FilterType.CUSTOM, 
                                                     value = ColorBeanLoadFilter.class) 
                                            }) 
            })

源码如下

    //OK,下一步处理配置类上的注解@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)) {
                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);
                        definitionHolder =AnnotationConfigUtils.
                                				applyScopedProxyMode
                            		(scopeMetadata, definitionHolder, this.registry);
                        beanDefinitions.add(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
    //即使User上也有注解@Import(ImportedByUser.class)
    //getImports(sourceClass) 返回的也是 User
    //User会被当做普通的配置类处理解析
    //解析的时候自然会解析User注解@Import(ImportedByUser.class)导入的ImportedByUser
     processImports(configClass, sourceClass, getImports(sourceClass), true);
    //importCandidates
    private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
    		// importCandidates为空直接返回
    		if (importCandidates.isEmpty()) {
    			return;
    		}
        	//检查循环依赖
    		if (checkForCircularImports && isChainedImportOnStack(configClass)) {
    			this.problemReporter.error
                    	(new CircularImportProblem(configClass, this.importStack));
    		}else {
                //入栈
    			this.importStack.push(configClass);
                
    			try {
                    //遍历循环配置类上的@Import注解上的value属性的值
    				for (SourceClass candidate : importCandidates) {
                        //第一种情况:candidate是ImportSelector接口类型
                        //比如@Import("User.class")
                        //User实现了ImportSelector接口或者DeferredImportSelector接口
    					if (candidate.isAssignable(ImportSelector.class)) {
    						// 获取ImportSelector对应的class
    						Class<?> candidateClass = candidate.loadClass();
                            // 反射调用ImportSelector的构造方法实例化
    						ImportSelector selector 
                                = BeanUtils.instantiateClass
                                		(candidateClass, ImportSelector.class);
                            //是否有实现相关Aware接口,如果有,调用相关方法
    						ParserStrategyUtils.invokeAwareMethods(
    								selector, 
                                	this.environment, 
                                	this.resourceLoader, 
                                	this.registry);
                            
    						//1.如果ImportSelector是DeferredImportSelector 
                            //即延迟加载的selector
    						if (selector instanceof DeferredImportSelector) {
    							this.deferredImportSelectorHandler.handle
                                    (configClass, (DeferredImportSelector) selector);
    						}else {
                             //2.ImportSelector 立即加载的selector
    						 //普通的ImportSelector接口实现类 ,执行其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
                            //注册BeanDefinition
    						configClass.addImportBeanDefinitionRegistrar
                                	(registrar, currentSourceClass.getMetadata());
    					}
                        //第三种情况
    					//既没有实现ImportSelector接口
                        //也没有实现ImportBeanDefinitionRegistrar接口
                        //candidate是就是1个普通类 上面没有任何注解
                        //比如@Import("User.class")
                        //就算是一个普通的class也会被当做带有@Configuration的class去解析
    					else {
    						// 普通 @Configuration class
    						this.importStack.registerImport(
    								currentSourceClass.getMetadata(), 
                                	candidate.getMetadata().getClassName());
    // 继续解析导入的@Configuration class
    // 因为解析的已经是一个class 所以将class转换为ConfigurationClass继续解析就可以了
    // 所以递归调用重新进入processConfigurationClass


    //解析配置类调用链
    //ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry                //ConfigurationClassPostProcessor#processConfigBeanDefinitions
    //进入ConfigurationClassParser#parse(Set<BeanDefinitionHolder> configCandidates)
    //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 {
    				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 {
                    //加入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", "源码之路");
            //这里注册bean
            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);
        //添加到集合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() {
    			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:校验配置类

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

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

校验所有的configurationClasses

    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

入口: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注释的方法。 再有20多行代码就分析完了,加油!

在这里统一处理没有注册的进行注册,将上一步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!