SpringBoot深度探究(十)源码探究启动流程之注册DispatcherServlet

56 篇文章 3 订阅
12 篇文章 2 订阅

前言

上篇【SpringBoot深度探究(九)源码探究启动流程之三】说完了Tomcat启动以后,到目前为止SpringBoot的启动流程就剩下一个问题没有解决,那就是如何把DispatcherServlet和Spring环境联系起来的,那么本篇博客将会把这个问题做一个讲解。更多Spring内容进入【Spring解读系列目录】

DispatcherServletAutoConfiguration

和之前模拟的一样,首先SpringBoot在启动中new出来,然后再把DispatcherServlet注册到Spring容器中。做这一步事情的组件,就是这一段的标题DispatcherServletAutoConfiguration类初始化之后的结果,首先先看这个类上的注解。

DispatcherServletAutoConfiguration注解

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

首先@Configuration注解,说明这是一个配置类,会在Spring容器启动的时候被加载。然后@ConditionalOnWebApplication指定这是一个web项目。之后@ConditionalOnClass,如果发现classpath里面有DispatcherServlet就实例化出来一个bean,这里其实就是在关联Spring环境和DispatcherServlet。最后@AutoConfigureAfter注解说明此类会在ServletWebServerFactoryAutoConfiguration配置之后再进行。然后往下走:

DispatcherServlet Bean & MultipartResolver Bean

public class DispatcherServletAutoConfiguration {

   //The bean name for a DispatcherServlet that will be mapped to the root URL "/"
   public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
   
   //The bean name for a ServletRegistrationBean for the DispatcherServlet "/"
   public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";

   @Configuration(proxyBeanMethods = false)
   @Conditional(DefaultDispatcherServletCondition.class)
   @ConditionalOnClass(ServletRegistration.class)
   @EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
   protected static class DispatcherServletConfiguration {
      //这里DispatcherServlet被new出来,并且作为一个Bean加入到容器中去
      @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
      public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
         DispatcherServlet dispatcherServlet = new DispatcherServlet();
         dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
         dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
         dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
         dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
         dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.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;
      }
   }
   /**暂时略**/
}

进来以后可以发现大多数都是static字段标注的,就说明这些内部类会在最初DispatcherServletAutoConfiguration 初始化的时候就执行这些内容,也就是说DispatcherServlet就是在DispatcherServletConfiguration这个静态类块new出来的。这也是第一个静态内部类块,在里面将DispatcherServlet创建出来并且作为一个bean加入到了Spring容器当中。同时也创建了一个上传模块MultipartResolver 的bean,加入到了Spring容器中。

DispatcherServlet配置

public class DispatcherServletAutoConfiguration {
   protected static class DispatcherServletConfiguration {
	//见上方,略
   }
   @Configuration(proxyBeanMethods = false)
   @Conditional(DispatcherServletRegistrationCondition.class)
   @ConditionalOnClass(ServletRegistration.class)
   @EnableConfigurationProperties(WebMvcProperties.class)
   @Import(DispatcherServletConfiguration.class) //依赖上面的静态内部类  
protected static class DispatcherServletRegistrationConfiguration {
      //初始化是在这里做的,setName,setLoadOnStartup等等
      @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) {
         //把DispatcherServlet和Spring环境和关联起来
         DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
               webMvcProperties.getServlet().getPath());
         //设置名字,作用就是servletContext.addServlet("myBootServlet", servlet);
         registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
         //设置优先值,作用就是registration.setLoadOnStartup(1);
         registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
         multipartConfig.ifAvailable(registration::setMultipartConfig);
         return registration;
      }
   }
   //下面两个类是做验证用的,忽略掉
   @Order(Ordered.LOWEST_PRECEDENCE - 10)
   private static class DefaultDispatcherServletCondition extends SpringBootCondition { }
   @Order(Ordered.LOWEST_PRECEDENCE - 10)
   private static class DispatcherServletRegistrationCondition extends SpringBootCondition { }
}

首先看到@Import(DispatcherServletConfiguration.class),说明这个类在执行的时候会依赖上面的DispatcherServletConfiguration配置类。当这个配置类运行完了才会执行这里,因为都是静态的,类被载入即执行。在这里面dispatcherServlet被传入,并且进行初始化。过程是这样的:首先把dispatcherServlet添加给ServletRegistration.register()方法,而后又通过SPI技术调用onStartup()方法内调用register()方法完成环境绑定。这部分代码跳的太厉害就再贴代码了,为了方便理解画个图示:
在这里插入图片描述
后面就是DispatcherServlet的正常配置了,配置Servlet名字,设置设置优先值等等。

总结

到此一个SpringBoot的Web项目从最初的Spring容器启动,到Tomcat启动完毕关联相关的Servlet的流程,至于SpringBoot中那些Listener的作用,这里先挖个坑。笔者有空了一定填上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值