SpringBoot自动装配实现原理(从SpringBoot启动开始)


前言

SpringBoot框架是目前企业级Java开发热门框架,亦是微服务项目的基础框架。SpringBoot因其约定优于配置以及强大的可扩展性,使程序员者开发起来简单,扩展起来方便。本文章主要介绍从SpringBoot启动开始,一步一步实现自动装配原理的过程。

一、SpringBoot启动类

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

1、@SpringBootApplication注解

@SpringBootApplication注解其下有@EnableAutoConfiguration,而@EnableAutoConfiguration下面有@Import注解,且@Import的value为AutoConfigurationImportSelector.class,这是重点,SpringBoot在启动过程中会对启动类上的注解进行递归获取,拿到AutoConfigurationImportSelector并执行一些操作。
@SpringBootApplication以及@EnableAutoConfiguration注解

2、main方法,程序入口,执行SpringApplication的静态方法run

main方法为java程序执行入口,SpringBoot项目也不例外,因此,探索自动装配实现的原理,从SpringBoot的main方法开始。由上述可以代码看到其调用了SpringApplication类的静态方法run,并将启动类的class对象以及参数传递下去。我们点进去看方法的调用过程,可以知道其实际会实例化一个SpringApplication对象并执行这个对象的run方法,如下所示。
SpringApplication.run方法执行链


二、执行SpringApplication实例化后对象的run方法

由第一步我们可以看到,run方法执行链会去创建一个SpringApplication对象,在其构造器中执行应用类型的判断、初始化、监听器的获取以及判断主启动类等方法。实例化完成之后,便调用本对象的run方法,代码如下。

