Spring Boot principle start parsing (execution flow articles)

Copyright Notice: Copyright: This article is a blogger original article, shall not be reproduced without the bloggers allowed. https://blog.csdn.net/qq_37598011/article/details/90969634

SpringApplication.run

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



	public static ConfigurableApplicationContext run(Class<?> primarySource,
			String... args) {
		return run(new Class<?>[] { primarySource }, args);
	}



	public static ConfigurableApplicationContext run(Class<?>[] primarySources,
			String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

    We can see SpringApplication.run start of two parts:

  • new SpringApplication (primarySources): instantiate a  SpringApplication target
  • run (args): execution

Instantiate SpringApplicationobjects

	public static ConfigurableApplicationContext run(Class<?>[] primarySources,
			String[] args) {
		return new SpringApplication(primarySources).run(args);
	}	



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



	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        //1、将资源初始化加载器设置为null
		this.resourceLoader = resourceLoader;
        //2、断言资源加载类不能为 null,否则报错
		Assert.notNull(primarySources, "PrimarySources must not be null");
        // 3、初始化加载资源类集合并去重
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        // 4、推断当前 WEB 应用类型,WebApplicationType(三种)
		this.webApplicationType = deduceWebApplicationType();
        // 5、设置应用上下文初始化器
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
        // 6、设置监听器 
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        // 7、推断主应用类
		this.mainApplicationClass = deduceMainApplicationClass();
	}

    Examples of SpringApplication seven principal steps:

  1. The resource loader initialized to null
  2. Assertion resource loaded class can not be null, otherwise an error
  3. Initialize and load the resource class set to heavy
  4. WEB concluded that the current application type, WebApplicationType (three kinds)
  5. Set application context initializer
  6. Set up listeners 
  7. Concluded that the main application class

1-4 is not a problem, take a look at the following major 5,6,7:  

Set application context initialization and listeners

	public void setInitializers(
			Collection<? extends ApplicationContextInitializer<?>> initializers) {
		this.initializers = new ArrayList<>();
		this.initializers.addAll(initializers);
	}


	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();
		//获取 ApplicationContextInitializer 的实例名称集合并去重,其实就是获取spring.factories中的配置类列表
		Set<String> names = new LinkedHashSet<>(
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        //根据返回的实例名称集合进行实例的创建并返回列表
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
        //实例列表排序
		AnnotationAwareOrderComparator.sort(instances);
        //实例列表返回
		return instances;
	}


    public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext>{
    
    	/**
    	 * Initialize the given application context.
    	 * @param applicationContext the application to configure
    	 */
	    void initialize(C applicationContext);

    }

You can see a list of instances set the initialization has five steps:

  1. Get the current thread context class loader
  2. Get ApplicationContextInitializer instance name to the collection and heavy, in fact, to get the configuration class list spring.factories in (in the comment papers have examined)
  3. According to the returned instance name to create an instance of the collection and returns a list
  4. Examples sorted list
  5. A list of instances returned

 

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

org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

The step of setting a listener provided context initialization process is substantially the same.

	public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
		this.listeners = new ArrayList<>();
		this.listeners.addAll(listeners);
	}


	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;
	}


    @FunctionalInterface
    public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

    	/**
    	 * Handle an application event.
    	 * @param event the event to respond to
    	 */
    	void onApplicationEvent(E event);

    }

    This interface inherits the JDK EventListener interface, the observer mode, it will do the listening event for the realization of the ApplicationListener class.

org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

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

Concluded that the main application class

	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;
	}

    It is built by a runtime exception is eligible, and then through the entire stack, inlet class to obtain information by matching a "main" method.

run method runs

