springboot web配置
springboot web的配置有:
- SpringMvc配置的前缀为:
spring.mvc
- web场景的通用配置为:
spring.web
- 文件上传的配置为:
spring.servlet.multipart
- 服务器相关配置为:
server
接管SpringMVC 的三种方式
方式 | 用法 | 效果 | |
---|---|---|---|
全自动 | 直接编写控制器逻辑 | 全部使用自动配置默认效果 | |
手自一体 | @Configuration + 配置**WebMvcConfigurer** + 配置 WebMvcRegistrations | 不要标注 @**EnableWebMvc** | 保留自动配置效果 手动设置部分功能 定义MVC底层组件 |
全手动 | @Configuration + 配置**WebMvcConfigurer** | 标注 @**EnableWebMvc** | 禁用自动配置效果 全手动设置 |
WebMvcConfigurer 定义扩展SpringMVC底层功能
提供方法 | 核心参数 | 功能 | 默认 |
---|---|---|---|
addFormatters | FormatterRegistry | 格式化器:支持属性上@NumberFormat和@DatetimeFormat的数据类型转换 | GenericConversionService |
getValidator | 无 | 数据校验:校验 Controller 上使用@Valid标注的参数合法性。需要导入starter-validator | 无 |
addInterceptors | InterceptorRegistry | 拦截器:拦截收到的所有请求 | 无 |
configureContentNegotiation | ContentNegotiationConfigurer | 内容协商:支持多种数据格式返回。需要配合支持这种类型的HttpMessageConverter | 支持 json |
configureMessageConverters | List<HttpMessageConverter<?>> | 消息转换器:标注@ResponseBody的返回值会利用MessageConverter直接写出去 | 8 个,支持byte,string,multipart,resource,json |
addViewControllers | ViewControllerRegistry | 视图映射:直接将请求路径与物理视图映射。用于无 java 业务逻辑的直接视图页渲染 | 无 mvc:view-controller |
configureViewResolvers | ViewResolverRegistry | 视图解析器:逻辑视图转为物理视图 | ViewResolverComposite |
addResourceHandlers | ResourceHandlerRegistry | 静态资源处理:静态资源路径映射、缓存控制 | ResourceHandlerRegistry |
configureDefaultServletHandling | DefaultServletHandlerConfigurer | 默认 Servlet:可以覆盖 Tomcat 的DefaultServlet。让DispatcherServlet拦截/ | 无 |
configurePathMatch | PathMatchConfigurer | 路径匹配:自定义 URL 路径匹配。可以自动为所有路径加上指定前缀,比如 /api | 无 |
configureAsyncSupport | AsyncSupportConfigurer | 异步支持: | TaskExecutionAutoConfiguration |
addCorsMappings | CorsRegistry | 跨域: | 无 |
addArgumentResolvers | List | 参数解析器: | mvc 默认提供 |
addReturnValueHandlers | List | 返回值解析器: | mvc 默认提供 |
configureHandlerExceptionResolvers | List | 异常处理器: | 默认 3 个 ExceptionHandlerExceptionResolver ResponseStatusExceptionResolver DefaultHandlerExceptionResolver |
getMessageCodesResolver | 无 | 消息码解析器:国际化使用 | 无 |
@EnableWebMvc 禁用默认行为
-
@EnableWebMvc
给容器中导入DelegatingWebMvcConfiguration
组件,他是WebMvcConfigurationSupport
-
WebMvcAutoConfiguration
有一个核心的条件注解,@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
,容器中没有WebMvcConfigurationSupport
,WebMvcAutoConfiguration
才生效. -
@EnableWebMvc
导入WebMvcConfigurationSupport
导致WebMvcAutoConfiguration
失效。导致禁用了默认行为 -
WebMvcConfigurationSupport
- 这个类提供了很多默认配置
- 并且判断了系统中是否由相应的类,如果有就启用相应的功能
SpringBoot 默认配置好了 SpringMVC 的所有常用特性。
-
如果我们需要全面接管SpringMVC的所有配置并禁用默认配置,仅需要编写一个
WebMvcConfigurer
配置类,并标注@EnableWebMvc
即可 -
全手动模式
-
@EnableWebMvc
: 禁用默认配置WebMvcConfigurer
组件:定义MVC的底层行为
web场景的最佳实践:
-
导入web的stater, 默认就会使用springboot 的 web配置,包括:
- 静态资源处理
- 数据类型转换
- json类型的处理
- 国际化等
-
使用
@Configuration
编写一个配置类继承WebMvcAutoConfiguration
,可以自定义一些web的配置,springboot 默认的web配置也能同时生效 -
但是如果在配置类上加上
@EnableWebMvc
就会使springboot所有的默认web配置失效,全部使用自定义配置
静态资源
WebMvcAutoConfiguration
定义了静态资源的规则
-
向容器中放入了两个filter:
HiddenHttpMethodFilter
规定了页面表单的rest请求(get、post、put、delete)FormContentFilter
规定了表单内容- 因为在http规范里,只有get(数据放在url后)和post(数据放在请求体)请求可以携带数据,put、delete请求体的数据会被忽略
FormContentFilter
使put、delete请求体也能携带数据
-
向容器中放入了
WebMvcConfigurer
组件,提供了配置spring mvc的所有入口- 所有的功能会和
WebMvcProperties
和WebProperties
这两个类进行绑定 WebMvcProperties
配置的前缀为:spring.mvc
WebProperties
配置前缀为:spring.web
- 所有的功能会和
-
EnableWebMvcConfiguration
向容器中放入了WebMvcConfigurationSupport
组件- 如果我们向容器中放入这个组件,springboot静态资源的配置都不会生效
- 配置了
HandlerMapping
相关的配置,会根据请求路径访问对应的Handler
,包括:- 欢迎页:
WelcomePageHandlerMapping
会在四个静态文件夹下寻找index.html
,只要存在,项目启动访问ip端口号就会访问首页
- 欢迎页:
-
boot会在静态资源目录下找 favicon.ico 图标
WebMvcConfigurer
中定义了静态资源的配置
-
访问
/webjars/**
路径会去classpath:/META-INF/resources/webjars/
下找资源 -
访问
/**
路径会去静态资源默认的四个位置,所以只要静态资源放到下面四个文件夹中就能直接访问:classpath:/META-INF/resources/
classpath:/resources/
classpath:/static/
classpath:/public/
-
静态资源默认都有缓存设置:如果浏览器访问了一个静态资源,并且这个资源没有发生改变,下次访问的时候就直接访问浏览器缓存,不会在向服务器发起请求
- 关于缓存的配置,可以通过配置文件
spring.web
来配置 - CachePeriod:缓存的有效时间,以秒为单位,默认为0
- CacheControl:与HTTP有关
- UseLastModified:是否使用最后修改,需要配合HTTP缓存,默认关闭
- 关于缓存的配置,可以通过配置文件
为什么容器中放一个WebMvcConfigurer
就能配置底层行为
- WebMvcAutoConfiguration 是一个自动配置类,它里面有一个
EnableWebMvcConfiguration
EnableWebMvcConfiguration
继承与DelegatingWebMvcConfiguration
,这两个都生效DelegatingWebMvcConfiguration
利用 DI 把容器中 所有WebMvcConfigurer
注入进来- 别人调用
DelegatingWebMvcConfiguration
的方法配置底层规则,而它调用所有WebMvcConfigurer
的配置底层方法。
自定义静态资源
配置方式,参考对应配置类的属性
spring:
mvc:
# webjars 访问路径前缀
webjars-path-pattern: /webjars/**
# 静态资源通用的范文前缀
static-path-pattern: /**
web:
resources:
# 是否开启静态资源映射,默认就是开启
add-mappings: true
cache:
# 缓存的时间,单位秒
period: 60
cachecontrol:
# 缓存的时间,是缓存的精确配置,会覆盖上面的配置
max-age: 100
# 静态资源文件夹
static-locations: classpath:/META-INF/resources/,classpath:/resources/, classpath:/static/, classpath:/public/
代码方式
-
只要给容器中放入一个
WebMvcConfigurer
组件即可,再通过WebMvcConfigurer
组件去设置各种配置 -
可以实现WebMvcConfigurer
//EnableWebMvc 注解会禁用掉springboot的默认配置 //@EnableWebMvc //这是一个配置类 @Configuration public class WebMvcConfig implements WebMvcConfigurer { // 静态资源展示 @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { //注意文件路径最后的斜杠(文件分隔符),如果缺少了,就不能够正确的映射到相应的目录 //这里的意思是,所有带 /static 的请求,都会被路由到 file:H:/upload/ registry.addResourceHandler("/static/**"). addResourceLocations("file:H:/upload/"). //设置缓存 setCacheControl(CacheControl.maxAge(Duration.ofMillis(100))); } }
-
也可以使用@bean的方式
@Bean public WebMvcConfigurer setWebMvcConfigurer(){ return new WebMvcConfigurer() { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/static/**"). addResourceLocations("file:H:/upload/"). //设置缓存 setCacheControl(CacheControl.maxAge(Duration.ofMillis(100))); } }; }
路径匹配
Ant 风格的路径模式语法具有以下规则:
*
:表示任意数量的字符。?
:表示任意一个字符。**
:表示任意数量的目录。{}
:表示一个命名的模式占位符。[]
:表示字符集合,例如[a-z]表示小写字母。
例如:
*.html
匹配任意名称,扩展名为.html
的文件。/folder1/*/*.java
匹配在folder1
目录下的任意两级目录下的.java
文件。/folder2/**/*.jsp
匹配在folder2
目录下任意目录深度的.jsp
文件。/{type}/{id}.html
匹配任意文件名为{id}.html
,在任意命名的{type}
目录下的文件。
注意:Ant 风格的路径模式语法中的特殊字符需要转义,如:
- 要匹配文件路径中的星号,则需要转义为\*。
- 要匹配文件路径中的问号,则需要转义为\?。
Spring5.3 之后加入了更多的请求路径匹配的实现策略;
以前只支持 AntPathMatcher 策略, 现在提供了 PathPatternParser 策略。并且可以让我们指定到底使用那种策略。
AntPathMatcher 与 PathPatternParser
- PathPatternParser 在 jmh 基准测试下,有 6~8 倍吞吐量提升,降低 30%~40%空间分配率
- PathPatternParser 兼容 AntPathMatcher语法,并支持更多类型的路径模式
- PathPatternParser “**” 多段匹配的支持仅允许在模式末尾使用
- sprngboot 默认使用 PathPatternParser路径匹配规则,但是如果路径中间需要有 **,需要要替换成ant风格路径
内容协商
如果想要一套系统返回多种数据格式,就需要多端内容适配
SpringBoot 多端内容适配 ,默认规则:
-
基于请求头内容协商(默认开启):
- 客户端向服务端发送请求,携带HTTP标准的Accept请求头。
- 例如Accept:
application/json
、text/xml
、text/yaml
- 服务端根据客户端请求头期望的数据类型进行动态返回
-
基于请求参数内容协商(需要开启):
-
例如发送请求 GET /projects/spring-boot?format=json
-
匹配到 @GetMapping(“/projects/spring-boot”)
-
根据参数协商,优先返回 json 类型数据【需要开启参数匹配设置】
-
发送请求 GET /projects/spring-boot?format=xml,优先返回 xml 类型数据
# 开启基于请求参数的内容协商功能。 默认参数名:format。 默认此功能不开启 spring.mvc.contentnegotiation.favor-parameter=true # 指定内容协商时使用的参数名。默认是 format,可以改成自定义的值,例如 type spring.mvc.contentnegotiation.parameter-name=format
-
HttpMessageConverter
@ResponseBody
注解
- 如果controller类上标注了
@RestController
,因为@RestController
包含了@ResponseBody
,所以相当于controller所有方法的返回值标注了@ResponseBody
注解 - 所有请求进来,都先来到
DispatcherServlet
的doDispatch()
进行处理,doDispatch()
会找到一个HandlerAdapter
适配器,利用适配器执行目标方法 RequestMappingHandlerAdapter
来执行,调用invokeHandlerMethod()
来执行目标方法- 目标方法执行之前,需要准备好:
HandlerMethodArgumentResolver
:参数解析器,确定目标方法每个参数值HandlerMethodReturnValueHandler
:返回值处理器,确定目标方法的返回值改怎么处理
- 由
RequestMappingHandlerAdapter
里面的invokeAndHandle()
真正执行目标方法 - 目标方法执行完成,会返回返回值对象
- 得到返回值对象后,需要找到一个合适的返回值处理器:
HandlerMethodReturnValueHandler
- 最终找到
RequestResponseBodyMethodProcessor
能处理 标注了@ResponseBody
注解的方法 RequestResponseBodyMethodProcessor
调用writeWithMessageConverters
,利用MessageConverter
把返回值写出去
所以由@ResponseBody
注解标注的方法,返回值是由MessageConverter
处理的
- 写出返回结果时,会遍历容器种所有的
MessageConverter
,然后根据请求头中的Accept
找到适配的MessageConverter
- 所以如果需要输出某种格式,就需要导入对应的
HttpMessageConverter
WebMvcAutoConfiguration
提供几种默认HttpMessageConverters
-
EnableWebMvcConfiguration
通过addDefaultHttpMessageConverters
添加了默认的MessageConverter
;如下: -
ByteArrayHttpMessageConverter
: 支持字节数据读写StringHttpMessageConverter
: 支持字符串读写ResourceHttpMessageConverter
:支持资源读写ResourceRegionHttpMessageConverter
: 支持分区资源写出AllEncompassingFormHttpMessageConverter
:支持表单xml/json读写MappingJackson2HttpMessageConverter
: 支持请求响应体Json读写
-
springboot默认提供的
MessageConverter
功能有限,只能json和普通的返回,如果需要额外的返回类型,就需要导入其他的HttpMessageConverter
示例
适配xml:
如果想要返回 xml 数据,需要先导入依赖,然后在返回的实体上加上@JacksonXmlRootElement
注解:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
请求头Accept为:application/xml
就能返回xml
请求头Accept为:application/json
就能返回 json
自定义返回格式
以适配yaml为例:
导入依赖:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
</dependency>
编写配置
#告知系统增加了一种新的返回格式
spring.mvc.contentnegotiation.media-types.yaml=text/yaml
需要自定义一个HttpMessageConverters
/**
* 把对象写为yaml的组件,这里的泛型代表支持的格式
* AbstractHttpMessageConverter是HttpMessageConverter的默认实现
*/
@Component
public class YamlMessageConverter extends AbstractHttpMessageConverter<Object> {
//设置yaml工厂,可以把对象转为yaml
private ObjectMapper objectMapper ;
public YamlMessageConverter() {
//支持的类型,对应 spring.mvc.contentnegotiation.media-types.yaml=text/yaml 的类型
//告诉springboot这个MessageConverter支持的类型
super(new MediaType("type","yaml"));
this.objectMapper = new ObjectMapper(new YAMLFactory());;
}
/**
* 是否支持把某种类型写出
*
* @param clazz
* @return
*/
@Override
protected boolean supports(Class clazz) {
//这里可以增加条件判断,直接返回true就是只要是对象类型都支持
return true;
}
/**
* 配合 @RequestBody 注解,格式化请求参数
*
* @param clazz
* @param inputMessage
*/
@Override
protected Object readInternal(Class clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return null;
}
/**
* 配合 `@ResponseBody` 写出返回结果
*
* @param o 需要写出的对象,也就是方法的返回值
* @param outputMessage
*/
@Override
protected void writeInternal(Object o, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
//自动关流
try (OutputStream outputStream = outputMessage.getBody()){
//写出返回结果
this.objectMapper.writeValue(outputStream,o);
}
}
}
把自定义的HttpMessageConverters
添加到容器,WebMvcConfigurer
中可以添加HttpMessageConverter
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
//配置yaml的消息转换器:HttpMessageConverter
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new YamlMessageConverter());
}
}
国际化
国际化的自动配置参照MessageSourceAutoConfiguration
实现步骤:
- Spring Boot 在类路径根下查找messages资源绑定文件。文件名为:messages.properties
- 多语言可以定义多个消息文件,命名为messages_区域代码.properties。如:
- amessages.properties:默认
- bmessages_zh_CN.properties:中文环境
- cmessages_en_US.properties:英语环境
- 在程序中可以自动注入 MessageSource组件,获取国际化的配置项值
- 在页面中可以使用表达式 #{}获取国际化的配置项值
@Autowired //国际化取消息用的组件
MessageSource messageSource;
@GetMapping("/haha")
public String haha(HttpServletRequest request){
Locale locale = request.getLocale();
//利用代码的方式获取国际化配置文件中指定的配置项的值
String login = messageSource.getMessage("login", null, locale);
return login;
}
错误处理机制
错误处理的自动配置都在ErrorMvcAutoConfiguration
中,两大核心机制:
- SpringMVC的错误处理机制依然保留,MVC处理不了,才会交给boot进行处理
- SpringBoot 会自适应处理错误,响应页面或JSON数据
SpringBoot 会自适应处理错误,同样的错误,客户端和浏览器得到的数据格式是不一样的
springMvc异常处理方法:
-
@ExceptionHandler注解处理异常:注解的参数为能处理的异常类型,可以标识一个方法处理错误,默认只能处理这个类里发生的指定类型的错误
/** * 错误处理方法,ExceptionHandler注解的参数为能处理的异常类型 */ @ExceptionHandler(Exception.class) public Object error(Exception e) { return "网络异常,请稍后再试:"+e.getMessage()+"**********"; }
-
@ControllerAdvice: 标注一个类,代表这个类会集中处理所有 @Controller 发生的错误
/** * ControllerAdvice 标注一个类,代表这个类会集中处理所有 @Controller 发生的错误 */ @ControllerAdvice @Slf4j public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) public void error(Exception e, HttpServletResponse response) { log.info( "网络异常,请稍后再试:{}",e.getMessage()); response.setContentType("text/html;charset=utf-8"); PrintWriter writer = null; try { writer = response.getWriter(); } catch (IOException ex) { ex.printStackTrace(); } writer.write("网络异常,请稍后再试:"+e.getMessage()); writer.flush(); writer.close(); } }
-
如果全局和具体类都配置了异常处理,采用就近原则,以类里配置的优先
springBoot 错误处理:
-
如果发生错误以后,springMvc异常处理机制不生效,转发给springBoot来处理
-
springBoot会把错误转发到/error路径进行处理,并且在底层写好一个 BasicErrorController的组件,专门处理这个请求
-
既可以返回页面,也可以返回json
-
如果需要返回页面,需要解析出一个错误页(总结就是:先精确后模糊)
-
如果发生了500、404等错误
- 如果有模板引擎,默认会去
classpath:/templates/error/错误码.html
- 如果没有模板引擎,会在静态资源文件夹下找:
精确码.html
- 如果有模板引擎,默认会去
-
如果找不到精确的错误码,会通过
5xx
、4xx
模糊去找- 同上,有模板引擎的,去
classpath:/templates/error/5xx.html
下找,没有的静态资源文件夹下找
- 同上,有模板引擎的,去
-
如果依然找不到,会返回error视图
-
-
如果是返回json,会返回默认的JSON格式的错误信息
/**默认会读取server.error.path配置,如果每有配置就会读取默认值:error.path:/error */ @RequestMapping("${server.error.path:${error.path:/error}}")
-
配置文件
server:
error:
# 当发生错误以后,错误会转发给这个路径进行处理,所以可以自定义错误页面
path: /error
spring:
web:
resources:
#默认配置为 :classpath:/META-INF/resources/,classpath:/resources/, classpath:/static/, classpath:/public/
#如果想要访问到指定的错误页,还需要加上以下配置:
static-locations: classpath:/templates/
如果前后端不分离,需要服务端页面渲染,最佳实践:
-
对于不可预知的一些,HTTP码表示的服务器或客户端错误
- 给
classpath:/templates/error/
下面,放常用精确的错误码页面。500.html
,404.html
- 给
classpath:/templates/error/
下面,放通用模糊匹配的错误码页面。5xx.html
,4xx.html
- 给
-
如果发生业务错误
-
- 核心业务,每一种错误,都应该代码控制,跳转到自己定制的错误页。
- 通用业务,
classpath:/templates/error.html
页面,显示错误信息
嵌入式容器
springboot嵌入式容器
-
Servlet容器:用于管理、运行Servlet组件(Servlet、Filter、Listener)的环境,一般指服务器
-
springboot默认嵌入了tomcat服务器,无需自己搭建服务器环境
自动配置原理
- SpringBoot 默认嵌入Tomcat作为Servlet容器。
- 自动配置类是
ServletWebServerFactoryAutoConfiguration
,EmbeddedWebServerFactoryCustomizerAutoConfiguration
- 自动配置类开始分析功能。
xxxxAutoConfiguration
ServletWebServerFactoryAutoConfiguration
自动配置了嵌入式容器场景
- 绑定了
ServerProperties
配置类,所有和服务器有关的配置都以server
开头 ServletWebServerFactoryAutoConfiguration
导入了 嵌入式的三大服务器Tomcat
、Jetty
、Undertow
- 导入
Tomcat
、Jetty
、Undertow
都有条件注解,需要系统中有对应类才行(也就是导了包) - web场景默认导入了
Tomcat
的包,所以Tomcat
配置会生效,给容器中放入 TomcatServletWebServerFactory - 都给容器中
ServletWebServerFactory
放了一个 web服务器工厂(造web服务器的) - web服务器工厂的
getWebServer
方法可以获取web服务器,TomcatServletWebServerFactory 创建了 tomcat。
- 导入
源代码为:
@AutoConfiguration(after = SslAutoConfiguration.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
//绑定了ServerProperties配置类
@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 {}
TomcatServletWebServerFactory getWebServer
创建了 tomcat。
ServletWebServerFactory 什么时候会创建 webServer出来。
ServletWebServerApplicationContext
ioc容器,启动的时候会调用创建web服务器- Spring容器刷新(启动)的时候,会预留一个时机,刷新子容器。
onRefresh()
- refresh() 容器刷新 十二大步的刷新子容器会调用
onRefresh()
;
总结:
-
Web场景的Spring容器启动,在onRefresh的时候,会调用创建web服务器的方法。
-
Web服务器的创建是通过WebServerFactory搞定的。容器中又会根据导了什么包条件注解,启动相关的 服务器配置,默认
EmbeddedTomcat
会给容器中放一个TomcatServletWebServerFactory
,导致项目启动,自动创建出Tomcat。
用法:
- 修改
server
下的相关配置就可以修改服务器参数 - 通过给容器中放一个
**ServletWebServerFactory**
,来禁用掉SpringBoot默认放的服务器工厂,实现自定义嵌入任意服务器。 - 如果想切换服务器,可以在导入web场景的时候,禁用掉tomcat,导入其他服务器的包(实际意义不大)