spring boot源码 (一)启动过程

首先spring boot就是一个约定大于配置的简化开发的一个框架,如果看spring boot的源码,我觉得有两个方向,一个方向就是spring boot基于spring容器以及自身的一些启动的过程,另外一个就是约定大于配置这个目的实现代码。

从spring boot容器的启动过程开始。

spring boot的启动无非就是一行代码,SpringApplication.run,不过这是一个静态的方法,其中内部的调用如下。

	/**
	 * Static helper that can be used to run a {@link SpringApplication} from the
	 * specified sources using default settings and user supplied arguments.
	 * @param primarySources the primary sources to load
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return the running {@link ApplicationContext}
	 */
	public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

当然建议直接使用new SpringApplication来启动spring boot,这样做有一个好处,可以在执行run方法前,设置一些自定义的属性。先来看看构造方法中的操作。

/**
	 * 创建一个新的SpringApplication实例,这个context会通过给定的primarySources参数加载bean
	 * Create a new {@link SpringApplication} instance. The application context will load
	 * beans from the specified primary sources (see {@link SpringApplication class-level}
	 * documentation for details. The instance can be customized before calling
	 * {@link #run(String...)}.
	 * @param resourceLoader the resource loader to use
	 * @param primarySources the primary bean sources
	 * @see #run(Class, String[])
	 * @see #setSources(Set)
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {

		//resourceLoader 可以自己从外部传入,或者默认当前是空
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        //WebApplicationType 有三种
		// NONE 不需要内嵌的web容器,不以web的方式启动
		// SERVLET 需要内嵌的servlet的web容器,启动一个基于servlet的web应用程序
        // REACTIVE 需要内嵌的reactive的web容器,启动一个基于reactive的web应用程序  参考webflux
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
        //从 META-INF/spring.factories 中找出实现了ApplicationContextInitializer的所有类设置到initializers中
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		//从 META-INF/spring.factories 中找出实现了ApplicationListener的所有类设置到listeners中
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
         //找到当前main方法所在的类
		this.mainApplicationClass = deduceMainApplicationClass();
	}

具体的功能都写在注释里面了,deduceFromClasspath返回WebApplicationType方法重点看一看,代码如下,决定当前是什么WebApplicationType就通过查看Class.forName是否能加载到以下这几个类

	static WebApplicationType deduceFromClasspath() {
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return WebApplicationType.NONE;
			}
		}
		return WebApplicationType.SERVLET;
	}

 SpringApplication初始化完成之后,执行run方法。

/**
	 * Run the Spring application, creating and refreshing a new
	 * {@link ApplicationContext}.
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return a running {@link ApplicationContext}
	 */
	public ConfigurableApplicationContext run(String... args) {
		//计时器
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();

		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		//配置headless,不用关注
		configureHeadlessProperty();
		//获取启动事件监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
		//传播启动事件
		listeners.starting();
		try {
			//初始化一个应用程序参数类
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

			//解析环境变量,参数,配置,发布事件
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			//创建spring容器类
			context = createApplicationContext();
			//获取继承了SpringBootExceptionReporter的类,异常输出
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
            
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			//刷新容器 spring refresh()
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			callRunners(context, applicationArguments);
		}

getRunListeners中获取SpringApplicationRunListener的实现类列表,而在spring boot的项目中默认有一个配置实现,EventPublishingRunListener

	private SpringApplicationRunListeners getRunListeners(String[] args) {
		//从 META-INF/spring.factories 中找出实现了SpringApplicationRunListener的所有类
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		return new SpringApplicationRunListeners(logger,
				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
	}

EventPublishingRunListener对象构造方法初始化过程中,创建了一个SimpleApplicationEventMulticaster,并添加了当前所有的事件监听器。

	public EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		for (ApplicationListener<?> listener : application.getListeners()) {
			this.initialMulticaster.addApplicationListener(listener);
		}
	}

在接下来的starting方法中,会调用EventPublishingRunListener监听器的starting方法,所以这里会向当前所有的监听器发布一个ApplicationStartingEvent事件

@Override
	public void starting() {
		this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
	}

当前支持ApplicationStartingEvent事件的监听器一共有四个:

1、LoggingApplicationListener

2、BackgroundPreinitializer

3、DelegatingApplicationListener

4、LiquibaseServiceLocatorApplicationListener。

LoggingApplicationListener用于初始化日志的一些配置,

BackgroundPreinitializer用于初始化一些耗时的操作(这里部分方法也没赋值,也不是静态变量存储的,看了也挺懵逼的,也不知道干了啥),其他的没有操作。

接着会去初始化环境变量的配置参数,首先获取一个Environment,先看当前SpringApplication中存不存在Environment,默认肯定没有那么就会根据webApplicationType的类型新建返回不同的Environment。

获取到Environment之后,执行configureEnvironment方法,首先创建了一个ConversionService ,接着配置PropertySources以及Profiles

/**
	 * Template method delegating to
	 * {@link #configurePropertySources(ConfigurableEnvironment, String[])} and
	 * {@link #configureProfiles(ConfigurableEnvironment, String[])} in that order.
	 * Override this method for complete control over Environment customization, or one of
	 * the above for fine-grained control over property sources or profiles, respectively.
	 * @param environment this application's environment
	 * @param args arguments passed to the {@code run} method
	 * @see #configureProfiles(ConfigurableEnvironment, String[])
	 * @see #configurePropertySources(ConfigurableEnvironment, String[])
	 */
	protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
		// addConversionService默认true
		if (this.addConversionService) {
			//创建一个类型转换器服务实例,创建过程中已经注册了默认的所有的类型转换器,单例模式
			ConversionService conversionService = ApplicationConversionService.getSharedInstance();
			environment.setConversionService((ConfigurableConversionService) conversionService);
		}
		//初始化PropertySources和Profiles
		configurePropertySources(environment, args);
		configureProfiles(environment, args);
	}