public ConfigurableApplicationContext run(String... args) {
    // 1、创建并启动计时监控类
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    
    // 2、初始化应用上下文和异常报告集合
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    
    // 3、设置系统属性 `java.awt.headless` 的值,默认值为:true
    configureHeadlessProperty();
    
    // 4、创建所有 Spring 运行监听器并发布应用启动事件
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    
    try {
        // 5、初始化默认应用参数类
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                args);
                
        // 6、根据运行监听器和应用参数来准备 Spring 环境
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
                applicationArguments);
        configureIgnoreBeanInfo(environment);
        
        // 7、创建 Banner 打印类
        Banner printedBanner = printBanner(environment);
        
        // 8、创建应用上下文
        context = createApplicationContext();
        
        // 9、准备异常报告器
        exceptionReporters = getSpringFactoriesInstances(
                SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
                
        // 10、准备应用上下文
        prepareContext(context, environment, listeners, applicationArguments,
                printedBanner);
                
        // 11、刷新应用上下文
        refreshContext(context);
        
        // 12、应用上下文刷新后置处理
        afterRefresh(context, applicationArguments);
        
        // 13、停止计时监控类
        stopWatch.stop();
        
        // 14、输出日志记录执行主类名、时间信息
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                    .logStarted(getApplicationLog(), stopWatch);
        }
        
        // 15、发布应用上下文启动完成事件
        listeners.started(context);
        
        // 16、执行所有 Runner 运行器
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        // 17、发布应用上下文就绪事件
        listeners.running(context);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    
    // 18、返回应用上下文
    return context;
}

    After creating SpringApplication objects will execute the run method, run this method can be made of several parts:

  1. Create and start the timer monitoring class
  2. 初始化应用上下文和异常报告集合
  3. 设置系统属性 ‘java.awt.headless’ 的值,默认值为:true
  4. 创建所有 Spring 运行监听器并发布应用启动事件
  5. 初始化默认应用参数类
  6. 根据运行监听器和应用参数来准备 Spring 环境
  7. 创建 Banner 打印类
  8. 创建应用上下文
  9. 准备异常报告器
  10. 准备应用上下文
  11. 刷新应用上下文
  12. 应用上下文刷新后置处理
  13. 停止计时监控类
  14. 输出日志记录执行主类名、时间信息
  15. 发布应用上下文启动完成事件
  16. 执行所有 Runner 运行器
  17. 发布应用上下文就绪事件
  18. 返回应用上下文

创建并启动计时监控类 

	StopWatch stopWatch = new StopWatch();
	stopWatch.start();


        public void start() throws IllegalStateException {
		start("");
	}


	public void start(String taskName) throws IllegalStateException {
		if (this.currentTaskName != null) {
			throw new IllegalStateException("Can't start StopWatch: it's already running");
		}
		this.currentTaskName = taskName;
		this.startTimeMillis = System.currentTimeMillis();
	}

    可以看到它会传入一个空字符串给当前任务作名称,然后记录当前Spring Boot应用启动的开始时间。并且它会判断当前任务名是否存在,保证Spring Boot应用不重复启动。

初始化应用上下文和异常报告集合

		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();

设置系统属性

		configureHeadlessProperty();


	private void configureHeadlessProperty() {
		System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(
				SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
	}


	private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";

创建所有 Spring 运行监听器并发布应用启动事件

		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();


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


	SpringApplicationRunListeners(Log log,
			Collection<? extends SpringApplicationRunListener> listeners) {
		this.log = log;
		this.listeners = new ArrayList<>(listeners);
	}


	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
        //获取当前线程上下文类加载器
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		//获取 ApplicationContextInitializer 的实例名称集合并去重,其实就是获取spring.factories中的配置类列表
		Set<String> names = new LinkedHashSet<>(
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        //根据返回的实例名称集合进行实例的创建并返回列表
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
        //实例列表排序
		AnnotationAwareOrderComparator.sort(instances);
        //实例列表返回
		return instances;
	}

    这块的内容和之前实例化初始化器的流程是一样的,通过getSpringFactoriesInstances方法从spring.factories中获取与SpringApplicationRunListener.class相关的实例类名列表。

org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

	public void starting() {
                //遍历并启动
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.starting();
		}
	}

初始化默认应用参数类

			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);

根据运行监听器和应用参数来准备 Spring 环境

			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			configureIgnoreBeanInfo(environment);

    先看看prepareEnvironment方法:

	private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// 获取(或者创建)应用环境
		ConfigurableEnvironment environment = getOrCreateEnvironment();
                //配置应用环境
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		listeners.environmentPrepared(environment);
                //绑定到SpringApplication
		bindToSpringApplication(environment);
		if (this.webApplicationType == WebApplicationType.NONE) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertToStandardEnvironmentIfNecessary(environment);
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

        //根据创建SpringApplication中的类型创建环境类型
	private ConfigurableEnvironment getOrCreateEnvironment() {
		if (this.environment != null) {
			return this.environment;
		}
		if (this.webApplicationType == WebApplicationType.SERVLET) {
			return new StandardServletEnvironment();
		}
		return new StandardEnvironment();
	}


        //配置应用环境
	protected void configureEnvironment(ConfigurableEnvironment environment,
			String[] args) {
		configurePropertySources(environment, args);//配置 property sources
		configureProfiles(environment, args);//配置 pofiles
	}

    这一步主要做创建并配置当前Spring Boot应用将要使用的Environment(包括配置要使用的PropertySource和Profile)。

创建 Banner 打印类

Banner printedBanner = printBanner(environment);


	private Banner printBanner(ConfigurableEnvironment environment) {
		if (this.bannerMode == Banner.Mode.OFF) {
			return null;
		}
		ResourceLoader resourceLoader = (this.resourceLoader != null ? this.resourceLoader
				: new DefaultResourceLoader(getClassLoader()));
		SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(
				resourceLoader, this.banner);
		if (this.bannerMode == Mode.LOG) {
			return bannerPrinter.print(environment, this.mainApplicationClass, logger);
		}
		return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
	}

    这块就是创建一个Banner类没什么好说的。

