SpringBoot2.1.1启动流程源码分析

使用springboot已经有一年多了,但是一直停留的在整合,项目能够正常跑的阶段。年底了。抽点时间研究一下Springboot源码 ,这肯定会对以后的工作有帮助。今天我们从springboot的启动流程的源码分析开始。

最新版本是Springboot2.1.1,Spring5.1.3,所以新特性本系列后面也会着重分析。

1.1创建一个新的项目springboot10

pom.xml


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.1.1.RELEASEversion>
        <relativePath/> 
    parent>
    <groupId>com.lidonggroupId>
    <artifactId>springboot01artifactId>
    <version>1.0.0version>
    <name>springboot01name>
    <description>研究springboot的启动过程description>

    <properties>
        <java.version>1.8java.version>
    properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
        plugins>
    build>

project>

1.2启动类

package com.lidong.springboot01;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@SpringBootApplication
public class Springboot01Application {

    public static void main(String[] args) {
//        SpringApplication.run(Springboot01Application.class, args);//默认web应用
        new SpringApplicationBuilder(Springboot01Application.class)
                //WebApplicationType 应用的类型
                // NONE 非web应用
                //SERVLET web应用
                //REACTIVE //响应式应用
                .web(WebApplicationType.SERVLET)//这里默认按照web应用来分析
                .run(args);
    }
    @GetMapping(value = "test")
    public String hello(String text){
        return text;
    }
}


1.3 SpringApplication和SpringApplicationBuilder 类的关系

SpringApplication和SpringApplicationBuilder这两个类名可以看出,是一个建造者模式的实现。

SpringBoot2.1.1启动流程源码分析_第1张图片

SpringBoot2.1.1启动流程源码分析_第2张图片

1.4 WebApplicationType.deduceFromClasspath() 方法分析

SpringBoot2.1.1启动流程源码分析_第3张图片

这里主要是做了三种不同应用的判断。

1.5 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)) 方法的分析

1.5.1 getSpringFactoriesInstances方法

ApplicationContextInitializer是spring组件spring-context组件中的一个接口,主要是spring ioc容器刷新之前的一个回调接口,用于处于自定义逻辑。这里用到了spring.factories文件,这个文件位于spring-boot-2.1.1.RELEASE.jar的META-INF目录下,spring.factories文件中配置接口的实现类名称,然后在程序中读取这些配置文件并实例化。这种自定义的SPI机制是Spring Boot Starter实现的基础。

spring.factories文件中的实现类:
SpringBoot2.1.1启动流程源码分析_第4张图片

下面的4个实现类ApplicationContextInitializer 初始化

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

1.6 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));方法的分析

下面的9个实现类ApplicationListener 初始化

 # Application Listeners
    org.springframework.context.ApplicationListener=\
    org.springframework.boot.ClearCachesApplicationListener,\
    org.springframework.boot.builder.ParentContextCloserApplicationListener,\
    org.springframework.boot.context.FileEncodingApplicationListener,\
    org.springframework.boot.context.config.AnsiOutputApplicationListener,\
    org.springframework.boot.context.config.ConfigFileApplicationListener,\
    org.springframework.boot.context.config.DelegatingApplicationListener,\
    org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
    org.springframework.boot.context.logging.LoggingApplicationListener,\
    org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

还有一个为:

org.springframework.boot.autoconfigure.BackgroundPreinitializer

这个10个重要的监听器,后面会对每一个监听器做解释。

1.7 deduceMainApplicationClass() 方法的分析

private Class<?> deduceMainApplicationClass() {
		try {
			StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
			for (StackTraceElement stackTraceElement : stackTrace) {
				if ("main".equals(stackTraceElement.getMethodName())) {
					return Class.forName(stackTraceElement.getClassName());
				}
			}
		}
		catch (ClassNotFoundException ex) {
			// Swallow and continue
		}
		return null;
	}

通过获取当前的调用栈,找到人口方法main所在的类,并将其赋值给springApplication对象的成员变量mainApplicationClass。
SpringBoot2.1.1启动流程源码分析_第5张图片

1.8 SpringApplication对象的run方法分析

源码

public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		//开启监控
		configureHeadlessProperty();
		//第一步:获取并启动监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
		启动监听器
		listeners.starting();
		try {
		    //第二步:构造容器环境
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			//设置需要忽略的bean		
			configureIgnoreBeanInfo(environment);
			//输出banner
			//第三步:创建容器
			context = createApplicationContext();
			//第四步:实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			//第五步:准备容器
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			//第六步:刷新容器
			refreshContext(context);
			//第七步:刷新容器后的扩展接口
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

1.8.1 获取并启动监听器

private SpringApplicationRunListeners getRunListeners(String[] args) {
    		Class[] types = new Class[] { SpringApplication.class, String[].class };
    		return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
    				SpringApplicationRunListener.class, types, this, args));
}