先看configurePropertySources的方法,首先看当前有没有defaultProperties,有的话添加,当前肯定没有,接下来就会判断当前有没有args变量,有的话创建一个SimpleCommandLinePropertySource。

接着是configureProfiles,这个方法就是将当前additionalProfiles变量中的Profiles和Environment中原有的组合,然后再放入Environment,当前additionalProfiles同样没有。

Environment装载完成之后,调用ConfigurationPropertySources.attach方法,创建一个ConfigurationPropertySourcesPropertySource放入Environment中。

接着执行environmentPrepared发布一个ApplicationEnvironmentPreparedEvent事件。

当前的默认事件接收者有以下几个:

1、ConfigFileApplicationListener

2、AnsiOutputApplicationListener

3、LoggingApplicationListener

4、BackgroundPreinitializer

5、ClasspathLoggingApplicationListener

6、DelegatingApplicationListener

7、FileEncodingApplicationListener

不过都对主线来说不是很重要,所以这里略过。

接着调用bindToSpringApplication,

createApplicationContext方法中开始创建spring的容器,根据不同的类型创建三种容器,当前看servlet的返回,AnnotationConfigServletWebServerApplicationContext

	/**
	 * Strategy method used to create the {@link ApplicationContext}. By default this
	 * method will respect any explicitly set application context or application context
	 * class before falling back to a suitable default.
	 * @return the application context (not yet refreshed)
	 * @see #setApplicationContextClass(Class)
	 */
	protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
				case SERVLET:
					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
					break;
				case REACTIVE:
					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
					break;
				default:
					contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
				}
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
			}
		}
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

spring的启动流程可以参考Spring源码分析(一)IoC容器,先看类图,看到AnnotationConfigServletWebServerApplicationContext继承自GenericWebApplicationContext,在初始化上,这个容器类和AnnotationConfigApplicationContext流程一样

再往下prepareContext,首先向spring容器中设置了当前的Environment,接着执行postProcessApplicationContext方法,向spring容器中设置当前的resourceLoader 和beanNameGenerator ,不过当前也没有这两个,接着设置ConversionService

	/**
	 * Apply any relevant post processing the {@link ApplicationContext}. Subclasses can
	 * apply additional processing as required.
	 * @param context the application 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());
			}
		}
		if (this.addConversionService) {
			context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
		}
	}

接着调用contextPrepared,发送ApplicationContextInitializedEvent事件,

这个事件接收者默认两个

1、BackgroundPreinitializer

2、DelegatingApplicationListener

接着向容器中注册单例ApplicationArguments,以及Banner,根据SpringApplication的lazyInitialization属性动态设置LazyInitializationBeanFactoryPostProcessor

接着执行load方法,这个可以参考spring容器的register方法,最后调用contextLoaded,这里先是找出继承了ApplicationContextAware的监听器,注入context容器对象,接着发布ApplicationPreparedEvent事件

	@Override
	public void contextLoaded(ConfigurableApplicationContext context) {
		for (ApplicationListener<?> listener : this.application.getListeners()) {
			if (listener instanceof ApplicationContextAware) {
				((ApplicationContextAware) listener).setApplicationContext(context);
			}
			context.addApplicationListener(listener);
		}
		this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
	}

当前事件接收者默认四个:

1、ConfigFileApplicationListener

2、LoggingApplicationListener

3、BackgroundPreinitializer

4、DelegatingApplicationListener

ConfigFileApplicationListener中添加了一个后置处理器PropertySourceOrderingPostProcessor,其他依旧略过。

接下来就会执行refreshContext方法,这个方法调用的就是spring容器的refresh方法,和AnnotationConfigApplicationContext那篇博客不同,当前是继承了GenericWebApplicationContext,所以部分子类实现部分有所区别。

其中先看prepareRefresh方法中的initPropertySources方法,这个方法是需要子类重写覆盖的,GenericWebApplicationContext中重写了该方法,不过由于当前这个servletContext不存在,所以方法也不会调用

/**
	 * {@inheritDoc}
	 * <p>Replace {@code Servlet}-related property sources.
	 */
	@Override
	protected void initPropertySources() {
		ConfigurableEnvironment env = getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, null);
		}
	}

