一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析

       本文是对spring boot2 启动整个流程进行源码解析,也是我发表springboot系列的第一篇文章,解析的过程我会一行一行解读,网上很多的解析案例都是贴一段源码,然后说几句概况性的语句,看了之后也是丈二和尚 摸不到头脑,既然解析源码,我们就尽量彻底理解每一行是做什么的,有什么作用。

  下面我们来看源码,走进启动模式;

SpringApplication.run(BootApplication.class, args);

  启动类开始执行;

new SpringApplication(primarySources).run(args);

public SpringApplication(Class<?>... primarySources) {
    this(null, primarySources);
}

先走进SpringApplication的构造方法,new一个SpringApplication实例,然后执行run方法。run方法执行结束之后整个启动流程就是走完了。

1、SpringApplication 的构造函数,创建SpringApplication对象,一下是构造函数实现的具体初始化过程。

《一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析》

    //使用的资源加载器
   《一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析》
    //主要的bean资源 primarySources【在这里是启动类所在的.class】,不能为null,如果为null,抛异常
   《一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析》
    /**
    * primarySources 是Set<Class<?>> 类型,把启动类(我这里是定义为BootApplication)
    * .class (Class<?> 该类的实例数组转化成list,放在LinkedHashSet集合中)
    */

  《一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析》
    /**
    * deduceWebApplicationType() = SERVER
    * 应用程序应该作为基于servlet的web应用程序运行,并且应该启动一个
    * 嵌入式servlet web服务器。
    */

  《一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析》
     /**
      * 使用类加载机制,和发射实现对ApplicationContextInitializer 接口熟悉的初始化。
      * ApplicationContextInitializer是Spring IOC一个回调接口,它会在ConfigurableApplicationContext的refresh()
      * 方法调用之前被调用,做一些容器的初始化工作。每一个initailizer都是一个实现了ApplicationContextInitializer接口的实例。
      * springboot中一共有6个实现类,后面遇到再分析各个实现类初始化的作用。
      */

   《一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析》
    /**  使用类加载机制,和发射实现对ApplicationListener(事件监听容器) 接口熟悉的初始化。
        当用Spring ApplicationContext注册时,事件将相应地过滤,并调用侦听器以匹配事件对象。
     */

   《一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析》
    /**  指定main函数启动所在的类,即启动类BootApplication.class
   《一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析》
 

2、启动运行Spring应用程序,创建并刷新一个新应用application程序

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);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			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;
	}

 

      /**
            StopWatch: 简单的秒表,允许定时的一些任务,公开每个指定任务的总运行时间和运行时间。
            这个对象的设计不是线程安全的,没有使用同步。不会作为生产环境中使用,二此处因为启动
            SpringApplication是在单线程环境下,使用安全。
        */

       《一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析》
        /** 设置当前启动的时间为系统时间startTimeMillis = System.currentTimeMillis();*/
       《一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析》
        /** 系统设置headless模式
        (是一种系统配置,其中显示设备、键盘或鼠标缺乏。听起来出乎意料,但实际上您可以在这种模式下执行不同的操作,即            使是使用  图形数据。)
           想进一步了解的访问(https://www.oracle.com/technetwork/articles/javase/headless-136834.html)
        */

      《一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析》

/** 通过类加载机制和反射创建SpringApplicationRunListeners实例SpringApplicationRunListeners:在创建和更新ApplicationContext 方法前后分别触发回调的形式调用了listeners对象的started方法和finished….等等方法, 并在创建和刷新ApplicationContext的不同阶段,调用listeners的相应方法以执行操作。所以,SpringApplicationRunListeners实际上就是在SpringApplication 对象的run方法执行的不同阶段,去监听回调一些操作,并且这些操作是可配置的。  通过spring.factories中发现SpringApplicationRunListeners 加载的是org.springframework.boot.context.event.EventPublishingRunListener是 SpringApplicationRunListener的实现类.(SpringApplicationRunListeners的内部存储了SpringApplicationRunListener的集合,提供了SpringApplicationRunListener一样方  法,方便统一遍历调用所有SpringApplicationRunListener。)
        */

       《一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析》
        /** 开始监听指定的一些event事件,查看源码springboot中应该是有45处会触发回调*/
      《一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析》

      《一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析》
        
            /** 提供对用于运行SpringApplication的参数的访问。取默认实现*/
      《一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析》
            /**
                构建容器环境
            */

     《一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析》
             /**
                对环境中一些bean忽略配置
            */    

     《一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析》
            /**
            * 日志控制台打印设置
            * /

      《一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析》
            /**
                创建容器
            */

      《一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析》
            /**
                追踪源码prepareContext()进去我们可以发现容器准备阶段做了下面的事情:
                容器设置配置环境,并且监听容器,初始化容器,记录启动日志,
                将给定的singleton对象添加到此工厂的singleton缓存中。
                将bean加载到应用程序上下文中。
            */

         《一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析》
            /**
                1、同步刷新,对上下文的bean工厂包括子类的刷新准备使用,初始化此上下文的消息源,注册拦截bean的处理器,
                检查侦听器bean并注册它们,实例化所有剩余的(非延迟-init)单例。
                2、异步开启一个同步线程去时时监控容器是否被关闭,当关闭此应用程序上下文,销毁其bean工厂中的所有bean。
                如果我们注册了一个jvm shutdown hook,那就不需要单独去开启一个线程去监控了
            */

          《一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析》
            /** 刷新容器之后无任何操作,里面是空的方法*/
          《一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析》
            /**
             stopwatch 的作用就是记录启动消耗的时间,和开始启动的时间等信息记录下来
            */ 

          《一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析》
            /** 启动监听容器中所有的事件 :
                上下文已经刷新,应用程序已经启动,但是没有调用CommandLineRunners和applicationrunner。
            */

           《一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析》

            /**
            启动监听:
            当应用程序上下文被刷新,所有CommandLineRunners和applicationrunner都被调用时,在运行方法完成之前立即调用。
            */

          《一、【彻底精通Spring Boot2源码】 SpringApplication.run() 启动流程源码彻底剖析》

……………………………………..

 

上面的源码中多处都是内部是用了下面源码中的几个方法,其实本质就是解析加载spring-boot-2.0.3.RELEASE.jar/META-INF/spring.factories
中的配置,来创建初始化我们需要的Application context/listeners/Loaders。

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

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
		return getSpringFactoriesInstances(type, new Class<?>[] {});
	}

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		// Use names and ensure unique to protect against duplicates
		Set<String> names = new LinkedHashSet<>(
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

 

spring-boot-2.0.3.RELEASE.jar/META-INF/spring.factories:
源码如下:


# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader

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

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


........

 

springboot run启动流程就是这样!看完差不多也知道springboot启动成功之后做了哪些!

 

 

 

 

 

 

 

 

    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/Z0157/article/details/82845022
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