spring boot 中的spring mvc 和spring4.0之前的版本在启动的时候有些差异, 以前springMVC主要是通过web.xml中配置servlet
来完成spring MVC的启动。 但是在spring boot主要是通过DispatcherServletAutoConfiguration来完成初始化工作,下面可以主要来看一下
源码版本:spring-mvc 4.3.9
在类上声明了如下注解:
@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 , 这两个类里面完成了大部分的初始化工作
@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 |
看一下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; .........省略诸多代码 } |
该类主要作用是为了将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 |
通过上面的代码分析,可以非常清晰的看到,spring mvc主要是依靠DispatcherServletAutoConfiguration 这个类中的两个内部类进行初始化,
DispatcherServletConfiguration负责生成dispatcherServlet
DispatcherServletRegistrationConfiguration 负责将dispatcherServlet注册到系统里面的servlet中,使其生效
两者配合,最终完成初始化工作。
在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); } } } } |