SpringBoot启动流程

启动类

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

    }
}

@SpringBootApplication

对于一个SpringBoot程序的启动首先需要一个加了@SpringBootApplication注解的启动类。

@SpringBootApplication本质上是一个复合注解由三个注解组成

  1. @EnableAutoConfiguration,有了它之后再启动时就会导入“自动配置”AutoConfigurationImportSelector类,这个类会将所有复合条件的@Configuration配置都进行加载。
  2. @SpringBootConfiguration,等同于@Configuration,就是将这个类标记为配置类,会被加载到容器中。
  3. @ComponentScan,自动扫描并加载复合条件的Bean

如果启动类中不需要增加配置内容不需要指定扫描路径,可以用@EnableAutoConfiguration替代@SpringBootApplication

SpringApplication.run(App.class,args);

注解完成后,我们运行的起点就是SpringApplicationrun方法,再run方法执行后会经历如下四个阶段

  1. 服务构建
  2. 环境准备
  3. 容器创建
  4. 填充容器
  5. 执行Runner

服务构建

服务构建指的服务就是SpringApplication本身,这个阶段指的就是创建SpringApplication对象。

  1. 首先需要把传入的资源加载器、主方法类记录再内存中

    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources))
    
  2. 然后逐一判断对应的服务类是否存在来确定Web服务的类型,ServletReactiveNone,默认为Servlet。

    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    
  3. 加载初始化类,通过读取所有META-INF/spring.factories文件中BootstrapRegistryInitializer(注册初始化)、ApplicationContextInitializer(上下文初始化)、ApplicationListener(监听器)这三类配置。SpringBoot中没有默认的初始化配置,但是配置了7个上下文初始化和8个监听器。这些配置信息会在后续的启动中用到,我们也可以自定义这三个配置,只需要放到工程中的spring.factories文件中。

    this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    
  4. 判断main方法所在的类,大概率就是启动类本身。

    this.mainApplicationClass = deduceMainApplicationClass();
    

自此我们的Spring服务SpringApplication就构造完成了。

环境准备

这个阶段的目的是给即将创建的容器配置好环境信息。

  1. 我们会先创建一个后续会用到的启动上下文BootStrapContext,同时逐一调用刚刚加载的"注册初始化器"BootstrapRegistryInitializer中的initialize方法。因为我们默认的没有BootstrapRegistryInitializer所以并不执行什么。

    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    
    	private DefaultBootstrapContext createBootstrapContext() {
    		DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
    		this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));
    		return bootstrapContext;
    	}
    
  2. 设置缺少显示器、键盘等输入设备也可以正常启动

    configureHeadlessProperty();
    
    private void configureHeadlessProperty() {
    		System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
    				System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
    	}
    
  3. 获取并启动'运行监听器'SpringApplicationRunListeners,同时发布启动事件。它获取并加载SpringBoot工程中的spirng.factories配置文件中的EventPublishingRunListener,它在启动时也会将刚刚我们说的8个ApplicationListener都进行引入。

    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting(bootstrapContext, this.mainApplicationClass);
    
    	private SpringApplicationRunListeners getRunListeners(String[] args) {
    		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    		return new SpringApplicationRunListeners(logger,
    				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
    				this.applicationStartup);
    	}
    
  4. 通过prepareEnvironment方法组装启动参数。

    	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
    			DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
    		//第一步
    		ConfigurableEnvironment environment = getOrCreateEnvironment();
        //第二步
    		configureEnvironment(environment, applicationArguments.getSourceArgs());
    		ConfigurationPropertySources.attach(environment);
    		listeners.environmentPrepared(bootstrapContext, environment);
    		DefaultPropertiesPropertySource.moveToEnd(environment);
        
    		bindToSpringApplication(environment);
    		if (!this.isCustomEnvironment) {
          
    			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
    					deduceEnvironmentClass());
    		}
    		ConfigurationPropertySources.attach(environment);
    		return environment;
    	}
    
    1. 根据不同的Web环境构建不同的环境 ,默认Servlet
    2. 加载systemEnvironment(系统环境变量,比如JAVA_HOME、PATH)systemProperties(jvm系统属性比如java.vm.version、file.encoding)等在内的4组配置信息。把这些配置信息都加载到一个叫做propertySources的内存集合中,方便后续使用就无需重新加载。
    3. 通过configureEnvironment(配置环境)将我们传入的环境参数args进行设置。发布环境准备完成事件
  5. spring.beaninfo.ignore设置为true,表示不加载Bean的元数据信息,同时打印Banner图

容器创建

我们通过createApplicationContext来创建容器,首先根据服务类型创建ConfigurableApplicationContext,默认的服务类型是servlet,所以创建的AnnotationConfigServletWebServerApplicationContext,在这个过程中会构造诸如:

  1. 存放和生产我们Bean实例的Bean工厂DefaultListableBeanFactory
  2. 用来解析@Component、@ComponentScan等注解的配置类后处理器ConfigurationClassPostProcessor
  3. 用来解析@Autowired、@Value、@Inject等注解的Bean后处理器AutowiredAnnotationBeanPostProcessor等在内的属性对象。

