springboot mvc 启动源码解析(一)

前言

spring boot 中的spring mvc 和spring4.0之前的版本在启动的时候有些差异, 以前springMVC主要是通过web.xml中配置servlet

来完成spring MVC的启动。 但是在spring boot主要是通过DispatcherServletAutoConfiguration来完成初始化工作,下面可以主要来看一下

源码版本:spring-mvc 4.3.9

1.DispatcherServletAutoConfiguration

DispatcherServletAutoConfiguration

在类上声明了如下注解:

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(EmbeddedServletContainerAutoConfiguration.class)

  1. @AutoConfigureOrder 自动化配置加载的顺序

  2. @Configuration 表明这是一个配置类 , 交给spring容器管理

  3. @ConditionalOnWebApplication   表示只在web环境才加载这个类

  4. @ConditionalOnClass(DispatcherServlet.class)     表示只有在DispatcherServlet存在的情况才会记在这个类,可见后面主要就是通过这个servlet来完成

spring MVC的主要工作的

  5. @AutoConfigureAfter(EmbeddedServletContainerAutoConfiguration.class)  在这个类之后加载

在这个类中有两个静态内部类DispatcherServletConfiguration,  DispatcherServletRegistrationConfiguration  , 这两个类里面完成了大部分的初始化工作

DispatcherServletConfiguration

@Configuration   // 表明是一个配置类
@Conditional(DefaultDispatcherServletCondition.class)    // 通过该类来判断是否加载这个bean
@ConditionalOnClass(ServletRegistration.class)			 // ServletRegistration存在的情况下才进行加载
@EnableConfigurationProperties(WebMvcProperties.class)	 // 加载MVCProperties的配置类
protected static class DispatcherServletConfiguration {

   private final WebMvcProperties webMvcProperties;

   public DispatcherServletConfiguration(WebMvcProperties webMvcProperties) {
      this.webMvcProperties = webMvcProperties;
   }
	// DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = dispatcherServlet 
   @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
   public DispatcherServlet dispatcherServlet() {
      // 初始化DispatcherServlet
      DispatcherServlet dispatcherServlet = new DispatcherServlet();
      // 设置是否允许分发Option请求,默认为true
      dispatcherServlet.setDispatchOptionsRequest(this.webMvcProperties.isDispatchOptionsRequest());
      dispatcherServlet.setDispatchTraceRequest( this.webMvcProperties.isDispatchTraceRequest());
      dispatcherServlet.setThrowExceptionIfNoHandlerFound(this.webMvcProperties.isThrowExceptionIfNoHandlerFound());

      return dispatcherServlet;
   }
	// 初始化上传文件的解析器
   @Bean
   @ConditionalOnBean(MultipartResolver.class)
   @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)  // 当容器中不存在multipartResolver 这个bean的时候,就初始化这个bean
   public MultipartResolver multipartResolver(MultipartResolver resolver) {
      // Detect if the user has created a MultipartResolver but named it incorrectly
      return resolver;
   }

}

看一下DefaultDispatcherServletCondition 这个类, 这个类主要是一些逻辑判断,判断容器中是否存在dispatcherServlet,

防止重复生成

@Order(Ordered.LOWEST_PRECEDENCE - 10)
private static class DefaultDispatcherServletCondition extends SpringBootCondition {