SpringBoot2.1.1启动流程源码分析_第6张图片

从getRunListeners源码看来,args本身默认为空,但是在获取监听器的方法中,getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args)将当前对象SpringApplicationRunListener作为参数,该方法用来获取spring.factories对应的监听器:

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

在SpringBoot 框架中获取factories都是通过createSpringFactoriesInstances(Class type,
Class[] parameterTypes, ClassLoader classLoader, Object[] args,
Set names)来获取的。

SpringBoot2.1.1启动流程源码分析_第7张图片

上面的通过反射构造SpringApplicationRunListener对象时,会触发EventPublishingRunListener的构造方法:

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

	private final SpringApplication application;
	private final String[] args;
	private final SimpleApplicationEventMulticaster initialMulticaster;

	public EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
		//创建一个事件多播类
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		//将spring.factories中的监听器传递到SimpleApplicationEventMulticaster中
		for (ApplicationListener<?> listener : application.getListeners()) {
			this.initialMulticaster.addApplicationListener(listener);
		}
	}
// 省略

接下来看一下addApplicationListener方法:

@Override
	public void addApplicationListener(ApplicationListener<?> listener) {
		synchronized (this.retrievalMutex) {
			// Explicitly remove target for a proxy, if registered already,
			// in order to avoid double invocations of the same listener.
			Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
			if (singletonTarget instanceof ApplicationListener) {
			    //移除
				this.defaultRetriever.applicationListeners.remove(singletonTarget);
			}
			//添加新的listener
			this.defaultRetriever.applicationListeners.add(listener);
			this.retrieverCache.clear();
		}
	}

启动监听器:

 listeners.starting();

listeners 只有一个listener,为EventPublishingRunListener,它是启动事件发布监听器,主要用来发布启动事件。

public void starting() {
        //listeners 只有一个listener,为EventPublishingRunListener,它是启动事件发布监听器,主要用来发布启动事件。
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.starting();
		}
	}

EventPublishingRunListener 的实例listener调用了starting()方法,我们在来看一下starting()的实现机制

@Override
public void starting() {
	//创建application启动事件ApplicationStartingEvent
	this.initialMulticaster.multicastEvent(
				new ApplicationStartingEvent(this.application, this.args));
}

EventPublishingRunListener这个是springBoot框架中第一个执行的监听器,在该监听器执行started()方法时,会继续发布事件,也就是事件传递。这种实现主要还是基于spring的事件机制。

继续跟踪multicastEvent方法,该方法是SimpleApplicationEventMulticaster类中的一个核心方法

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			//获取线程池,如果为空则同步处理。这里线程池为空,还未没初始化。
			Executor executor = getTaskExecutor();
			if (executor != null) {
			    //异步发送事件
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
				//同步发送事件
				invokeListener(listener, event);
			}
		}
	}

这里会根据事件类型ApplicationStartingEvent获取对应的监听器,在容器启动之后执行响应的动作,有如下4种监听器:
SpringBoot2.1.1启动流程源码分析_第8张图片

这是springBoot启动过程中,第一处根据类型,执行监听器的地方。根据发布的事件类型从上述10种监听器中选择对应的监听器进行事件发布。

这里选了一个 springBoot 的日志监听器来进行讲解,核心代码如下:
SpringBoot2.1.1启动流程源码分析_第9张图片

由于我们的事件类型为ApplicationEvent,所以会执行

onApplicationStartedEvent((ApplicationStartedEvent) event);

springBoot会在运行过程中的不同阶段,发送各种事件,来执行对应监听器的对应方法

1.8.2 容器的环境构建

ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);

private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		//获取对应的ConfigurableEnvironment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		//配置
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		//发布环境已准备事件,这是第二次发布事件
		listeners.environmentPrepared(environment);
	   //将环境绑定到spring应用
		bindToSpringApplication(environment);
		if (this.webApplicationType == WebApplicationType.NONE) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertToStandardEnvironmentIfNecessary(environment);
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

来看一下getOrCreateEnvironment()方法,

private ConfigurableEnvironment getOrCreateEnvironment() {
		if (this.environment != null) {
			return this.environment;
		}
		switch (this.webApplicationType) {
		case SERVLET://web 应用
			return new StandardServletEnvironment();
		case REACTIVE:
			return new StandardReactiveWebEnvironment();
		default:
			return new StandardEnvironment();
		}
	}

