zoukankan      html  css  js  c++  java
  • Spring Boot启动流程

    一.前言

    本文主要讲解spring boot应用启动的流程,看spring boot在启动过程中,如何实现以下几个非常重要的过程:

    1. 如何决定是web应用
    2. 创建spring环境
    3. 创建上下文

    对于每个步骤的原理细节,本文不详细介绍。本文旨在探索宏观的流程控制。


    二.整体流程

    众所周知,Java应用的启动入口在于包含main方法的主类中,对于传统的web应用,包含main方法的主类由web容器实现。应用开发者只要遵循web开发规范,而不需要关心main方法主类。

    spring boot应用恰好相反,spring boot应用的main方法主类由应用开发者编写,主要利用其SpringApplication快捷启动项目。

    SpringApplication主要是帮助引导和启动spring boot应用,它本身提供很多配置方法用于设置spring应用。在启动过程中SpringApplication中主要完成以下几个步骤:

    1. 初始化SpringApplication
    2. 创建合适的spring环境
    3. 打印banner
    4. 依赖类路径创建合适的ApplicationContext
    5. 注册CommandLinePropertySource,作为命令行参数属性源
    6. 刷新上下文,载入单例Bean
    7. 以上的每个阶段,触发监听器的相应阶段
    8. 如果启动失败,进行失败分析

    接下来看下SpringApplication的几个主步骤:

    1.初始化SpringApplication
    @SuppressWarnings({ "unchecked", "rawtypes" })
    private void initialize(Object[] sources) {
    	if (sources != null && sources.length > 0) {
    		this.sources.addAll(Arrays.asList(sources));
    	}
    	this.webEnvironment = deduceWebEnvironment();
    	setInitializers((Collection) getSpringFactoriesInstances(
    			ApplicationContextInitializer.class));
    	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    	this.mainApplicationClass = deduceMainApplicationClass();
    }
    

    这个initialize方法中主要是将配置的源类加入Set集合,决定是否为web环境、设置上下文初始化器、设置上下文监听器、mainApplicationClass。

    2.SpringApplication run
    public ConfigurableApplicationContext run(String... args) {
    	// 创建StopWatch,用于统计spring boot应用的启动时间
    	StopWatch stopWatch = new StopWatch();
    	stopWatch.start();
    	// 定义applicationContext,失败分析器analyzers
    	ConfigurableApplicationContext context = null;
    	FailureAnalyzers analyzers = null;
    	configureHeadlessProperty();
    	// 加载SpringApplicationRunListener,主要用于监听spring boot应用run过程的每个主要阶段
    	SpringApplicationRunListeners listeners = getRunListeners(args);
    	// 监听spring应用run过程的start开始阶段
    	listeners.started();
    	try {
    		// 创建ApplicationArguments,抽象保存应用main方法参数
    		ApplicationArguments applicationArguments = new DefaultApplicationArguments(
    				args);
    		// 准备Environment
    		ConfigurableEnvironment environment = prepareEnvironment(listeners,
    				applicationArguments);
    		// 打印Banner
    		Banner printedBanner = printBanner(environment);
    		// 创建ApplicationContext
    		context = createApplicationContext();
    		// 创建失败分析器
    		analyzers = new FailureAnalyzers(context);
    		// 上下文准备,主要是配置上下文。刷新的前置
    		prepareContext(context, environment, listeners, applicationArguments,
    				printedBanner);
    		// 刷新上下文,就是加载bean
    		refreshContext(context);
    		// 刷新的后置
    		afterRefresh(context, applicationArguments);
    		// 监听spring应用run过程的finish完成阶段
    		listeners.finished(context, null);
    		// stopWatch停止,代表spring应用启动完成,完成启动的时间统计
    		stopWatch.stop();
    		if (this.logStartupInfo) {
    			new StartupInfoLogger(this.mainApplicationClass)
    					.logStarted(getApplicationLog(), stopWatch);
    		}
    		return context;
    	}
    	catch (Throwable ex) {
    		handleRunFailure(context, listeners, analyzers, ex);
    		throw new IllegalStateException(ex);
    	}
    }
    

    以上的run方法是spring boot应用启动的核心流程,启动的逻辑控制完全在该run中实现。

    接下来就细细剖析其中的每个阶段。


    三.如何决定Web应用

    spring boot是根据类路径是否存在web应用的标志类来判断是否为web应用。所以,这点对于日常开发项目时,maven的依赖要求非常严格,务必不能混乱引用依赖,同事也要注意依赖传递等。

    private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
    		"org.springframework.web.context.ConfigurableWebApplicationContext" };
    

    如果类路径上有以上两个类,spring boot则认为该应用是为web应用。

    private boolean deduceWebEnvironment() {
    	for (String className : WEB_ENVIRONMENT_CLASSES) {
    		if (!ClassUtils.isPresent(className, null)) {
    			return false;
    		}
    	}
    	return true;
    }
    

    如果Servlet和ConfigurableWebApplicationContext都在类路径上,即认为是web项目。


    四.创建spring环境

    private ConfigurableEnvironment prepareEnvironment(
    		SpringApplicationRunListeners listeners,
    		ApplicationArguments applicationArguments) {
    	// 创建spring环境
    	ConfigurableEnvironment environment = getOrCreateEnvironment();
    	// 配置spring环境
    	configureEnvironment(environment, applicationArguments.getSourceArgs());
    	// 触发监听器环境准备阶段
    	listeners.environmentPrepared(environment);
    	if (isWebEnvironment(environment) && !this.webEnvironment) {
    		environment = convertToStandardEnvironment(environment);
    	}
    	return environment;
    }
    

    spring环境准备由SpringApplication中的prepareEnvironment完成,其中抉择环境时,会根据是否为web应用选择不同的环境类型:

    • StandardEnvironment:标准的spring环境,其中包含了profiles和spring propertySources
    • StandardServletEnvironment:web spring环境,是StandardEnvironment的扩展,其中增加了servletContext propertySource和servletConfig propertySource

    其中配置spring环境时,会将命令行的参数和prfile解析进入spring环境的propertySource和profiles中。


    五.创建上下文

    protected ConfigurableApplicationContext createApplicationContext() {
    	// 如果SpringApplication中的applicationContextClass不为空,直接返回applicationContextClass
    	Class<?> contextClass = this.applicationContextClass;
    	if (contextClass == null) {
    		try {
    			// 依据web环境,抉择上下文类型
    			contextClass = Class.forName(this.webEnvironment
    					? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
    		}
    		catch (ClassNotFoundException ex) {
    			throw new IllegalStateException(
    					"Unable create a default ApplicationContext, "
    							+ "please specify an ApplicationContextClass",
    					ex);
    		}
    	}
    	// 格局类型,反射实例化上下问类
    	return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
    }
    

    spring boot抉择上下文由SpringApplication中的createApplicationContext实现,其中只有两种上下文形式:

    • DEFAULT_WEB_CONTEXT_CLASS:嵌入式的web上下文AnnotationConfigEmbeddedWebApplicationContext。该上下文由spring boot实现,其中包含了嵌入式容器的创建,初始化等逻辑

    • DEFAULT_CONTEXT_CLASS:注解式配置上下文AnnotationConfigApplicationContext,该上下文是spring框架中实现,主要用于注解式配置的spring应用

  • 相关阅读:
    [SQL Server]分页功能的实现
    [Others]每个文件夹都具有的特殊文件夹
    [ASP.NET]使页面的大小适合打印尺寸
    [SQL Server]树形结构的创建
    [ASP.NET]获取用户控件对象的引用
    [SQL Server]关于15517号错误的一点想法
    [SQL Server]创建自定义聚合函数值得注意的问题
    Java开源BI商业智能工具
    电子商务网站搜索架构方案
    产品经理如何培养对市场的敏感度和洞察力?
  • 原文地址:https://www.cnblogs.com/lxyit/p/12511709.html
Copyright © 2011-2022 走看看