SpringBoot2源码2-核心启动过程和run方法

1. SpringBoot中怎么启动Tomcat?

1.1 ServletWebServerFactoryAutoConfiguration

配置Servlet web容器。

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
      ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
      ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
      ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
}
  • ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class 导入 WebServerFactoryCustomizerBeanPostProcessor组件,web服务工厂定制器的后置增强

  • 导入了三种不同的web服务器(Tomcat、Jetty、Undertow),默认是Tomcat

我们来看一下 EmbeddedTomcat:

@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {

   @Configuration(proxyBeanMethods = false)
   @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
   @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
   static class EmbeddedTomcat {

      @Bean
      TomcatServletWebServerFactory tomcatServletWebServerFactory(
            ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
            ObjectProvider<TomcatContextCustomizer> contextCustomizers,
            ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
         TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
         factory.getTomcatConnectorCustomizers()
               .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
         factory.getTomcatContextCustomizers()
               .addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
         factory.getTomcatProtocolHandlerCustomizers()
               .addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
         return factory;
      }

   }
}

tomcatServletWebServerFactory()方法中的参数都是从容器中获取,我们可以自定义直接放到容器中。实现ServletWebServerFactory 接口,该接口的getWebServlet()方法就是创建 Tomcat容器。

1.2 DispatcherServletAutoConfiguration

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {

  
   public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";

   public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";

   @Configuration(proxyBeanMethods = false)
   @Conditional(DefaultDispatcherServletCondition.class)
   @ConditionalOnClass(ServletRegistration.class)
   @EnableConfigurationProperties(WebMvcProperties.class)
   protected static class DispatcherServletConfiguration {

      @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
      public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
         DispatcherServlet dispatcherServlet = new DispatcherServlet();
         dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
         dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
         dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
         dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
         dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
         return dispatcherServlet;
      }

      @Bean
      @ConditionalOnBean(MultipartResolver.class)
      @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
      public MultipartResolver multipartResolver(MultipartResolver resolver) {
         // Detect if the user has created a MultipartResolver but named it incorrectly
         return resolver;
      }

   }

   @Configuration(proxyBeanMethods = false)
   @Conditional(DispatcherServletRegistrationCondition.class)
   @ConditionalOnClass(ServletRegistration.class)
   @EnableConfigurationProperties(WebMvcProperties.class)
   @Import(DispatcherServletConfiguration.class)
   protected static class DispatcherServletRegistrationConfiguration {

      @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
      @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
      public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
            WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
         DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
               webMvcProperties.getServlet().getPath());
         registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
         registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
         multipartConfig.ifAvailable(registration::setMultipartConfig);
         return registration;
      }

   }
}
  • dispatcherServlet方法就是往容器中注入DispatcherServlet组件。
  • dispatcherServletRegistration() 方法就是将将DispatcherServlet组件添加到Tomcat中(tomcat.addServlet()

我们来看一下DispatcherServletRegistrationBean继承图:

在Tomcat启动的时候 会调用 ServletContextInitializer.onStartup() 方法回调,将DispatcherServlet 添加到 Tomcat中,后面的启动流程就跟SpringMVC的启动流程是一样的

2. 核心方法run()

下面是一个简单的项目,我们直接开始看run方法

@SpringBootApplication
public class SpringbootDemoApplication {
	
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(SpringbootDemoApplication.class, args);
        Object demoService = run.getBean("demoService");
        System.out.println("demoService = " + demoService);
   }
}

 我们这里直接看 SpringApplication#run(java.lang.Class<?>[], java.lang.String[]) 方法:

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

我们这里先看一下 SpringApplication 的构造函数流程:

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环境、web环境、reactive环境三种。其判断逻辑就是判断是否存在指定的类,默认是Servlet 环境,我们这也是Servlet
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        // getSpringFactoriesInstances 方法加载了 spring.factories文件。在这里进行了首次加载spring.factoies文件。设置 ApplicationContextInitializer
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // 获取监听器,也加载了spring.factories文件
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        // 设置启动类信息
        this.mainApplicationClass = deduceMainApplicationClass();
}

我们下面直接来看 SpringApplication#run(java.lang.String...) 方法的执行流程:

public ConfigurableApplicationContext run(String... args) {
        // 开启关于启动时间的信息监控
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        // 准备 ApplicationContext
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        //java.awt.headless是J2SE的一种模式用于在缺少显示屏、键盘或者鼠标时的系统配置,很多监控工具如jconsole 需要将该值设置为true,系统变量默认为true
        configureHeadlessProperty();
        // 1. 获取Spring的监听器类,这里是从 spring.factories 中去获取,默认的是以 org.springframework.boot.SpringApplicationRunListener 为key,获取到的监听器类型为 EventPublishingRunListener。
        SpringApplicationRunListeners listeners = getRunListeners(args);
        // 1.1 监听器发送启动事件
        listeners.starting();
        try {
                // 封装参数
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
                // 2. 构造容器环境。将容器的一些配置内容加载到 environment  中
                ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
                // 配置BeanInfo的忽略 :“spring.beaninfo.ignore”,值为“true”表示跳过对BeanInfo类的搜索
                configureIgnoreBeanInfo(environment);
                // 打印信息对象
                Banner printedBanner = printBanner(environment);
                // 3. 创建上下文对象
                context = createApplicationContext();
                // 从 spring.factries 中获取错误报告的类。出错的时候会调用其方法通知
                exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                                new Class[] { ConfigurableApplicationContext.class }, context);
                // 4. 准备刷新上下文
                prepareContext(context, environment, listeners, applicationArguments, printedBanner);
                // 5. 刷新上下文
                refreshContext(context);
                // 结束刷新,留待扩展功能,并未实现什么
                afterRefresh(context, applicationArguments);
                // 停止监听
                stopWatch.stop();
                if (this.logStartupInfo) {
                        new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
                }
                // 监听器发送启动结束时间
                listeners.started(context);
                // 调用 ApplicationRunner 和 CommandLineRunner 对应的方法
                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
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值