   @Override
   public ConditionOutcome getMatchOutcome(ConditionContext context,
         AnnotatedTypeMetadata metadata) {
      ConditionMessage.Builder message = ConditionMessage
            .forCondition("Default DispatcherServlet");
      ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
      List dispatchServletBeans = Arrays.asList(beanFactory
            .getBeanNamesForType(DispatcherServlet.class, false, false));
	 //如果beanFactory 中存在id 为 dispatcherServlet,类型为DispatcherServlet的bean ,返回不匹配.否则进入下一步
      if (dispatchServletBeans.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
         return ConditionOutcome.noMatch(message.found("dispatcher servlet bean")
               .items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
      }
     // 如果beanFactory中包含id为dispatcherServlet的bean,返回不匹配
      if (beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
         return ConditionOutcome
               .noMatch(message.found("non dispatcher servlet bean")
                     .items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
      }
     //  如果beanFactory中不包含类型为DispatcherServlet的bean,返回匹配
      if (dispatchServletBeans.isEmpty()) {
         return ConditionOutcome
               .match(message.didNotFind("dispatcher servlet beans").atAll());
      }
     //  其他情况下,默认返回匹配
      return ConditionOutcome.match(message
            .found("dispatcher servlet bean", "dispatcher servlet beans")
            .items(Style.QUOTE, dispatchServletBeans)
            .append("and none is named " + DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
   }

}


看一下WebMvcProperties这个类,这个类主要是读取配置文件,关键点就是在于类头上的一个注解,他就是为了读取配置文件中 “spring.mvc”开头的配置


@ConfigurationProperties(prefix = "spring.mvc")
public class WebMvcProperties {

   /**
    * Formatting strategy for message codes (PREFIX_ERROR_CODE, POSTFIX_ERROR_CODE).
    */
   private DefaultMessageCodesResolver.Format messageCodesResolverFormat;

   /**
    * Locale to use. By default, this locale is overridden by the "Accept-Language"
    * header.
    */
   private Locale locale;

   /**
    * Define how the locale should be resolved.
    */
   private LocaleResolver localeResolver = LocaleResolver.ACCEPT_HEADER;

   /**
    * Date format to use (e.g. dd/MM/yyyy).
    */
   private String dateFormat;

   /**
    * Dispatch TRACE requests to the FrameworkServlet doService method.
    */
   private boolean dispatchTraceRequest = false;

   /**
    * Dispatch OPTIONS requests to the FrameworkServlet doService method.
    */
   private boolean dispatchOptionsRequest = true;

   /**
    * If the content of the "default" model should be ignored during redirect scenarios.
    */
   private boolean ignoreDefaultModelOnRedirect = true;

   .........省略诸多代码
}

DispatcherServletRegistrationConfiguration

该类主要作用是为了将dispatcherServlet 注册到系统(servlet)里面去。

@Configuration     // 表明为一个配置类
@Conditional(DispatcherServletRegistrationCondition.class)   // 判断容器里面是否存在dispatcherServlet这个类,必须在存在的情况下才会创建这个bean
@ConditionalOnClass(ServletRegistration.class)               // 在ServletRegistration 这个类存在的情况进行加载
@EnableConfigurationProperties(WebMvcProperties.class)       //属性文件配置类
@Import(DispatcherServletConfiguration.class)                // 导入DispatcherServletConfiguration
protected static class DispatcherServletRegistrationConfiguration {

   private final ServerProperties serverProperties;       // 服务器配置 ,读取server.x这种配置

   private final WebMvcProperties webMvcProperties;		  // mvc配置

   private final MultipartConfigElement multipartConfig;    

   public DispatcherServletRegistrationConfiguration(
         ServerProperties serverProperties, WebMvcProperties webMvcProperties,
         ObjectProvider multipartConfigProvider) {
      this.serverProperties = serverProperties;
      this.webMvcProperties = webMvcProperties;
      this.multipartConfig = multipartConfigProvider.getIfAvailable();
   }

   @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
   @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
   public ServletRegistrationBean dispatcherServletRegistration(
         DispatcherServlet dispatcherServlet) {
    // 将dispatcherServlet通过ServletRegistrationBean 注册为servlet,如此该servlet才会生效
      ServletRegistrationBean registration = new ServletRegistrationBean(
            dispatcherServlet, this.serverProperties.getServletMapping());
      registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
     // 加载优先级 , getLoadOnStartup() 默认值为-1 
      registration.setLoadOnStartup(
            this.webMvcProperties.getServlet().getLoadOnStartup());
      if (this.multipartConfig != null) {
         registration.setMultipartConfig(this.multipartConfig);
      }
      return registration;
   }

}


通过上面的代码分析,可以非常清晰的看到,spring mvc主要是依靠DispatcherServletAutoConfiguration 这个类中的两个内部类进行初始化,

DispatcherServletConfiguration负责生成dispatcherServlet

DispatcherServletRegistrationConfiguration 负责将dispatcherServlet注册到系统里面的servlet中,使其生效

两者配合,最终完成初始化工作。

2.DispatcherServlet

在spring创建完applicationContext后,会调用该类中的initStrategies方法 , 用于初始化spring mvc相关配置

protected void initStrategies(ApplicationContext context) {
	initMultipartResolver(context);//文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析
	initLocaleResolver(context);//本地化解析
	initThemeResolver(context);//主题解析
	initHandlerMappings(context);//通过HandlerMapping,将请求映射到处理器
	initHandlerAdapters(context);//通过HandlerAdapter支持多种类型的处理器, 将请求与映射到的方法做一个适配
	initHandlerExceptionResolvers(context);//如果执行过程中遇到异常,将交给HandlerExceptionResolver来解析
	initRequestToViewNameTranslator(context);//直接解析请求到视图名
	initViewResolvers(context);//通过viewResolver解析逻辑视图到具体视图实现
	initFlashMapManager(context);//flash映射管理器
}


下面主要看一下doDispatch这个方法,所有的请求,都会集中到这里来进行分发处理


protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   HttpServletRequest processedRequest = request;
   HandlerExecutionChain mappedHandler = null;
   boolean multipartRequestParsed = false;

   WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

   try {
      ModelAndView mv = null;
      Exception dispatchException = null;

      try {
        // 判断是否是上传文件
         processedRequest = checkMultipart(request);
         multipartRequestParsed = (processedRequest != request);

         // Determine handler for the current request.
         // 获取方法执行的handler , 此处主要是通过handleMappings ,然后gethandle
		 //	通过请求地址,从mappingRegistry通过请求路径获取到对应的handlerMethod
		 // 之后获取拦截器,获取拦截器的执行列表。
         // 后面会重点讲这个方法
         mappedHandler = getHandler(processedRequest);
         if (mappedHandler == null || mappedHandler.getHandler() == null) {
            noHandlerFound(processedRequest, response);
            return;
         }

         // 获取请求适配器,用于适配request和HandlerMethod最终执行的信息适配
         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

         // Process last-modified header, if supported by the handler.
         String method = request.getMethod();
         
         boolean isGet = "GET".equals(method);
         if (isGet || "HEAD".equals(method)) {
            long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
            if (logger.isDebugEnabled()) {
               logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
            }
            if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
               return;
            }
         }
         //执行拦截器列表里面的preHandle方法,如果有一个执行失败,那么则return ,这也是为什么prHandle方法可以阻止请求的执行
         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }

         // Actually invoke the handler.  执行方法
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

         if (asyncManager.isConcurrentHandlingStarted()) {
            return;
         }

         applyDefaultViewName(processedRequest, mv);
         // 执行拦截器
         mappedHandler.applyPostHandle(processedRequest, response, mv);
      }
      catch (Exception ex) { //上述调用的过程中如果发生异常
         dispatchException = ex;
      }
      catch (Throwable err) {  //上述调用的过程中如果发生异常
         // As of 4.3, we're processing Errors thrown from handler methods as well,
         // making them available for @ExceptionHandler methods and other scenarios.
         dispatchException = new NestedServletException("Handler dispatch failed", err);
      }
       // 由ViewResolver组件根据ModelAndView对象得到实际的View
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
   }
   catch (Exception ex) {
      // 发生异常,调用拦截器的AfterCompletion 方法
      triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
   }
   catch (Throwable err) {
      // 发生异常,调用拦截器的AfterCompletion 方法
      triggerAfterCompletion(processedRequest, response, mappedHandler,
            new NestedServletException("Handler processing failed", err));
   }
   finally {
      if (asyncManager.isConcurrentHandlingStarted()) {
         // Instead of postHandle and afterCompletion
         if (mappedHandler != null) {
            mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
         }
      }
      else {
         // Clean up any resources used by a multipart request.
         if (multipartRequestParsed) {
            cleanupMultipart(processedRequest);
         }
      }
   }
}



springboot mvc 启动源码解析(一)_第1张图片
















你可能感兴趣的:(springboot mvc 启动源码解析(一))