前面已经提到webApplicationType 是枚举类型,environment已经被设置了servlet类型,所以这里创建的是环境对象是StandardServletEnvironment(web 应用)。

Environment接口提供了4种实现方式,

  • StandardEnvironment(普通应用)
  • StandardServletEnvironment(web应用)
  • MockEnvironment(测试程序的环境)
  • StandardReactiveWebEnvironment(响应式web环境)

具体后面会详细讲解。

因为我们这里是web应用,因此在这里返回

return new StandardServletEnvironment();

对象的时候,会完成一系列初始化动作,主要就是将运行机器的系统变量和环境变量,加入到其父类AbstractEnvironment定义的对象MutablePropertySources中,MutablePropertySources对象中定义了一个属性集合:

private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<PropertySource<?>>();

执行到这里,系统变量和环境变量已经被载入到配置文件的集合中,接下来就行解析项目中的配置文件。

来看一下

listeners.environmentPrepared(environment);

,上面已经提到了,这里是第二次发布事件。系统环境初始化完成的事件。

SpringBoot2.1.1启动流程源码分析_第10张图片

从上面的截图可以看到获取到的监听器和第一次发布启动事件获取的监听器有几个是重复的,这也验证了监听器是可以多次获取,根据事件类型来区分具体处理逻辑。
主要来看一下ConfigFileApplicationListener,该监听器非常核心,主要用来处理项目配置。
项目中的 properties 和yml文件都是其内部类所加载。具体来看一下:
首先方法执行入口:
SpringBoot2.1.1启动流程源码分析_第11张图片

首先还是会去读spring.factories 文件,

List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();

loadPostProcessors方法的实现

List loadPostProcessors() {
		return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class,
				getClass().getClassLoader());
	}

获取的处理类有以下四种:

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=	//一个@FunctionalInterface函数式接口
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,//为springCloud提供的扩展类
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,//支持json环境变量
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor //springBoo2提供的一个包装类,主要将`StandardServletEnvironment`包装成`SystemEnvironmentPropertySourceEnvironmentPostProcessor`对象

在执行完上述三个监听器流程后,ConfigFileApplicationListener会执行该类本身的逻辑。由其内部类Loader加载项目制定路径下的配置文件:

private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";

这样就可以将项目中所有的变量的配置文件加载完。

SpringBoot2.1.1启动流程源码分析_第12张图片

这里一共6个配置文件,前面5个都是系统默认的配置的文件。只有最后一个是我们自己的application.properties,注意 配置文件变量的命名时候,前面尽量带上前缀,防止后面的定义的变量将前面的值覆盖掉。

配置忽略bean的信息

SpringBoot2.1.1启动流程源码分析_第13张图片

配置控制台打印Banner
SpringBoot2.1.1启动流程源码分析_第14张图片

1.8.3 创建容器
context = createApplicationContext();

createApplicationContext()方法的分析
SpringBoot2.1.1启动流程源码分析_第15张图片

创建容器的类型 还是根据webApplicationType进行判断的,前面已经分析过该变量如何赋值的过程。因为该类型为SERVLET类型,所以会通过反射装载对应的class文件

public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
			+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

该对象是springBoot 2创建的容器,接下来所有的操作都是该容器内部操作的。

1.8.4 报告错误信息

exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);

加载spring.factorties中的SpringBootExceptionReporter的接口

# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers

该类主要是在项目启动失败之后,输出log文件的时候使用:

@Override
	public boolean reportException(Throwable failure) {
		FailureAnalysis analysis = analyze(failure, this.analyzers);
		return report(analysis, this.classLoader);
	}

	private FailureAnalysis analyze(Throwable failure, List<FailureAnalyzer> analyzers) {
		for (FailureAnalyzer analyzer : analyzers) {
			try {
				FailureAnalysis analysis = analyzer.analyze(failure);
				if (analysis != null) {
					return analysis;
				}
			}
			catch (Throwable ex) {
				logger.debug("FailureAnalyzer " + analyzer + " failed", ex);
			}
		}
		return null;
	}

	private boolean report(FailureAnalysis analysis, ClassLoader classLoader) {
		List<FailureAnalysisReporter> reporters = SpringFactoriesLoader
				.loadFactories(FailureAnalysisReporter.class, classLoader);
		if (analysis == null || reporters.isEmpty()) {
			return false;
		}
		for (FailureAnalysisReporter reporter : reporters) {
			reporter.report(analysis);
		}
		return true;
	}

1.8.5 准备容器

这里主要做的是在容器刷新之前的准备动作。其中包括将启动类注入容器,为后续开启自动化配置做准备。