创建应用上下文

    context = createApplicationContext();



	protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
				case SERVLET:
					contextClass = Class.forName(DEFAULT_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);
	}

   在这一步中它会根据webApplicationType的类型去反射创建ConfigurableApplicationContext的具体实例。

准备异常报告器

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

    这一步的逻辑和实例化初始化器和监听器的一样,都是通过调用 getSpringFactoriesInstances 方法来获取配置的异常类名称并实例化所有的异常处理类。

org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers

准备应用上下文

			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);


	private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
                //设置环境到上下文
		context.setEnvironment(environment);

                //设置上下文的 bean 生成器和资源加载器
		postProcessApplicationContext(context);

                //将任何applicationContextInitialalizer应用于上下文
		applyInitializers(context);

                //触发所有 SpringApplicationRunListener 监听器的 contextPrepared 事件方法
		listeners.contextPrepared(context);

                //记录启动日志
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}

		// 添加特定于引导的单例bean
		context.getBeanFactory().registerSingleton("springApplicationArguments",
				applicationArguments);
		if (printedBanner != null) {
			context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
		}

		// 加载所有资源
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));

                //触发所有 SpringApplicationRunListener 监听器的 contextLoaded 事件方法
		listeners.contextLoaded(context);
	}

    这块会对整个上下文进行一个预处理,比如触发监听器的相应事件、加载资源、设置上下文环境等等。

刷新应用上下文

    refreshContext(context);


	private void refreshContext(ConfigurableApplicationContext context) {
		refresh(context);
		if (this.registerShutdownHook) {
			try {
                //向JVM运行时注册一个关机钩子,在JVM关机时关闭这个上下文,
                //除非它当时已经关闭。
                //这个方法可以调用多次。每个上下文实例只注册一个关闭钩子(最大)。
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}


	protected void refresh(ApplicationContext applicationContext) {
		Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
		((AbstractApplicationContext) applicationContext).refresh();
	}

    这块主要做了两件事:

  1. 通过refresh方法对整个IoC容器的初始化(包括Bean资源的定位、解析、注册等等)
  2. 通过context.registerShutdownHook()(向JVM运行时注册一个关机钩子,在JVM关机时关闭这个上下文,除非它当时已经关闭。)

 应用上下文刷新后置处理

afterRefresh(context, applicationArguments);


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

    这块方法是空的,可以做一些自定义的后置处理操作。

停止计时监控类

    stopWatch.stop();



	public void stop() throws IllegalStateException {
		if (this.currentTaskName == null) {
			throw new IllegalStateException("Can't stop StopWatch: it's not running");
		}
		long lastTime = System.currentTimeMillis() - this.startTimeMillis;
		this.totalTimeMillis += lastTime;
		this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime);
		if (this.keepTaskList) {
			this.taskList.add(this.lastTaskInfo);
		}
		++this.taskCount;
		this.currentTaskName = null;
	}

    这块方法主要会做一个计时监听器停止操作,并统计一些任务执行信息。

输出日志记录执行主类名、时间信息

			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}


	protected Log getApplicationLog() {
		if (this.mainApplicationClass == null) {
			return logger;
		}
		return LogFactory.getLog(this.mainApplicationClass);
	}

    它会打印主类信息和时间信息等等……

发布应用上下文启动完成事件

    listeners.started(context);


	public void started(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.started(context);
		}
	}

    这块会执行所有SpringApplicationRunListener的实现的started方法。

执行所有 Runner 运行器

    callRunners(context, applicationArguments);


	private void callRunners(ApplicationContext context, ApplicationArguments args) {
		List<Object> runners = new ArrayList<>();
		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
		AnnotationAwareOrderComparator.sort(runners);
		for (Object runner : new LinkedHashSet<>(runners)) {
			if (runner instanceof ApplicationRunner) {
				callRunner((ApplicationRunner) runner, args);
			}
			if (runner instanceof CommandLineRunner) {
				callRunner((CommandLineRunner) runner, args);
			}
		}
	}

    执行所有 ApplicationRunner 和 CommandLineRunner 这两种运行器。

发布应用上下文就绪事件

    listeners.running(context);


	public void running(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.running(context);
		}
	}

    这块会执行所有SpringApplicationRunListener的实现的running方法。

返回应用上下文

	return context;

总结

     SpirngBoot通过将对特定的场景事先进行了固化,然后通过对自动配置类的反射加载到IoC容器中,最后使得整个开发变得更加方便简介。

 

参考:

https://www.cnblogs.com/shamo89/p/8184960.html

https://www.jianshu.com/p/714f7b054041

Guess you like

Origin blog.csdn.net/qq_37598011/article/details/90969634