亲宝软件园·资讯

展开

Springboot启动原理

bijian-bijian 人气:0

主启动类方法:

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

点击进入方法

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

先看看new SpringApplication(primarySources)里做了什么?

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
   this.resourceLoader = resourceLoader;
   Assert.notNull(primarySources, "PrimarySources must not be null");
   this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//判断当前web容器的类型,一般返回SERVLET,标识是web类型
   this.webApplicationType = WebApplicationType.deduceFromClasspath();
//获取META-INF/spring.factories文件中以//org.springframework.boot.Bootstrapper和
//org.springframework.boot.BootstrapRegistryInitializer为key的class
//创建对象,然后装入对象属性中;
   this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
   setInitializers((Collection) 
//获取META-INF/spring.factories文件中以
//org.springframework.context.ApplicationContextInitializer的key的对
//存入到initializers集合属性中;
getSpringFactoriesInstances(ApplicationContextInitializer.class));
//获取META-INF/spring.factories文件中以
//org.springframework.context.ApplicationListener的key的对
//存入到listeners集合属性中;
   setListeners((Collection) 
getSpringFactoriesInstances(ApplicationListener.class));
//找到main方法的启动主类class对象赋值到对象属性中。
   this.mainApplicationClass = deduceMainApplicationClass();
}

spring.factories读取了对外扩展的ApplicationContextInitializer ,ApplicationListener 对外扩展, 对类解耦(比如全局配置文件、热部署插件)

所以我们可以利用这一特性,在容器加载的各个阶段进行扩展。

上面读取到的对象getSpringFactoriesInstances(ApplicationContextInitializer.class))

存入到initializers集合中的对象

getSpringFactoriesInstances(ApplicationListener.class));存入到listeners集合属性中的对象

再看一下最重要的run方法:

public ConfigurableApplicationContext run(String... args) {
// 用来记录当前springboot启动耗时 
   StopWatch stopWatch = new StopWatch();
// 就是记录了启动开始时间 
   stopWatch.start();
//创建DefaultBootstrapContext bootstrapContext = new 
//DefaultBootstrapContext();对象,然后循环执行上面文件中赋值的bootstrapRegistryInitializers集合中对象的方法,入参就是bootstrapContext对象,利用此处我们可以扩展改变bootstrapContext对象中的一项属性值等,在这里还不知道此bootstrapContext对象的用处。
   DefaultBootstrapContext bootstrapContext = createBootstrapContext();
// 它是任何spring上下文的接口, 所以可以接收任何ApplicationContext实现 
   ConfigurableApplicationContext context = null;
// 开启了Headless模式,暂时不知道此模式的作用 
   configureHeadlessProperty();
//去spring.factroies中读取了 org.springframework.boot.SpringApplicationRunListener为key的对象,默认是EventPublishingRunListener对象,然后封装进SpringApplicationRunListeners 对象中,此对象还是比较有用的,用来发布springboot启动进行中的各个状态的事件,上面方法中读取到的监听器就可以监听到这些事件,所以可以运用这些特性进行自己的扩展。
   SpringApplicationRunListeners listeners = getRunListeners(args);
// 发布1.ApplicationStartingEvent事件,在运行开始时发送 ,发送springboot启动开始事件;
   listeners.starting(bootstrapContext, this.mainApplicationClass);
   try {
//根据启动项目命令所带的参数创建applicationArguments 对象
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//初始化环境变量:读取系统环境变量、发送了一个ApplicationEnvironmentPreparedEvent事件,利用相关监听器来解析项目中配置文件中的配置,(EnvironmentPostProcessorApplicationListener
监听器解析的配置文件)
      ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 忽略beaninfo的bean 
      configureIgnoreBeanInfo(environment);
// 打印Banner 横幅 
      Banner printedBanner = printBanner(environment);
//根据webApplicationType 容器类型,创建对应的spring上下文,一般是AnnotationConfigServletWebServerApplicationContext;
      context = createApplicationContext();
//给spring上下文赋值DefaultApplicationStartup对象
      context.setApplicationStartup(this.applicationStartup);
//预初始化上下文,这里做了给上下文添加environment对象,上面获取到的initializers集合中的ApplicationContextInitializer对象执行其入参为上下文initialize方法,对上下文做编辑,所以此处我们可以做扩展;发送ApplicationContextInitializedEvent容器初始化事件;发送BootstrapContextClosedEvent事件;最重要的一个方法就是把启动配置类注册成了beanDefinition;发送ApplicationPreparedEvent事件,并把listeners集合属性中的事件添加到上下文中;
	prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
	//刷新容器:和spring中的refresh()方法的作用是一样的,主要的作用就是读取所有的bean转成beanDefinition然后再创建bean对象;不过这个容器重写了其中的onRefresh()方法,在此方法中,创建了springboot内置的tomcat对象并进行启动;接下来特别说明一下这个内置tomcat
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      stopWatch.stop();
//打印启动时间;
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
      }
//发布容器启动事件ApplicationStartedEvent;发布AvailabilityChangeEvent容器可用实践;
      listeners.started(context);