接着是postProcessBeanFactory,AnnotationConfigServletWebServerApplicationContext中重写了这个方法,向容器中注册了一个WebApplicationContextServletContextAwareProcessor。

最重要的是onRefresh方法的重写,ServletWebServerApplicationContext中将会创建服务器。

	@Override
	protected void onRefresh() {
		super.onRefresh();
		try {
			createWebServer();
		}
		catch (Throwable ex) {
			throw new ApplicationContextException("Unable to start web server", ex);
		}
	}
private void createWebServer() {
		WebServer webServer = this.webServer;
		ServletContext servletContext = getServletContext();
		if (webServer == null && servletContext == null) {
			ServletWebServerFactory factory = getWebServerFactory();
			this.webServer = factory.getWebServer(getSelfInitializer());
		}
		else if (servletContext != null) {
			try {
				getSelfInitializer().onStartup(servletContext);
			}
			catch (ServletException ex) {
				throw new ApplicationContextException("Cannot initialize servlet context", ex);
			}
		}
		initPropertySources();
	}

首先当前webServer和servletContext默认肯定是空,所以会从spring容器中获取ServletWebServerFactory的首个Bean,当前是TomcatServletWebServerFactory

/**
	 * Returns the {@link ServletWebServerFactory} that should be used to create the
	 * embedded {@link WebServer}. By default this method searches for a suitable bean in
	 * the context itself.
	 * @return a {@link ServletWebServerFactory} (never {@code null})
	 */
	protected ServletWebServerFactory getWebServerFactory() {
		// Use bean names so that we don't consider the hierarchy
		String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
		if (beanNames.length == 0) {
			throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
					+ "ServletWebServerFactory bean.");
		}
		if (beanNames.length > 1) {
			throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
					+ "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
		}
		return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
	}

接下来会启动tomcat,这个过程略过,但是需要注意的是这里注册了一个回调,tomcat启动后会调用,以下代码是jdk8的方法引用。

	/**
	 * Returns the {@link ServletContextInitializer} that will be used to complete the
	 * setup of this {@link WebApplicationContext}.
	 * @return the self initializer
	 * @see #prepareWebApplicationContext(ServletContext)
	 */
	private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
		return this::selfInitialize;
	}

调用如下,首先prepareWebApplicationContext内容很简单,就是打印了几行日志,将servletContext设置到当前容器中,registerApplicationScope则是将servletContext设置为application的scope,registerEnvironmentBeans将contextParameters和contextAttributes注册进容器中,然后再onStartup方法中添加了一些默认的过滤器什么的。

	private void selfInitialize(ServletContext servletContext) throws ServletException {
		prepareWebApplicationContext(servletContext);
		registerApplicationScope(servletContext);
		WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
		for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
			beans.onStartup(servletContext);
		}
	}

最后再调用一遍initPropertySources方法。spring容器启动完成之后,返回,注册一个回调钩子,这个回调钩子会在jvm停止时被调用

public void registerShutdownHook() {
		if (this.shutdownHook == null) {
			// No shutdown hook registered yet.
			this.shutdownHook = new Thread(SHUTDOWN_HOOK_THREAD_NAME) {
				@Override
				public void run() {
					synchronized (startupShutdownMonitor) {
						doClose();
					}
				}
			};
			Runtime.getRuntime().addShutdownHook(this.shutdownHook);
		}
	}

接着调用afterRefresh方法,当前没有实现

接着调用listeners.started,发布ApplicationStartedEvent事件

当前事件接收者默认两个:

1、BackgroundPreinitializer

2、DelegatingApplicationListener

接着调用callRunners方法,这个方法就是找出实现了ApplicationRunner以及CommandLineRunner,调用run方法

最后调用listeners.running,发布ApplicationReadyEvent事件

当前事件接收者默认三个:

1、SpringApplicationAdminMXBeanRegistrar

2、BackgroundPreinitializer

3、DelegatingApplicationListener

spring boot启动完成。

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值