SpringBoot2 | @SpringBootApplication comment automated source code analysis configuration process

Start with the @SpringBootApplicationbeginning.

In the startup process section, we describe the SpringBoot2 rough start step, and explain the source. But this was not deployed to refresh the container, refreshContext(context);a simple line of code behind it to do too many things. So in order not overwhelming herein, this also try to select annotations and @SpringBootApplicationmethods related to explain.


1) springBoot start class loader

First, load the spring springBoot startup class poured into the vessel bean map, load look prepareContext method method:
load(context, sources.toArray(new Object[0]));
follow-up which will eventually execute BeanDefinitionLoaderthe loadmethod:

	private int load(Object source) {
		Assert.notNull(source, "Source must not be null");
		//如果是class类型,启用注解类型
		if (source instanceof Class<?>) {
			return load((Class<?>) source);
		}
		//如果是resource类型,启用xml解析
		if (source instanceof Resource) {
			return load((Resource) source);
		}
		//如果是package类型,启用扫描包,例如:@ComponentScan
		if (source instanceof Package) {
			return load((Package) source);
		}
		//如果是字符串类型,直接加载
		if (source instanceof CharSequence) {
			return load((CharSequence) source);
		}
		throw new IllegalArgumentException("Invalid source type " + source.getClass());
	}

Continue to follow up load(Class<?> source)method:

Write pictures described here

The above-described method of determining whether the starting class contains @componentannotations, we can not start the annotation category. Follow up will find that AnnotationUtilsdetermines whether to include the comment by recursive implementation, notes on the notes contained if the specified type is also possible.
Start class contains @SpringBootApplicationannotations, find further @SpringBootConfigurationcomment, and then look for @Componentnotes, finally looks to @ComponentNote:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
}

Found in @Componentthe annotation, the surface of the object is a spring bean, which then will be packaged in the information beanDefinitaion, beanDefinitionMap added to the container. As follows:
Write pictures described here
In this way, we will start classes are packaged AnnotatedGenericBeanDefinition, and the subsequent processing based on the starting class are the subject.


2) automatic assembly inlet:

Refresh vessel from the beginning:
public void refresh() throws BeansException, IllegalStateException {
		//...
		invokeBeanFactoryPostProcessors(beanFactory);
		//...
	}

Eliminating the above code is not relevant, continue to follow up invokeBeanFactoryPostProcessorsmethod:

	protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		//开始执行beanFactoryPostProcessor对应实现类
		PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
	// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
	// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
	if (beanFactory.getTempClassLoader() == null &amp;&amp; beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
		beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
		beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
	}

首先我们要知道beanFactoryPostProcessor接口是spring的扩展接口,从名字也可以看出,是 beanFactory的扩展接口。在刷新容器之前,该接口可用来修改bean元数据信息。具体实现方式,我们继续跟着上述执行逻辑便知。
继续跟进上面invokeBeanFactoryPostProcessors方法,第一行很关键:

PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

一个比较核心的代理类出现了,AbstractApplicationContext委托执行post processors任务的工具类
而在项目启动时会委托什么任务呢?

或许你还记得第一篇博客中介绍的SpringApplication类中applyInitializers(context);方法吧,它会将三个默认的内部类加入到 spring 容器DefaultListableBeanFactory中,如下:

//设置配置警告
ConfigurationWarningsApplicationContextInitializer$ConfigurationWarningsPostProcessor
SharedMetadataReaderFactoryContextInitializer$CachingMetadataReaderFactoryPostProcessor
ConfigFileApplicationListener$PropertySourceOrderingPostProcessor

来看一下具体任务执行细节,跟进invokeBeanFactoryPostProcessors方法:

