SpringBoot2 | @SpringBootApplication注解 自动化配置流程源码分析(三)

栏目: Java · 发布时间: 6年前

微信公众号:吉姆餐厅ak 学习更多源码知识,欢迎关注。

SpringBoot2 | @SpringBootApplication注解 自动化配置流程源码分析(三)

SpringBoot2 | SpringBoot启动流程源码分析(一)

SpringBoot2 | SpringBoot启动流程源码分析(二)

SpringBoot2 | @SpringBootApplication注解 自动化配置流程源码分析(三)

SpringBoot2 | SpringBoot Environment源码分析(四)

SpringBoot2 | SpringBoot自定义AutoConfiguration | SpringBoot自定义starter(五)

SpringBoot2 | SpringBoot监听器源码分析 | 自定义ApplicationListener(六)

SpringBoot2 | 条件注解@ConditionalOnBean原理源码深度解析(七)

SpringBoot2 | Spring AOP 原理源码深度剖析(八)

SpringBoot2 | SpingBoot FilterRegistrationBean 注册组件 | FilterChain 责任链源码分析(九)

SpringBoot2 | BeanDefinition 注册核心类 ImportBeanDefinitionRegistrar (十)

SpringBoot2 | Spring 核心扩展接口 | 核心扩展方法总结(十一)

在上一篇博客中分析了 springBoot 启动流程,大体的轮廓只是冰山一角。今天就来看一下 springBoot 的亮点功能:自动化装配功能。

先从 @SpringBootApplication 开始。

在启动流程章节中,我们讲述了SpringBoot2大致的启动步骤,并进行了源码详解。但是在刷新容器这块并未展开, refreshContext(context); 简单的一行代码,背后却做了太多事情。所以为了不喧宾夺主,本篇也尽量选取和注解 @SpringBootApplication 有关的方法讲解。

1)springBoot启动类加载

首先加载springBoot启动类注入到spring容器中bean map中,看下prepareContext方法中的load方法: load(context, sources.toArray(new Object[0])); 跟进该方法最终会执行 BeanDefinitionLoaderload 方法:

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());
	}
复制代码

继续跟进 load(Class<?> source) 方法:

SpringBoot2 | @SpringBootApplication注解 自动化配置流程源码分析(三)

上述方法判断启动类中是否包含 @component 注解,可我们的启动类并没有该注解。继续跟进会发现, AnnotationUtils 判断是否包含该注解是通过递归实现,注解上的注解若包含指定类型也是可以的。 启动类中包含 @SpringBootApplication 注解,进一步查找到 @SpringBootConfiguration 注解,然后查找到 @Component 注解,最后会查找到 @Component 注解:

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

在查找到 @Component 注解后,表面该对象为spring bean,然后会将其信息包装成 beanDefinitaion ,添加到容器的 beanDefinitionMap中。如下:

SpringBoot2 | @SpringBootApplication注解 自动化配置流程源码分析(三)
如此一来,我们的启动类就被包装成 AnnotatedGenericBeanDefinition

了,后续启动类的处理都基于该对象了。

2)自动装配的入口:

从刷新容器开始:

public void refresh() throws BeansException, IllegalStateException {
		//...
		invokeBeanFactoryPostProcessors(beanFactory);
		//...
	}
复制代码

上述省去了不相关的代码,继续跟进 invokeBeanFactoryPostProcessors 方法:

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 && 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<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

			// 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) && 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)开始执行自动配置逻辑(启动类指定的配置,非默认配置):

SpringBoot2 | @SpringBootApplication注解 自动化配置流程源码分析(三)

如上如:首先获得 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 && ((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);
	}
复制代码

继续跟进 doProcessConfigurationClass 方法,该方法可以说是 spring 框架支持注解配置的核心逻辑了,来看看:

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 && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
            // The config class is annotated with @ComponentScan -> perform the scan immediately
            Set<BeanDefinitionHolder> 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<? 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 = 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") && !this.knownSuperclasses.containsKey(superclass)) {
                this.knownSuperclasses.put(superclass, configClass);
                // Superclass found, return its annotation metadata and recurse
                return sourceClass.getSuperClass();
            }
        }

        // No superclass -> processing is complete
        //递归实现,superclass为空,则结束递归中的循环
        return null;
    }
复制代码

来看一下获取导入配置类的逻辑:

processImports(configClass, sourceClass, getImports(sourceClass), true);
复制代码

跟进 getImports 方法:

SpringBoot2 | @SpringBootApplication注解 自动化配置流程源码分析(三)
可以看到我自定义的bean以导入的方式被加载进去了。另外 processImports 方法执行逻辑和上述 parse

方法类似,同样采用递归处理,这里就不做展开了。

4)开始执行 SpringBoot 默认配置逻辑

继续回到 ConfigurationClassParser 中的 parse 方法,回到该方法的最后一步:

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

继续跟进该方法 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对象为 EnableAutoConfigurationImportSelector ,继续跟进该对象的 selectImports 方法:

@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);
		}
	}
复制代码

这里的处理方式,前面的博客中已经详细介绍过了,通过 class 类型来获取 spring.factories 中的指定类, class 类型为: EnableAutoConfiguration

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
		return EnableAutoConfiguration.class;
	}
复制代码

springBoot 为我们提供的所有配置类如下,大概100多个:

SpringBoot2 | @SpringBootApplication注解 自动化配置流程源码分析(三)

在获取到 springBoot 提供的配置后,再次调用 processImports 方法进行递归解析,根据我们自定义的配置文件,进行选择性配置。

这么多的配置类,不可能全部进行加载,项目也用不了这么多。选择的规则是什么呢?

后续会有完整博文详细介绍。

springBoot 自动化装配流程就先介绍到这里。

SpringBoot2 | SpringBoot启动流程源码分析(一)

SpringBoot2 | SpringBoot启动流程源码分析(二)

SpringBoot2 | @SpringBootApplication注解 自动化配置流程源码分析(三)

SpringBoot2 | SpringBoot Environment源码分析(四)

SpringBoot2 | SpringBoot自定义AutoConfiguration | SpringBoot自定义starter(五)

SpringBoot2 | SpringBoot监听器源码分析 | 自定义ApplicationListener(六)

SpringBoot2 | 条件注解@ConditionalOnBean原理源码深度解析(七)


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Agile Web Application Development with Yii 1.1 and PHP5

Agile Web Application Development with Yii 1.1 and PHP5

Jeffrey Winesett / Packt Publishing / 2010-08-27

In order to understand the framework in the context of a real-world application, we need to build something that will more closely resemble the types of applications web developers actually have to bu......一起来看看 《Agile Web Application Development with Yii 1.1 and PHP5》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

随机密码生成器
随机密码生成器

多种字符组合密码