接着通过prepareContext方法对容器中的部分属性进行初始化

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);
  // 第一步
		postProcessApplicationContext(context);
  // 第二步
		applyInitializers(context);
		
		// 第三步
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
		if (beanFactory instanceof DefaultListableBeanFactory) {
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		if (this.lazyInitialization) {
			context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
		}
		// Load the sources
		Set<Object> sources = getAllSources();
		// 第四步
		load(context, sources.toArray(new Object[0]));
		listeners.contextLoaded(context);
	}
  1. 先用postProcessApplicationContext 方法设置Bean名称生成器、资源加载器、类型转换器。
  2. 接着执行之前我们加载进来的上下文初始化 ApplicationContextInitializer
  3. 为容器注册启动参数、Banner、Bean引用策略和懒加载等。
  4. 通过Bean定义加载器将启动类在内的资源加载到Bean定义池BeanDefinitionMap中,以便后续根据Bean定义创建Bean对象。
  5. 发布资源加载完成事件。

填充容器

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);
				beanPostProcess.end();

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				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();
				contextRefresh.end();
			}
		}
	}
  1. 通过prepareRefresh方法,在已有的系统环境基础上准备servlet相关的环境Environment,通过initServletPropertySources对servlet初始化参数servletContextInitParamsservletConfigInitParams进行赋值。然后通过validateRequiredProperties方法检验是否有必填的环境变量。

  2. 通过obtainFreshBeanFactory,通过obtainFreshBeanFactory重新构造BeanFactory。

  3. prepareBeanFactory方法主要在BeanFactory中准备 BeanClassLoader(类加载器)BeanExpressionResolver(表达式解析器)PropertyEditorRegistrar(配置文件处理器)等系统级处理器,用以解析Aware接口的ApplicationContextAwareProcessor,用来处理自定义监听器注册和注销的ApplicationListenerDetector。同时会注册一些特殊的Bean和系统级Bean,比如容器本身BeanFactoryApplicationContext,系统环境environment,系统属性systemProperties将它们放入特殊对象池和单例池中。

  4. 通过postProcessBeanFactory对BeanFactory进行额外设置或修改,这里主要定义了包括requestsession在内的servlet相关作用域Scopes,同时也注册跟Servlet相关的一些特殊Bean。包括ServletRequest,ServletResponse,HttpSession,WebRequest

        public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, @Nullable ServletContext sc) {
            beanFactory.registerScope("request", new RequestScope());
            beanFactory.registerScope("session", new SessionScope());
            if (sc != null) {
                ServletContextScope appScope = new ServletContextScope(sc);
                beanFactory.registerScope("application", appScope);
                sc.setAttribute(ServletContextScope.class.getName(), appScope);
            }
    
            beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory());
            beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory());
            beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory());
            beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory());
    
        }
    
  5. 执行invokeBeanFactoryPostProcessors,会逐一执行在第三个大阶段容器创建中注册的各种BeanFactory后置处理器beanFactoryPostProcessor。其中最主要的就是用来加载所有Bean定义的ConfigurationClassPostProcessor,通过它加载所有的@Configuration配置类。同时检索指定的Bean扫描路径compoentScans,然后通过Bean扫描器ClassPathBeanDefinitionScannerdoScan扫描每个类,将所有扫描的Bean定义,都放到Bean定义池beanDefinitionMap中。同样也会扫描所有加了@Bean、@Import等注解的类和方法,将它们对应的Bean定义也都放到Bean定义池中。

  6. 通过registerBeanPostProcessors检索所有的Bean后置处理器,同时根据指定的order进行排序,然后放入后置处理器池beanPostProcessors中,没一个后置处理器都会在Bean初始化之前和之后分别执行对应的逻辑。

    public static void registerBeanPostProcessors(
    			ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
    
    
    		String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
    
    
    		for (String ppName : postProcessorNames) {
    			postProcessors.add(beanFactory.getBean(ppName,BeanPostProcessor.class))
    			
    		}
    		sortPostProcessors(postprocessors, beanFactory);
    		registerBeanPostProcessors(beanFactory, postprocessors);
    		// ...
    		}
    
  7. initMessageSource从单例池中获名为messageSource的Bean放入ApplicationContext中用于实现国际化。

  8. initApplicationEventMulticaster 从单例池中获取名为applicationEventMulticaster的Bean放入ApplicationContext中用于自定义广播事件,有了它就可以通过publishEvent方法进行事件的发布。

  9. 通过onRefresh构造并启动Web服务器,先查找实现了ServletWebServerFactory这个接口的应用服务器Bean,默认是tomcat,接下来通过getWebServer方法构造一个Tomcat对象,同时通过start方法启动。这样容器内的web服务器就开始运行了

  10. registerListeners,在bean中查找所有的监听器Bean,将它们注册到第8步构造的消息广播器applicationEventMulticaster

  11. finishBeanFactoryInitialization这一步来生产我们所有的Bean整体分为:构造对象、填充属性、初始化实例、注册销毁 四个步骤

  12. finishRefresh构造并注册lifecycleProcessor(生命周期管理器),同时会调用所有实现了LifeCycle的Bean中的start方法。发布一个容器刷新完成的事件。

执行Runner

Spring Boot提供了ApplicationRunnerCommandLineRunner两种类型的接口,可以让我们在容器启动完成后,来回调执行run方法。

	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);
			}
		}
	}
posted @ 2023-04-05 22:11  loveletters  阅读(145)  评论(0编辑  收藏  举报