if (beanFactory instanceof BeanDefinitionRegistry) {
			BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
			List<BeanFactoryPostProcessor> regularPostProcessors = new LinkedList<>();
			List<BeanDefinitionRegistryPostProcessor> registryProcessors = new LinkedList<>();
		//这里开始遍历上面三个内部类,如果属于BeanDefinitionRegistryPostProcessor 子类,
		//加入到bean注册的集合,否则加入到 regularPostProcessors中,从名字可以看出是有规律集合。
		for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
			if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
				BeanDefinitionRegistryPostProcessor registryProcessor =
						(BeanDefinitionRegistryPostProcessor) postProcessor;
				registryProcessor.postProcessBeanDefinitionRegistry(registry);
				registryProcessors.add(registryProcessor);
			}
			else {
				regularPostProcessors.add(postProcessor);
			}
		}

		// Do not initialize FactoryBeans here: We need to leave all regular beans
		// uninitialized to let the bean factory post-processors apply to them!
		// Separate between BeanDefinitionRegistryPostProcessors that implement
		// PriorityOrdered, Ordered, and the rest.
		List&lt;BeanDefinitionRegistryPostProcessor&gt; currentRegistryProcessors = new ArrayList&lt;&gt;();

		// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
		String[] postProcessorNames =
				beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
				
		//首先执行类型为PriorityOrdered的BeanDefinitionRegistryPostProcessor
		//PriorityOrdered类型表明为优先执行
		for (String ppName : postProcessorNames) {
			if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
				//获取对应的bean
				currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
				//用来存储已经执行过的`BeanDefinitionRegistryPostProcessor`				
				processedBeans.add(ppName);
			}
		}
		sortPostProcessors(currentRegistryProcessors, beanFactory);
		registryProcessors.addAll(currentRegistryProcessors);
		invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
		currentRegistryProcessors.clear();

		// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
		postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
		//其次执行类型为Ordered的BeanDefinitionRegistryPostProcessor
		//Ordered表明按顺序执行
		for (String ppName : postProcessorNames) {
			if (!processedBeans.contains(ppName) &amp;&amp; beanFactory.isTypeMatch(ppName, Ordered.class)) {
				currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
				processedBeans.add(ppName);
			}
		}
		sortPostProcessors(currentRegistryProcessors, beanFactory);
		registryProcessors.addAll(currentRegistryProcessors);
		invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
		currentRegistryProcessors.clear();

		// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
		boolean reiterate = true;
		//循环中执行类型不为PriorityOrdered,Ordered类型的BeanDefinitionRegistryPostProcessor
		while (reiterate) {
			reiterate = false;
			postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
			for (String ppName : postProcessorNames) {
				if (!processedBeans.contains(ppName)) {
					currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
					processedBeans.add(ppName);
					reiterate = true;
				}
			}
			sortPostProcessors(currentRegistryProcessors, beanFactory);
			registryProcessors.addAll(currentRegistryProcessors);
			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
			currentRegistryProcessors.clear();
		}

		// Now, invoke the postProcessBeanFactory callback of all processors handled so far.
		//执行父类方法,优先执行注册处理类
		invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
		//执行有规则处理类
		invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
	}

来分析一下核心代码:

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

这行代码通过类型BeanDefinitionRegistryPostProcessor获取的处理类名称为:
"org.springframework.context.annotation.internalConfigurationAnnotationProcessor"
而在源码中却搜不到internalConfigurationAnnotationProcessor类,为什么呢?最初看这块代码确实迷惑了半天。
在第一篇博客中,当启动springBoot,创建springBoot容器上下文AnnotationConfigEmbeddedWebApplicationContext时,会装配几个默认bean:

	public AnnotationConfigEmbeddedWebApplicationContext() {
		//在这里装配
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

继续跟进会执行registerAnnotationConfigProcessors方法:


public static final String CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME =
			"org.springframework.context.annotation.internalConfigurationAnnotationProcessor";
//将 internalConfigurationAnnotationProcessor 对应的类包装成 RootBeanDefinition 加载到容器
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
		RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
		def.setSource(source);
		beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
	}

到这里,答案清晰浮现。internalConfigurationAnnotationProcessor为bean名称,容器中真正的类则是ConfigurationClassPostProcessor

继续后面流程,获取ConfigurationClassPostProcessor后,开始执行BeanDefinitionRegistryPostProcessor:

//开始执行装配逻辑
invokeBeanDefinitionRegistryPostProcessors(priorityOrderedPostProcessors, registry);

3)开始执行自动配置逻辑(启动类指定的配置,非默认配置):

Write pictures described here

如上如:首先获得ConfigurationClassParser,这个是所有配置类的解析类,比较核心。所有的解析逻辑在parser.parse(candidates);中,我们详细的来看一下:

	public void parse(Set<BeanDefinitionHolder> configCandidates) {
		this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();
	for (BeanDefinitionHolder holder : configCandidates) {
		BeanDefinition bd = holder.getBeanDefinition();
		try {
			//是否是注解类
			if (bd instanceof AnnotatedBeanDefinition) {
				parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
			}
			else if (bd instanceof AbstractBeanDefinition &amp;&amp; ((AbstractBeanDefinition) bd).hasBeanClass()) {
				parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
			}
			else {
				parse(bd.getBeanClassName(), holder.getBeanName());
			}
		}
		catch (BeanDefinitionStoreException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new BeanDefinitionStoreException(
					"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
		}
	}

	//执行配置类
	processDeferredImportSelectors();
}

继续跟进parse方法:

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
		//...省略不核心代码
		// Recursively process the configuration class and its superclass hierarchy.
		SourceClass sourceClass = asSourceClass(configClass);
		do {
			//循环处理bean,如果有父类,则处理父类。直至结束。
			sourceClass = doProcessConfigurationClass(configClass, sourceClass);
		}
		while (sourceClass != null);
	this.configurationClasses.put(configClass, configClass);
}