private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		//1.设置容器环境,包括各种变量
		context.setEnvironment(environment);
		//2.执行容器后置处理
		postProcessApplicationContext(context);
		//3.执行容器中的ApplicationContextInitializer(包括 spring.factories和自定义的实例)
		applyInitializers(context);
		//4.发送容器已经准备好的事件,通知各监听器
		listeners.contextPrepared(context);
		//5.输出log
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// Add boot specific singleton beans
		//6.注册启动参数bean,这里将容器指定的参数封装成bean,注入容器
		context.getBeanFactory().registerSingleton("springApplicationArguments",
				applicationArguments);
		//7.设置banner
		if (printedBanner != null) {
			context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
		}
		// Load the sources
		//8.获取我们的启动类指定的参数,可以是多个
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		//9.加载我们的启动类,将启动类注入容器
		load(context, sources.toArray(new Object[0]));
		//10.发布容器已加载事件。
		listeners.contextLoaded(context);
	}

容器的后置处理:

protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
		if (this.beanNameGenerator != null) {
			context.getBeanFactory().registerSingleton(
					AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
					this.beanNameGenerator);
		}
		if (this.resourceLoader != null) {
			if (context instanceof GenericApplicationContext) {
				((GenericApplicationContext) context)
						.setResourceLoader(this.resourceLoader);
			}
			if (context instanceof DefaultResourceLoader) {
				((DefaultResourceLoader) context)
						.setClassLoader(this.resourceLoader.getClassLoader());
			}
		}
	}

这里默认不执行任何逻辑,因为beanNameGenerator和resourceLoader默认为空。之所以这样做,是springBoot留给我们的扩展处理方式,类似于这样的扩展,spring中也有很多。

加载启动指定类(重点)

这里会将我们的启动类加载spring容器beanDefinitionMap中,为后续springBoot 自动化配置奠定基础,springBoot为我们提供的各种注解配置也与此有关。

load(context, sources.toArray(new Object[0]));
protected void load(ApplicationContext context, Object[] sources) {
		if (logger.isDebugEnabled()) {
			logger.debug(
					"Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
		}
		BeanDefinitionLoader loader = createBeanDefinitionLoader(
				getBeanDefinitionRegistry(context), sources);
		if (this.beanNameGenerator != null) {
			loader.setBeanNameGenerator(this.beanNameGenerator);
		}
		if (this.resourceLoader != null) {
			loader.setResourceLoader(this.resourceLoader);
		}
		if (this.environment != null) {
			loader.setEnvironment(this.environment);
		}
		loader.load();
	}

这里参数即为我们项目启动时传递的参数:SpringApplication.run(SpringBootApplication.class, args);
由于我们指定了启动类,所以上面也就是加载启动类到容器。

需要注意的是,springBoot2会优先选择groovy加载方式,找不到再选用java方式。或许groovy动态加载class文件的性能更胜一筹。

	private int load(Class<?> source) {
		if (isGroovyPresent()
				&& GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
			// Any GroovyLoaders added in beans{} DSL can contribute beans here
			GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source,
					GroovyBeanDefinitionSource.class);
			load(loader);
		}
		if (isComponent(source)) {
			//以注解的方式,将启动类bean信息存入beanDefinitionMap
			this.annotatedReader.register(source);
			return 1;
		}
		return 0;
	}

上面代码中启动类被加载到 beanDefinitionMap中,后续该启动类将作为开启自动化配置的入口,后面一篇文章我会详细的分析,启动类是如何加载,以及自动化配置开启的详细流程。

通知监听器,容器已准备就绪

listeners.contextLoaded(context);

主要还是针对一些日志等监听器的响应处理。

1.8.6刷新容器

执行到这里,springBoot相关的处理工作已经结束,接下的工作就交给了spring。

