浅谈SpringBoot启动流程

手写SpringCloud项目地址,深入理解微服务核心原理.

github:https://github.com/huangjianguo2000/spring-cloud-lightweight
gitee:https://gitee.com/huangjianguo2000/spring-cloud-lightweigh

序目

SpringBoot启动流程就是创建IOC容器的过程。

版本不一样,代码实现也可能不一样,本文参考的2.2.6和2.7.14

我们运行main方法, 会调用SpringApplication的run方法, 是一个静态方法。
@SpringBootApplication只是标注当前类为配置类,这里run方法参数不是只能传启动类,只要传的是一个配置类就行, 该配置类就可以理解成定义了SpringBean的扫描文件。

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

run方法最终调用分为两步, 实例化SpringApplication对象和调用该对象的run方法。

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

所以SpringBoot启动流程分为两大步,1.实例化SpringApplication对象。2. 调用该对象的run方法。

一: 实例化对象

	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
            // 判断类型
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
            //获取监听器
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

1. 判断容器类型

这里主要是做属性赋值,和判断当前项目容器的类型的类型。就是一些初始化操作

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.XXXX = XXXX;
    .......
    this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 判断容器类型
    // ......
}

这里注意一点: 我们使用的starter-web用的是servlet模式。 容器类型是SERVLET

public enum WebApplicationType {
    NONE,
    SERVLET, // 传统的基于Servlet的编程模型
    REACTIVE; // 响应式编程模型
}

2. 去spring.factories获取监听器。

这里获取的是实现了ApplicationContextInitializer或者ApplicationListener接口的监听器。

二: run方法

	public ConfigurableApplicationContext run(String... args) {
		listeners.starting(); // 发布开始事件
		try {
                    // 封装参数 如--server.port = 8081
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
                    // 准备环境
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
                    // 打印banner
			Banner printedBanner = printBanner(environment);
                  // 创建容器
			context = createApplicationContext();
			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);
		}
		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;
	}

1: 发布starting事件

获取SpringApplicationRunListeners, 发布starting事件。默认情况下获取到的是EventPublishingRunListener,他会发布ApplicationStartingEvent事件。我们可以去消费这条事件。

SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);

消费事件示例:

public class ApplicationStartingEventListener implements ApplicationListener<ApplicationStartingEvent> {
    @Override
    public void onApplicationEvent(ApplicationStartingEvent event) {
        System.out.println("收到了发布事件");
    }
}

需要在spring.factories文件中注册监听器
org.springframework.context.ApplicationListener=\
com.example.demo.test.TestL

2: 获取环境变量

这里会获取到JVM的参数,操作系统的环境变量等,

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);//1
ConfigurableEnvironment environment = 
this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);//2

获取环境变量这个方法里面会调用listeners.environmentPrepared(bootstrapContext, environment);来发布ApplicationEnvironmentPreparedEvent事件。同样我们可以对这个事件进行消费。
SpringBoot会在EnvironmentPostProcessorApplicationListener中来处理这个事件,他定义了很多个处理该事件的postprocessor来处理配置, 向environment对象中添加属性。

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
   ConfigurableEnvironment environment = event.getEnvironment();
   SpringApplication application = event.getSpringApplication();
   for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),
         event.getBootstrapContext())) {
      postProcessor.postProcessEnvironment(environment, application);
   }
}

环境变量步骤完后会打印banner。Banner printedBanner = printBanner(environment);

3:创建Spring容器。

context = createApplicationContext();

这里会根据实例化SpringBootApplication对象时候获取到的webApplicationType类型来创建容器。我们平时用的阻塞式编程创建的就是AnnotationConfigServletWebServerApplicationContext
我使用的2.2.6代码如下,2.7.X写法变了, 但是效果都是一样的

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

4: 预处理容器

就是对刚才创建的容器对象进行赋值,加载配置类等操作。

prepareContext(context, environment, listeners, applicationArguments, printedBanner){
    context.setEnvironment(environment);// 1. 环境对象赋值
    postProcessApplicationContext(context);// 2. 处理器 设置 Bean 名称生成器、设置资源加载器以及配置类型转换服务
    applyInitializers(context);// 3. 利用ApplicationContextInitializer进行一些初始化操作。比如添加Bean工厂后置处理器等
    listeners.contextPrepared(context);// 4. 发布事件
    ....
    if (this.lazyInitialization) { // 5. 如果Bean容器是设置了懒加载的,就添加懒加载的处理器
      context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
      // 懒加载的实现很简单, 就是给所有的bean定义设置懒加载属性为true
      // beanDefinition.setLazyInit(true);
    }
    .....
    load(context, sources.toArray(new Object[0]));//6. 把我们传入的配置类加载到容器里,正常情况下就是启动类
    isteners.contextLoaded(context);// 7. 发布加载完成事件
    
}

5:刷新容器

refreshContext(context);, 这个就是我们熟知的Spring的核心方法,这不用细说了。Tomcat就是在这个阶段创建的,具体是onRefresh方法调用TomcatServletWebServerFactory的getWebServer方法进行创建。其实就是Tomcat tomcat = new Tomcat();

6:发布启动完成事件

调用listeners.started(context);发布事件ApplicationStartedEvent

7:发布SpringBoot正在运行事件

listeners.running(context);

8: 结束

return 返回我们创建好的容器。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值