Continue to follow up doProcessConfigurationClassmethod, which can be said to spring framework to support core logic configuration of the notes, take a look:

 protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
        //处理内部类逻辑,由于传来的参数是我们的启动类,不含内部类,所以跳过。
        processMemberClasses(configClass, sourceClass);
    // Process any @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 {
            logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                    "]. Reason: Environment must implement ConfigurableEnvironment");
        }
    }

    //这里是根据启动类 @ComponentScan 注解来扫描项目中的bean
    AnnotationAttributes componentScan = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ComponentScan.class);
    if (componentScan != null &amp;&amp; !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
        // The config class is annotated with @ComponentScan -&gt; perform the scan immediately
        Set&lt;BeanDefinitionHolder&gt; scannedBeanDefinitions =
                this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
        // Check the set of scanned definitions for any further config classes and parse recursively if necessary

        //遍历我们项目中的bean,如果是注解定义的bean,则进一步解析
        for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
            //判断是否是注解bean
            if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {
                //这里是关键,递归解析。所有的bean,如果有注解,会进一步解析注解中包含的bean
                parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
            }
        }
    }

    // Process any @Import annotations
    //这里又是一个递归解析,获取导入的配置类。很多情况下,导入的配置类中会同样包含导入类注解。
    processImports(configClass, sourceClass, getImports(sourceClass), true);

    // Process any @ImportResource annotations
    //解析导入的 xml 配置类
    if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
        AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
        String[] resources = importResource.getAliasedStringArray("locations", ImportResource.class, sourceClass);
        Class&lt;? extends BeanDefinitionReader&gt; readerClass = importResource.getClass("reader");
        for (String resource : resources) {
            String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
            configClass.addImportedResource(resolvedResource, readerClass);
        }
    }

    // Process individual @Bean methods
    Set&lt;MethodMetadata&gt; beanMethods = sourceClass.getMetadata().getAnnotatedMethods(Bean.class.getName());
    for (MethodMetadata methodMetadata : beanMethods) {
        configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }

    // 获取接口中的默认方法,1.8以上的处理逻辑
    for (SourceClass ifc : sourceClass.getInterfaces()) {
        beanMethods = ifc.getMetadata().getAnnotatedMethods(Bean.class.getName());
        for (MethodMetadata methodMetadata : beanMethods) {
            if (!methodMetadata.isAbstract()) {
                // A default method or other concrete method on a Java 8+ interface...
                configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
            }
        }
    }

    // Process superclass, if any
    //如果该类有父类,则继续返回。上层方法判断不为空,则继续递归执行。
    if (sourceClass.getMetadata().hasSuperClass()) {
        String superclass = sourceClass.getMetadata().getSuperClassName();
        if (!superclass.startsWith("java") &amp;&amp; !this.knownSuperclasses.containsKey(superclass)) {
            this.knownSuperclasses.put(superclass, configClass);
            // Superclass found, return its annotation metadata and recurse
            return sourceClass.getSuperClass();
        }
    }

    // No superclass -&gt; processing is complete
    //递归实现,superclass为空,则结束递归中的循环
    return null;
}

Fetch logic configured introduced look like:

processImports(configClass, sourceClass, getImports(sourceClass), true);

Follow-up getImportsmethods:
Write pictures described here
you can see my custom bean import manner is loaded into it. Further processImportsmethods execution logic and said parsemethod analogous, similar recursive processing employed here do not started.


4) a default configuration logic started SpringBoot

Continue back to ConfigurationClassParserthe parseway back to the last step of the process:

public void parse(Set<BeanDefinitionHolder> configCandidates) {
		//...
		//开始执行默认配置
		processDeferredImportSelectors();
	}

Continue to follow up the process processDeferredImportSelectors:

private void processDeferredImportSelectors() {
		List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
		this.deferredImportSelectors = null;
		Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR);
	for (DeferredImportSelectorHolder deferredImport : deferredImports) {
		ConfigurationClass configClass = deferredImport.getConfigurationClass();
		try {
			//获取配置类
			String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
			//再次递归解析配置类
			processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);
		}
		catch (BeanDefinitionStoreException ex) {
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" +
					configClass.getMetadata().getClassName() + "]", ex);
		}
	}
}

getImportSelector()Selector object methods to get EnableAutoConfigurationImportSelector, continue to follow up the object's selectImportsmethod:

	@Override
	public String[] selectImports(AnnotationMetadata metadata) {
		try {
			AnnotationAttributes attributes = getAttributes(metadata);
			//获取默认配置类
			List<String> configurations = getCandidateConfigurations(metadata,
					attributes);
			configurations = removeDuplicates(configurations);
			Set<String> exclusions = getExclusions(metadata, attributes);
			configurations.removeAll(exclusions);
			configurations = sort(configurations);
			recordWithConditionEvaluationReport(configurations, exclusions);
			return configurations.toArray(new String[configurations.size()]);
		}
		catch (IOException ex) {
			throw new IllegalStateException(ex);
		}
	}

Approach here, in front of the blog has been described in detail before, by classacquiring the type spring.factoriesspecified class, classtype:EnableAutoConfiguration

	protected Class<?> getSpringFactoriesLoaderFactoryClass() {
		return EnableAutoConfiguration.class;
	}

springBootAll configuration class provides us with the following, probably more than 100:
Write pictures described here

In acquired springBootafter configuration provides the call again processImportsmethod recursive resolution, according to our custom configuration files, optionally configurable.

So many configuration class, can not all be loaded, the project does not take too much. What are the rules of choice is it?

Follow-up will be complete Bowen detail.

Guess you like

Origin blog.csdn.net/u014513171/article/details/93210617