//SpringBoot2.7.0版本
public ConfigurableApplicationContext run(String... args) {
		long startTime = System.nanoTime();
		DefaultBootstrapContext bootstrapContext = createBootstrapContext();
		ConfigurableApplicationContext context = null;
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting(bootstrapContext, this.mainApplicationClass);
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			//重点方法1
			context = createApplicationContext();
			context.setApplicationStartup(this.applicationStartup);
			prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
			//重点方法2
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
			}
			listeners.started(context, timeTakenToStartup);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, listeners);
			throw new IllegalStateException(ex);
		}
		try {
			Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
			listeners.ready(context, timeTakenToReady);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

在整个自动装配实现过程中,主要原理涉及到了createApplicationContext()方法以及refreshContext(context)的调用,其他方法在本文中暂时不展开,有兴趣的可以跟一下源码。

三、根据当前应用类型创建对应的Spring容器

1、 应用类型webApplicationType

当前SpringBoot版本中一个共有三种webApplicationType(web应用类型):NONE、SERVLET、REACTIVE。实际开发应用经常使用的SERVLET,而REACTIVE(响应式web)也会根据需求进行选用,本文以SERVLET应用为例进行讲解。
webApplicationType的获取在第一步中SpringApplication对象实例化的过程中会通过WebApplicationType的静态方法deduceFromClasspath方法的调用来获取当前应用的类型,其会通过当前项目中是否存在某个类来区分类型,具体细节可以看一下deduceFromClasspath方法的源码。

2、Spring容器的实例化

根据第二步run方法中的createApplicationContext方法,讲解一下Spring容器(ConfigurableApplicationContext)的创建。
Spring容器的创建

从上面代码可以看到,此方法调用了SpringApplication成员变量applicationContextFactory的create方法,并传入当前应用类型webApplicationType。applicationContextFactory对象具体是什么呢?来看下下面的代码。
ApplicationContextFactory接口

根据上面代码可以看到ApplicationContextFactory是一个函数式接口,其唯一抽象方法为create(WebApplicationType webApplicationType) .
而ApplicationContextFactory中一个ApplicationContextFactory的实现DEFAULT,其会从spring.factories文件中获取ApplicationContextFactory对应的value,即ApplicationContextFactory的实现,然后调用此实现create方法,从而创建得到Spring容器对象。
spring.factories文件中的ApplicationContextFactory对应的value

从上图可以看到,其中有两个实现类AnnotationConfigReactiveWebServerApplicationContext以及AnnotationConfigServletWebServerApplicationContext。
AnnotationConfigServletWebServerApplicationContext 内部类create方法

AnnotationConfigServletWebServerApplicationContext的create方法会根据传入的webApplicationType与SERVLET进行对比,若不同则返回null,若相同则返回AnnotationConfigServletWebServerApplicationContext实例化对象,也即创建并返回了一个Spring容器对象。

3、AnnotatedBeanDefinitionReader的实例化

在上述第2点的Spring容器即AnnotationConfigServletWebServerApplicationContext实例化过程中,会进行AnnotatedBeanDefinitionReader的实例化,并将当前AnnotationConfigServletWebServerApplicationContext对象做为参数传入。
AnnotationConfigServletWebServerApplicationContext构造器
AnnotationConfigServletWebServerApplicationContext关系类图

AnnotationConfigServletWebServerApplicationContext实现了BeanDefinitionRegistry接口
AnnotatedBeanDefinitionReader构造器

4、将ConfigurationClassPostProcessor注册到BeanDefinitionRegistry中

通过第3点可以看到,在AnnotatedBeanDefinitionReader的实例化过程中会调用AnnotationConfigUtils的registerAnnotationConfigProcessors方法,其会将ConfigurationClassPostProcessor添加到当前BeanDefinitionRegistry中。ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor接口,BeanDefinitionRegistryPostProcessor又继承了BeanFactoryPostProcessor接口,因此,可以看出ConfigurationClassPostProcessor实际上是一个BeanFactory的后置处理器/增强器,其会在Spring容器刷新过程中,执行相对应的方法。
ConfigurationClassPostProcessor的添加

从上面代码可以看到,首先会判断当前BeanDefinitionRegistry中是否包含该Bean名称对应的bean定义信息,如果没有的话,则会创建一个beanName为org.springframework.context.annotation.internalConfigurationAnnotationProcessor,class对象为ConfigurationClassPostProccessor的BeanDefinition,并添加到当前的BeanDefinitionRegistry中,在后面方法的执行中,就可以取出此ConfigurationClassPostProccessor并进行相应的方法操作了。

四、容器刷新,调用Spring框架的refresh方法

上面讲完了Spring容器的创建,接下来将通过Spring框架源码中的refresh方法,分析其是如何一步一步执行并实现自动装配的。
SpringBoot在run方法中执行refreshContext方法,其实际上调用了AbstractApplicationContext的refresh方法。

1、invokeBeanFactoryPostProcessors方法,执行BeanFactory后置处理

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
			// Prepare this context for refreshing.
			prepareRefresh();
			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);
			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);
				StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
				// Invoke factory processors registered as beans in the context.
				//此方法为重点方法,会获取当前容器中实现了BeanFactoryPostProccessor接口的BeanDefinition并执行相应方法,实现BeanFactory后置处理,我们也可以自己自定义BeanFactoryPostProccesor的实现类,来完成扩展需求。
				invokeBeanFactoryPostProcessors(beanFactory);
				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);
				beanPostProcess.end();
				// Initialize message source for this context.
				initMessageSource();
				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();
				// Initialize other special beans in specific context subclasses.
				onRefresh();
				// Check for listener beans and register them.
				registerListeners();
				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);
				// Last step: publish corresponding event.
				finishRefresh();
			}
			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}
				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();
				// Reset 'active' flag.
				cancelRefresh(ex);
				// Propagate exception to caller.
				throw ex;
			}
			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
				contextRefresh.end();
			}
		}
	}

上面讲过在Spring容器创建的过程中,将ConfigurationClassPostProccessor注册在当前容器中,而ConfigurationClassPostProcessors是BeanDefinitionRegistryPostProcessor接口实现,BeanDefinitionRegistryPostProcessor又继承了BeanFactoryPostProcessor接口。因此,其中的invokeBeanFactoryPostProcessors的方法便能够去执行到ConfigurationClassPostProcessor相应的扩展操作。
委托PostProcessorRegistrationDelagate进行后置处理
invokeBeanFactoryPostProcessors方法会委托PostProcessorRegistrationDelagate进行处理,会将Spring容器对象beanFactory传递下去,下面为执行重点方法。

