SpringBoot MVC 和静态资源

首先,我们一定要搞清楚,mvc 配置和 static 配置的联系和区别。 mvc 配置其实就是给 spring mvc 框架用的, 具体来说, 比如 @RequestMapping, 它会返回一个ModelAndView。 我们对这个ModelAndView进行渲染的时候, 需要查找 view, 具体怎么查找呢? 通过mvc 的各种配置。 当然,最终是交给了某个 viewResolver 进行解析。静态资源,其实也是经过了spring mvc,具体来说是ResourceHttpRequestHandler,但ResourceHttpRequestHandler 几乎不需要配置。spring mvc几乎没有提供静态资源的配置项(除了一个静态资源的映射),boot倒是提供了 关于位置和后缀的配置, 具体参见我的boot相关博客。

SpringBoot 中的MVC配置

boot 提供了很多 mvc 配置项,

# SPRING MVC (WebMvcProperties)
spring.mvc.async.request-timeout= # Amount of time (in milliseconds) before asynchronous request handling times out.
spring.mvc.date-format= # Date format to use. For instance `dd/MM/yyyy`.
spring.mvc.dispatch-trace-request=false # Dispatch TRACE requests to the FrameworkServlet doService method.
spring.mvc.dispatch-options-request=true # Dispatch OPTIONS requests to the FrameworkServlet doService method.
spring.mvc.favicon.enabled=true # Enable resolution of favicon.ico.
spring.mvc.formcontent.putfilter.enabled=true # Enable Spring's HttpPutFormContentFilter.
spring.mvc.ignore-default-model-on-redirect=true # If the content of the "default" model should be ignored during redirect scenarios.
spring.mvc.locale= # Locale to use. By default, this locale is overridden by the "Accept-Language" header.
spring.mvc.locale-resolver=accept-header # Define how the locale should be resolved.
spring.mvc.log-resolved-exception=false # Enable warn logging of exceptions resolved by a "HandlerExceptionResolver".
spring.mvc.media-types.*= # Maps file extensions to media types for content negotiation.
spring.mvc.message-codes-resolver-format= # Formatting strategy for message codes. For instance `PREFIX_ERROR_CODE`.
spring.mvc.servlet.load-on-startup=- # Load on startup priority of the Spring Web Services servlet.
spring.mvc.static-path-pattern=/** # Path pattern used for static resources.
spring.mvc.throw-exception-if-no-handler-found=false # If a "NoHandlerFoundException" should be thrown if no Handler was found to process a request.
spring.mvc.view.prefix= # Spring MVC view prefix.
spring.mvc.view.suffix= # Spring MVC view suffix.

最常用的大概是:

spring.mvc.view.prefix=/somePath/
spring.mvc.view.suffix=.html

这个两个都是对 viewResolver  进行配置用的。这两个值默认都是空, 为空的意思是说,@RequestMapping 返回的是什么, 那么boot 就直接查找那个view, 没有前缀和后缀 。这会产生一种奇怪的情况, 那就是, 比如映射是@RequestMapping("lk")  返回 "aa" 或者"/aa" ( 结果是一样的 ),  如果aa 文件不存在于跟目录,那么就返回 404; 如果存在,不管 aa 文件内容是什么, 由于浏览器不 识别器类型, 于是变成了下载。。

boot 在查找mvc  view 的时候, 是去静态资源目录去查找的,  也就是 spring.resources.static-locations 对应的目录。

如果我们直接反问静态资源, 不管是 spring.mvc.view.prefix 还是 spring.mvc.view.suffix , 还是 其他的 spring.mvc.view 开头的属性, 怎么配置都是没影响的。 但是@RequestMapping返回的view, 却又是在 静态目录进行查找的(非thymeleaf 等视图模板的情况, thymeleaf等是在templates下查找的, 而且 spring.mvc.view.prefix 及 spring.mvc.view.suffix 都是有效的。 这里应该要理清, 不然容易搞混。

再强调一遍, 访问静态资源, 一定要路径完全匹配, 否则就被 boot 认为是访问 动态资源, 比如 mvc 请求!

SpringBoot MVC 和JSP

如果我们希望使用 jsp 做动态资源的渲染, 那么, 我们应该这么配置:

spring.mvc.view.suffix=.jsp

但是呢, 这样做之后,还是不够的。 因为boot 内嵌的 tomcat容器默认不能解析 jsp, 关于 boot 中使用jsp,参见我的另外的博客。 一般来说,最好还是不要再boot 中使用jsp。谁用谁苦自己知道, 我当初就被坑了很久。 官方的说明是这样的:

If possible, JSPs should be avoided. There are several known limitations when using them with embedded servlet containers. —— 尽量不要使用jsp !!

为什么? 因为各种局限:

27.4.5 JSP Limitations
When running a Spring Boot application that uses an embedded servlet container (and is packaged as an executable archive), there are some limitations in the JSP support.

局限1 With Tomcat, it should work if you use war packaging. That is, an executable war works and is also deployable to a standard container (not limited to, but including Tomcat). An executable jar does not work because of a hard-coded file pattern in Tomcat.
局限2  With Jetty, it should work if you use war packaging. That is, an executable war works, and is also deployable to any standard container.
局限3 Undertow does not support JSPs.
局限4 Creating a custom error.jsp page does not override the default view for error handling. Custom error pages should be used instead.

There is a JSP sample so that you can see how to set things up

这么简单的英语我就不翻译了。其实官方也是有提供boot集成jsp示例的,jsp官方的示例是:https://github.com/spring-projects/spring-boot/tree/v2.0.0.M7/spring-boot-samples/spring-boot-sample-web-jsp。 我试过,确实可行(不废话吗,官方的能有啥毛病....) 但是尽管如此,还是很容易踩坑,一不小心就。。

SpringBoot MVC 自动配置是如何生效的?

boot 的mvc 功能大致是通过 WebMvcAutoConfiguration,以及DispatcherServletAutoConfiguration ,EmbeddedServletContainerAutoConfiguration 完成的。它做了很多工作。 多到, 很多时候, 出了问题, 我们搞不清, 只能通过源码来了解。

我们看一下大致有那些 自动配置:

配置了:

DispatcherServlet 这个当然是必须的,在传统项目中,我们可能通过web.xml 来配置,boot 是使用编程方式配置
MultipartResolver 上传解析器
ServletRegistrationBean 用来定制传统的Servlet,一般配合@Bean使用。
OrderedHttpPutFormContentFilter 对method 为put即patch 的form 请求的做一些参数处理
OrderedHiddenHttpMethodFilter 把post请求中_method 参数转换为 请求的method,
ResourceChainResourceHandlerRegistrationCustomizer 资源链,前端资嵌套引用时会用到
WelcomePage 首页,欢迎页面
ResourceResolver 静态资源
RequestMappingHandlerAdapter 
RequestMappingHandlerMapping
Validator
ConfigurableWebBindingInitializer 
ExceptionHandlerExceptionResolver
ContentNegotiationManager
InternalResourceViewResolver
BeanNameViewResolver
ContentNegotiatingViewResolver
LocaleResolver
Formatter<Date>
FaviconConfiguration
ErrorPagexxx

官方说明

它完成了很多的工作。引用 官方 27.1.1 Spring MVC Auto-configuration(https://docs.spring.io/spring-boot/docs/2.0.0.M7/reference/htmlsingle/#boot-features-spring-mvc-auto-configuration)的描述:

Spring Boot provides auto-configuration for Spring MVC that works well with most applications.

The auto-configuration adds the following features on top of Spring’s defaults:

Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
Support for serving static resources, including support for WebJars (covered later in this document).
Automatic registration of Converter, GenericConverter, and Formatter beans.
Support for HttpMessageConverters (see below).
Automatic registration of MessageCodesResolver (covered later in this document).
Static index.html support.
Custom Favicon support (covered later in this document).
Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).

If you want to keep Spring Boot MVC features and you want to add additional MVC configuration (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc. If you wish to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.

如果你想使用使用spring boot mvc 的特性的同时,想增加一些mvc 的配置,比如拦截器,格式化去,视图控制器等,你可以写一个类继承WebMvcConfigurer,注解 @Configuration,但不要有@EnableWebMvc注解。如果你只是想定制化RequestMappingHandlerMapping, RequestMappingHandlerAdapter, 或者ExceptionHandlerExceptionResolver,那么你只要定义一个WebMvcRegistrationsAdapter 实例就ok了

If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.

如果你想自己控制整个 Spring MVC,你可以写一个自己的配置类,同时注解上@Configuration 和 @EnableWebMvc。

关于OrderedHttpPutFormContentFilter , 大致是这样的:

protected void doFilterInternal(final HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if (("PUT".equals(request.getMethod()) || "PATCH".equals(request.getMethod())) && this.isFormContentType(request)) {
HttpInputMessage inputMessage = new ServletServerHttpRequest(request) {
public InputStream getBody() throws IOException {
return request.getInputStream();
}
};
MultiValueMap<String, String> formParameters = this.formConverter.read((Class)null, inputMessage);
if (!formParameters.isEmpty()) {
HttpServletRequest wrapper = new HttpPutFormContentFilter.HttpPutFormContentRequestWrapper(request, formParameters);
filterChain.doFilter(wrapper, response);
return;
}
} filterChain.doFilter(request, response);
}

也就是把put/patch请求的 body解析为请求参数,body 应该是这个格式: aa=11&bb=22; 使用 & 和= 来分隔。就跟我们的get 的querystring是一样的。然后使用 URLDecoder.decode 来解码。 不太懂这里的FormHttpMessageConverter 为什么这么设计,感觉怪怪的。

SpringBoot 之 MVC的更多相关文章

  1. spring boot学习(3) SpringBoot 之MVC 支持

    第一节:@RequestMapping 配置url 映射   第二节:@Controller 处理http 请求 转发到一个页面,以前是转发到jsp页面,现在使用freemarker: 在pom.xm ...

  2. 第一篇 Springboot + Web MVC + MyBatis + 简单UI + Thymeleaf实现

    源码链接:https://pan.baidu.com/s/1-LtF56dnCM277v5lILRM7g 提取码:c374 第二篇 Springboot mybatis generate根据数据库表自 ...

  3. SpringBoot + Spring MVC国际化使用示例

    项目中需要显示中英文两种语言,所以需要对显示的内容进行国际化,如下是一个示例程序. 程序文件结构,如下图,后面详细列出各文件的代码. 1. 编写maven的pom.xml文件,如下: <proj ...

  4. SpringBoot学习之mvc

    Spring Boot非常适合Web应用程序开发. 我们可以使用嵌入式Tomcat,Jetty或Undertow轻松创建自包含的HTTP服务器. 大多数Web应用程序将使用spring-boot-st ...

  5. SpringBoot微服务架构下的MVC模型总结

    SpringBoot微服务架构下的MVC模型产生的原因: 微服务概念改变着软件开发领域,传统的开源框架结构开发,由于其繁琐的配置流程 , 复杂的设置行为,为项目的开发增加了繁重的工作量,微服务致力于解 ...

  6. springboot mvc自动配置(目录)

    对于长时间基于spring框架做web开发的我们,springmvc几乎成为了开发普通web项目的标配.本系列文章基于快速启动的springboot,将从源码角度一点点了解springboot中mvc ...

  7. springboot使用之四:错误页面404处理建议

    每个项目可能都会遇到404,403,500等错误代码,如没有错误页面,则会给用户一个很不友好的界面,springboot项目同样也存在这个问题. 但在官方文档并没有相关配置信息,这就要求我们自己来实现 ...

  8. springboot学习(二)——springmvc配置使用

    以下内容,如有问题,烦请指出,谢谢 上一篇讲解了springboot的helloworld部分,这一篇开始讲解如何使用springboot进行实际的应用开发,基本上寻着spring应用的路子来讲,从s ...

  9. springboot学习(三)——http序列化/反序列化之HttpMessageConverter

    以下内容,如有问题,烦请指出,谢谢! 上一篇说掉了点内容,这里补上,那就是springmvc的http的序列化/反序列化,这里简单说下如何在springboot中使用这个功能. 使用过原生netty ...

随机推荐

  1. python 安装scrapy need vistual c++ 14.0 的正面解法

    为什么一堆教程里面,都是侧面的. 因为需要你自己去正面刚 正题: 这个问题要的是 build tools 人(控制台)说的很清楚了, 给的链接不是直接解决问题的链接(我安装了 vs_redis.exe ...

  2. C基础学习笔记

    1.C语言运算符优先级: 2.三种循环比较 while.do-while和for三种循环在具体的使用场合上是有区别的,如下: 1).在知道循环次数的情况下更适合使用for循环: 2).在不知道循环次数 ...

  3. 自动化测试-7.selenium定位一组对象

    前言 前面的几篇都是讲如何定位一个元素,有时候一个页面上有多个对象需要操作,如果一个个去定位的话,比较繁琐,这时候就可以定位一组对象. webdriver 提供了定位一组元素的方法,跟前面八种定位方式 ...

  4. video自动填充满父级元素

    想要video能自动填充慢父div的大小,只要给video标签加上style="width= 100%; height=100%; object-fit: fill"即可. obj ...

  5. 为什么要将Apache与Tomcat集成?(或不)

    Why should I integrate Apache with Tomcat? (or not) There are many reasons to integrate Tomcat with ...

  6. python学习之路03

    一.常量和变量 1.python中的数据类型 分类: ​ Number:数字型[整型,浮点型,复数] ​ String:字符串型 ​ Boolean:布尔型[True,False] ​ None:空值 ...

  7. css文本是否换行

    关于文本换行有三个属性: white-space word-break word-wrap white-space normal 默认.空白会被浏览器忽略 pre 空白会被浏览器保留.其行为方式类似 ...

  8. string函数的一些实现

    /************************************************************************* > File Name: test.cpp ...

  9. 装系统时 System clock uses UTC 问题

    装系统也装了至少不下50次了,每次都是傻瓜一样的按照第一印象在弄,从未想过为啥,装到这里的时候,System clock uses UTC 勾不勾呢,每次都是百度,然后装完这一次下一次又忘了,这是没有 ...

  10. [C#]typeof,Gettype()和is的区别

    typeof 参数是一个类型名称,比如你自己编写的一个类 GetType()是类的方法,继承自object,返回该实例的类型 is 用来检测实例的兼容性(是否可以相互转换) 例: class Anim ...