//执行容器中ApplicationRunner、ApplicationRunner类型对象的run方法
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, listeners);
      throw new IllegalStateException(ex);
   }
   try {
//发布ApplicationReadyEvent容器已经正常事件;
      listeners.running(context);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

prepareEnvironment方法

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments) {
   // 根据webApplicationType 创建Environment  创建就会读取: java环境变量和系统环境变量
   ConfigurableEnvironment environment = getOrCreateEnvironment();
   // 将命令行参数读取环境变量中
   configureEnvironment(environment, applicationArguments.getSourceArgs());
   // 将@PropertieSource的配置信息 放在第一位, 因为读取配置文件@PropertieSource优先级是最低的
   ConfigurationPropertySources.attach(environment);
   // 发布了ApplicationEnvironmentPreparedEvent 的监听器  读取了全局配置文件
   listeners.environmentPrepared(environment);
   // 将所有spring.main 开头的配置信息绑定SpringApplication
   bindToSpringApplication(environment);
   if (!this.isCustomEnvironment) {
      environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
            deduceEnvironmentClass());
   }
   //更新PropertySources
   ConfigurationPropertySources.attach(environment);
   return environment;
}

lprepareContext

l预初始化上下文

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
      SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
   context.setEnvironment(environment);
   postProcessApplicationContext(context);
   // 拿到之前读取到所有ApplicationContextInitializer的组件, 循环调用initialize方法
   applyInitializers(context);
   // 发布了ApplicationContextInitializedEvent
   listeners.contextPrepared(context);
   if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
   }
   // 获取当前spring上下文beanFactory (负责创建bean)
   ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
   beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
   if (printedBanner != null) {
      beanFactory.registerSingleton("springBootBanner", printedBanner);
   }
   // 在Spring下 如果出现2个重名的bean, 则后读取到的会覆盖前面
   // 在SpringBoot 在这里设置了不允许覆盖, 当出现2个重名的bean 会抛出异常
   if (beanFactory instanceof DefaultListableBeanFactory) {
      ((DefaultListableBeanFactory) beanFactory)
            .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
   }
   // 设置当前spring容器是不是要将所有的bean设置为懒加载
   if (this.lazyInitialization) {
      context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
   }
   // Load the sources
   Set<Object> sources = getAllSources();
   Assert.notEmpty(sources, "Sources must not be empty");
   // 读取主启动类,将它注册为BD、就像我们以前register(启动类);一个意思 (因为后续要根据配置类解析配置的所有bean)
   load(context, sources.toArray(new Object[0]));
   //4.读取完配置类后发送ApplicationPreparedEvent。
   listeners.contextLoaded(context);
}

1.初始化SpringApplication 从spring.factories 读取 listener ApplicationContextInitializer 。

2.运行run方法

3.读取 环境变量 配置信息…

4.创建springApplication上下文:ServletWebServerApplicationContext

5.预初始化上下文 : 读取启动类

6.调用refresh 加载ioc容器

7.加载所有的自动配置类

8.创建servlet容器

9.ps.在这个过程中springboot会调用很多监听器对外进行扩展

看一下内置tomcat如何启动的:

Springboot的spring容器ServletWebServerApplicationContext对象重新了refresh()方法中的onRefresh()放用来启动tomcat

@Override
protected void onRefresh() {
   super.onRefresh();
   try {
//创建tomcat
      createWebServer();
   }
   catch (Throwable ex) {
      throw new ApplicationContextException("Unable to start web server", ex);
   }
}

createWebServer创建tomcat的方法(也可以是Jetty,根据配置)

private void createWebServer() {
   WebServer webServer = this.webServer;
   ServletContext servletContext = getServletContext();
//如果servletContext 为null说明是内置tomcat,不为null,则使用的是外置tomcat,这个servletContext 是tomcat创建传入的;
   if (webServer == null && servletContext == null) {
      StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
//获取servlet容器创建工厂,tomcat的是TomcatServletWebServerFactory
      ServletWebServerFactory factory = getWebServerFactory();
      createWebServer.tag("factory", factory.getClass().toString());
//创建内置tomcat对象,并启动;getSelfInitializer()这个方法也是很重要的,返回的是ServletWebServerApplicationContext#selfInitialize方法的引用函数,其作用是tomcat启动时会回调此方法,并传入servletContext对象,进行DispacherServlet添加到servletContext中,把当前spring容器和filter过滤器也添加到servletContext中
      this.webServer = factory.getWebServer(getSelfInitializer());
      createWebServer.end();
      getBeanFactory().registerSingleton("webServerGracefulShutdown",
            new WebServerGracefulShutdownLifecycle(this.webServer));
      getBeanFactory().registerSingleton("webServerStartStop",
            new WebServerStartStopLifecycle(this, this.webServer));
   }
   else if (servletContext != null) {
      try {
         getSelfInitializer().onStartup(servletContext);
      }
      catch (ServletException ex) {
         throw new ApplicationContextException("Cannot initialize servlet context", ex);
      }
   }
   initPropertySources();
}

selfInitializ方法

private void selfInitialize(ServletContext servletContext) throws ServletException {
//把spring容器和servletContext进行相互引用
   prepareWebApplicationContext(servletContext);
   registerApplicationScope(servletContext);
   WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
//servletContext中添加Dispacherservlet和多个fileter
   for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
      beans.onStartup(servletContext);
   }
}

加载全部内容

相关教程
猜你喜欢
用户评论