private void refreshContext(ConfigurableApplicationContext context) {
		refresh(context);
		if (this.registerShutdownHook) {
			try {
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}

继续分析refresh(context)方法

/**
	 * Refresh the underlying {@link ApplicationContext}.
	 * @param applicationContext the application context to refresh
	 */
	protected void refresh(ApplicationContext applicationContext) {
		Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
		((AbstractApplicationContext) applicationContext).refresh();
	}

继续对refresh()分析

@Override
	public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			/**
			 * 刷新上下文环境
			 * 初始化上下文环境,对系统的环境变量或者系统属性进行准备和校验
			 * 如环境变量中必须设置某个值才能运行,否则不能运行,这个时候可以在这里加这个校验,
			 * 重写initPropertySources方法就好了
			 */
			prepareRefresh();
 
			// Tell the subclass to refresh the internal bean factory.
			/**
			 * 初始化BeanFactory,解析XML,相当于之前的XmlBeanFactory的操作,
			 */
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 
			// Prepare the bean factory for use in this context.
			/**
			 * 为上下文准备BeanFactory,即对BeanFactory的各种功能进行填充,如常用的注解@Autowired @Qualifier等
			 * 设置SPEL表达式#{key}的解析器
			 * 设置资源编辑注册器,如PerpertyEditorSupper的支持
			 * 添加ApplicationContextAwareProcessor处理器
			 * 在依赖注入忽略实现*Aware的接口,如EnvironmentAware、ApplicationEventPublisherAware等
			 * 注册依赖,如一个bean的属性中含有ApplicationEventPublisher(beanFactory),则会将beanFactory的实例注入进去
			 */
			prepareBeanFactory(beanFactory);
 
			try {
				// Allows post-processing of the bean factory in context subclasses.
				/**
				 * 提供子类覆盖的额外处理,即子类处理自定义的BeanFactoryPostProcess
				 */
				postProcessBeanFactory(beanFactory);
 
				// Invoke factory processors registered as beans in the context.
				/**
				 * 激活各种BeanFactory处理器,包括BeanDefinitionRegistryBeanFactoryPostProcessor和普通的BeanFactoryPostProcessor
				 * 执行对应的postProcessBeanDefinitionRegistry方法 和  postProcessBeanFactory方法
				 */
				invokeBeanFactoryPostProcessors(beanFactory);
 
				// Register bean processors that intercept bean creation.
				/**
				 * 注册拦截Bean创建的Bean处理器,即注册BeanPostProcessor,不是BeanFactoryPostProcessor,注意两者的区别
				 * 注意,这里仅仅是注册,并不会执行对应的方法,将在bean的实例化时执行对应的方法
				 */
				registerBeanPostProcessors(beanFactory);
 
				// Initialize message source for this context.
				/**
				 * 初始化上下文中的资源文件,如国际化文件的处理等
				 */
				initMessageSource();
 
				// Initialize event multicaster for this context.
				/**
				 * 初始化上下文事件广播器,并放入applicatioEventMulticaster,如ApplicationEventPublisher
				 */
				initApplicationEventMulticaster();
 
				// Initialize other special beans in specific context subclasses.
				/**
				 * 给子类扩展初始化其他Bean
				 */
				onRefresh();
 
				// Check for listener beans and register them.
				/**
				 * 在所有bean中查找listener bean,然后注册到广播器中
				 */
				registerListeners();
 
				// Instantiate all remaining (non-lazy-init) singletons.
				/**
				 * 设置转换器
				 * 注册一个默认的属性值解析器
				 * 冻结所有的bean定义,说明注册的bean定义将不能被修改或进一步的处理
				 * 初始化剩余的非惰性的bean,即初始化非延迟加载的bean
				 */
				finishBeanFactoryInitialization(beanFactory);
 
				// Last step: publish corresponding event.
				/**
				 * 初始化生命周期处理器DefaultLifecycleProcessor,DefaultLifecycleProcessor含有start方法和stop方法,spring启动的时候调用start方法开始生命周期,
				 * spring关闭的时候调用stop方法来结束生命周期,通常用来配置后台程序,启动有一直运行,如一直轮询kafka
				 * 启动所有实现了Lifecycle接口的类
				 * 通过spring的事件发布机制发布ContextRefreshedEvent事件,以保证对应的监听器做进一步的处理,即对那种在spring启动后需要处理的一些类,这些类实现了
				 * ApplicationListener ,这里就是要触发这些类的执行(执行onApplicationEvent方法)另外,spring的内置Event有ContextClosedEvent、ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、RequestHandleEvent
				 * 完成初始化,通知生命周期处理器lifeCycleProcessor刷新过程,同时发出ContextRefreshEvent通知其他人
				 */
				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();
			}
		}
}

refresh方法在spring整个源码体系中非常的重要,是实现 ioc 和 aop的关键。

1.8.7 刷新容器后的扩展接口

protected void afterRefresh(ConfigurableApplicationContext context,
			ApplicationArguments args) {
	}

扩展接口,设计模式中的模板方法,默认为空实现。如果有自定义需求,可以重写该方法。比如打印一些启动结束log,或者一些其它后置处理。

做一个简单的总结:

  1. 获取并启动监听器
  2. 构造容器环境
  3. 创建容器
  4. 启动错误报告
  5. 准备容器
  6. 刷新容器
  7. 刷新容器后的扩展接口

springboot2.1.1的启动流程先分析到。

参考链接:
https://blog.csdn.net/woshilijiuyi/article/details/82219585

如有问题,一起讨论(QQ:1561281670)。

你可能感兴趣的:(springboot)