public static void invokeBeanFactoryPostProcessors(
			ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
		   //...省略部分会根据参数获取beanFactoryPostProcessors

			//下面代码会获取到前面注册在Spring容器中的ConfigurationClassPostProcessor
			List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
			String[] postProcessorNames =
					beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
			//循环遍历		
			for (String ppName : postProcessorNames) {
				//ConfigurationClassPostProcessor实现了PriorityOrdered
				if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
					currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
					processedBeans.add(ppName);
				}
			}
			sortPostProcessors(currentRegistryProcessors, beanFactory);
			registryProcessors.addAll(currentRegistryProcessors);
			//执行BeanDefinitionRegistryPostProcessors
			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
			currentRegistryProcessors.clear();

			//省略...
	}

	//执行BeanDefinitionRegistryPostProcessors:
	private static void invokeBeanDefinitionRegistryPostProcessors(
			Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) {
		for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
			StartupStep postProcessBeanDefRegistry = applicationStartup.start("spring.context.beandef-registry.post-process")
					.tag("postProcessor", postProcessor::toString);
			//循环执行BeanDefinitionRegistryPostProcessors实现的postProcessBeanDefinitionRegistry方法,其中便会执行到ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法
			postProcessor.postProcessBeanDefinitionRegistry(registry);
			postProcessBeanDefRegistry.end();
		}
	}

由上面代码可以看出,通过遍历所有的BeanDefinitionRegistryPostProcessors实现类,并分别执行postProcessBeanDefinitionRegistry方法,其便会执行到ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法。

2、执行processConfigBeanDefinitions处理方法

ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法
代码跟进来,会发现此处调用了其processConfigBeanDefinitions方法,如下。
配置类的解析、获取、注册

processConfigBeanDefinitions方法会调用ConfigurationClassParser的parse方法进行注解解析,并执行相关ImportSelector实现的方法将需要注册的配置类添加到一个Set集合中;之后进行获取并构造BeanDefinition注册到Register中。

3、ConfigurationClassParser解析自动配置类的过程

上面讲到processConfigBeanDefinitions中会调用ConfigurationClass解析器的解析方法parse,此方法是如何进行解析并添加到解析器中的CofigurationClasses集合中的,其通过判断最终都会调用到解析器中的processConfigurationClass方法,其会doProcessConfigurationClass方法,在doProcessConfigurationClass方法中会有一个processImports方法,此方法就是用于递归解析@Import注解,并获取到注解上面的class对象,执行相关方法,其会通过deferredImportSelectorHandler.handle方法,会调用到实现了DeferredImportSelector.Group接口的process方法。
deferredImportSelectorHandler的handle方法
handle方法的执行链

4、AutoConfigurationImportSelector内部类AutoConfigurationGroup的process方法的执行

上面我们讲过@EnableAutoConfiguration注解上的@Import注解的value为AutoConfigurationImportSelector,其有一个内部类AutoConfigurationGroup,它实现了DeferredImportSelector.Group,当前ConfigurationClassParser解析到此时,便会执行其重写的process方法,如下。
process方法的执行

从上面代码的调用过程getAutoConfigurationEntry——>getCandidateConfigurations——>SpringFactoriesLoader.loadFactoryNames,到此处我们可以看到其最终通过spring.factories文件中EnableAutoConfiguration作为key值找到的所有全类限定名,并添加到集合中,便可提供给上层方法进行获取,最后创建对应的BeanDefinition对象,在后面Bean的实例化过程中,便可实现自动装配了。

当然,在SpringBoot2.7.0版本中除了读取spring.factories文件外,其还提供了另外一种读取模式,其会从META-INF/spring路径下查找*.imports文件,也可以获取到配置类的全类名,如下代码。
SpringBoot2.7.0其他获取方式
SpringBoot2.7.0其他获取方式,通过*imports文件进行读取

总结

下面为SpringBoot自动装配实现的简要流程图,实际远不止这些,有兴趣的可以研究一下整个SpringBoot启动过程源码。
SpringBoot自动装配实现简易流程

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值