对Spring MVC的默认行为进行变动,或者进行扩展,一般要经过WebMvcConfigurer
进行配置。它定义了不少课供重写的方法,经过对方法进行重写,就能够实现咱们想要的效果。html
在Spring Boot 2.x以前,WebMvcConfigurerAdapter
是WebMvcConfigurer
的实现抽象类,能够经过继承WebMvcConfigurerAdapter
重写它的方法进行配置。到SpringBoot2.x时代的时候, 被标记为了@Deprecated
,经过实现WebMvcConfigurer
接口,经过重写默认方法来进行配置。整体来讲,这两种配置方式基本相同。java
@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(sleuthInterceptor); registry.addInterceptor(idempotencyOperationInterceptor) // 拦截全部请求 .addPathPatterns("/**") // 排除掉查询类的 POST 请求 .excludePathPatterns("/**/search/**","xx"); }
此方法用来专门注册拦截器(Interceptor),经过registry#addInterceptor
进行拦截器的注册,拦截器必须是HandlerInterceptor
的子类,在下面在 《对SpringMVC进行扩展 》会进行详细的说明。web
@Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { resolvers.add(new PropertiesHandlerMethodArgumentResolver()); }
此方法能够用来添加参数解析器(argumentResolver),经过resolvers#add
进行添加参数解析器。注意,此处添加的Resolver
优先级会低于系统内建的Resolver
,若是想添加优先级高于内建的Resolver
,能够经过requestMappingHandlerAdapter#setArgumentResolvers
方法进行覆盖,在下面在 《对SpringMVC进行扩展 》会进行详细的说明。spring
@Override public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) { }
这个是用来配置,注意,这个配置和 addArgumentResolvers
添加参数解析器 的配置相似,添加的自定义Handler
优先级会低于系统内建的Handler
,若是想添加优先级高于内建的Handler
,须要经过requestMappingHandlerAdapter#setReturnValueHandlers
方法进行覆盖,在下面在《对SpringMVC进行扩展 》会进行详细的说明。json
@Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter(); ObjectMapper objectMapper = jsonConverter.getObjectMapper(); // 解决 18位 Long 类型转换成 JSON 时形成的数据丢失 SimpleModule simpleModule = new SimpleModule(); simpleModule.addSerializer(Long.class, ToStringSerializer.instance); simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance); objectMapper.registerModule(simpleModule); converters.add(jsonConverter); }
消息转换器能够对接收或返回的消息进行转换,好比解决中文乱码、json中Long精度丢失等,咱们可使用系统提供的消息转换器,或咱们自定义转换器,经过这个方法converters#add
进行注册使用,会把这里添加的converter
依次放在最高优先级(List的头部)。有多个自定义的converter
时,能够改变相互之间的顺序,可是都在内置的converter
前面。后端
这个配置的使用场景比较常见,在下面在《对SpringMVC进行扩展 》会进行详细的说明。跨域
@Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter(); ObjectMapper objectMapper = jsonConverter.getObjectMapper(); // 解决 18位 Long 类型转换成 JSON 时形成的数据丢失 SimpleModule simpleModule = new SimpleModule(); simpleModule.addSerializer(Long.class, ToStringSerializer.instance); simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance); objectMapper.registerModule(simpleModule); converters.add(jsonConverter);riverroad }
这个与上个 configureMessageConverters
相似,不一样点在于这个方法在 configureMessageConverters
以后运行,这时系统内置的converter
已经添加完毕,此时咱们一样能够能够经过改变converters
列表中的converter
实现处理顺序的变动。数组
@Override public void addFormatters(FormatterRegistry registry) { }
类型转换器和格式化器,部分功能和消息转换器类似。不一样它的源类型必须是一个String
, 目标类型是java类型。在下面在《对SpringMVC进行扩展 》 会进行说明。缓存
@Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowCredentials(true) .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .maxAge(3600); }
这个是关于跨域问题的设置app
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { //将请求映射到指定的位置 registry.addResourceHandler("swagger-ui.html") .addResourceLocations("classpath:/META-INF/resources/"); }
能够设置静态资源的映射,这个在目前开发中用的很少。
@Override public void configurePathMatch(PathMatchConfigurer configurer) { // 表示不区分URL的最后一个字符是不是斜杠/ configurer.setUseSuffixPatternMatch(true); }
让开发人员能够根据需求定制URL路径的匹配规则,经常使用的setUseSuffixPatternMatch
方法,用来设置使用后缀模式匹配的方式,好比设置为true的话(默认为true),URL后面加个斜杠并不影响路径访问,例如“/user”等同于“/user/。若是须要定制path匹配发生的过程,能够提供本身定制的PathMatcher
和UrlPathHelper
,可是这种需求不常见。
@Override public void configureViewResolvers(ViewResolverRegistry registry) { InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver(); //请求视图文件的前缀地址 internalResourceViewResolver.setPrefix("/WEB-INF/jsp/"); //请求视图文件的后缀 internalResourceViewResolver.setSuffix(".jsp"); registry.viewResolver(internalResourceViewResolver); }
这个对咱们来讲很熟悉,配置html、Jsp页面视图时就会用到InternalResourceViewResolver
配置类,而后设置preffix
、suffix
参数进行配置视图文件路径前缀与后缀,不过如今目前开发中开发所有转向了先后端分离方式,这个已经配置几乎不会在遇到了。
Spring MVC提供众多的经常使用的功能,基本上能知足咱们平常的使用,但有时候咱们会有特殊的需求,而Spring MVC没有默认的支持,此时就是须要咱们对它进行扩展,下面就对Spring MVC常见的扩展方式进下介绍说明。
HandlerMapping
(处理请求映射)
处理请求的映射。保存请求URL到具体的方法的映射关系,咱们能够编写任意的HandlerMapping
实现类,依据任何策略来决定一个web请求到 HandlerExecutionChain
对象的生成。一般咱们不须要对他进行扩展。
HandlerAdapter
(处理适配器)
真正调用Controller的地方,其实就是适配各类Controller。HandlerAdapter就是你能够提供本身的实现类来处理handler对象,咱们通常不会对他进行扩展。
HandlerInterceptor
(接口拦截器)
经过自定义拦截器,咱们能够在一个请求被真正处理以前、请求被处理但还没输出到响应中、请求已经被输出到响应中以后这三个时间点去作任何咱们想要作的事情,这个是咱们在Spring MVC中用到最多的一种扩展方式。
HandlerMethodArgumentResolver
(处理方法参数解释器)
接收到请求参数的时候,会经过它的不一样实现类对参数进行处理,经过对它进行扩展,能让咱们实现对参数进行自定义操做,以前超哥写过一个自定注入Header参数到接收类的参数解释器,这个是咱们对Spring MVC经常使用的扩展方式之一。
HandlerMethodReturnValueHandler
(处理方法返回值处理器)
程序方法运行结束后的返回值进行处理,转换成咱们所须要的格式写入返回请求。
Converter
(类型转换器)
对数据类型进行转换,主要是用到的是 HttpMessageConverter
( http消息转换器),用来对请求和响应的数据进行处理。
Formatter
(格式化器)
对接收到的参数进行处理和格式化,只能应用于输入为String类型的数据。
ViewResolver
(视图解析器)
完成从ModelAndView
到真正的视图的过程,ViewResolver
接口是在DispatcherServlet
中进行调用的,当DispatcherServlet
调用完Controller后,会获得一个ModelAndView
对象,而后DispatcherServlet
会调用render方法进行视图渲染。在目前先后端分离的状况下,这个咱们通常不会进行扩展。
HandlerExceptionResolver
(异常处理)
用不到,略
HandlerInterceptor
是一个接口,用过实现这个接口,咱们能够实现出一个自定义的拦截器,这个接口有三个方法,以下图所示:
public interface HandlerInterceptor { //在业务处理器处理请求以前被调用,其返回值表示是否中断后续操做 boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{ } // 在业务处理器处理请求完成以后,生成视图以前执行 void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception{ } // 在DispatcherServlet彻底处理完请求以后被调用,可用于清理资源,日志打印 void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception{ } }
最后将拦截器经过WebMvcConfigurer
配置类中的 addInterceptors
方法进行注册,便可生效。
处理方法参数解释器能够对方法参数进行处理,经过它能够获取该方法参数上的一些信息 ,如方法参数中的注解信息等,根据获取到的信息对参数数据进行处理。经过实现这个接口或继承它的实现类,就能够实现一个自定义的处理方法参数解释器,而后将Resolver
经过WebMvcConfigurer
配置类中的 addArgumentResolvers
方法进行注册,便可生效。
package org.springframework.web.method.support; public interface HandlerMethodArgumentResolver { //判断是否使用这个组件 boolean supportsParameter(MethodParameter parameter); //对参数进行处理 Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception; }
在Spring MVC中,系统已经提供了多种不一样用处的 处理方法参数解释器 ,当有请求到来时,系统首先会从系统提供的解释器中寻找合适的Resolver
,若是匹配到,才会查找注册的自定义实现Resolver
,因此一般咱们要建立自定义注解放在要处理的参数上,方便使用自定义的Resolver
进行处理。
选择 Resolver
进行处理的流程:
// class:HandlerMethodArgumentResolverComposite private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) { //先从缓存中查找 HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter); // 未找到默认的Resolver if (result == null) { //遍历Resolver for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) { //判断是否支持此Resolver if (methodArgumentResolver.supportsParameter(parameter)) { result = methodArgumentResolver; //写入缓存 this.argumentResolverCache.put(parameter, result); break; } } } return result; }
若是咱们确实须要配置高于系统内置的自定义Resolver
的时候,能够经过以下的方式进行配置:
在WebConfig
配置类中,经过@PostConstruct
注解在一个方法上,可让这个方法在Bean依赖注入完成后被自动调用,而后在这个方法里对 Resolver
集合进行从新设置,就能够实现将自定义 Resolver
优先级提高到内置的以前了。
@Configuration public class RestWebMvcConfigurer implements WebMvcConfigurer { @Autowired private RequestMappingHandlerAdapter requestMappingHandlerAdapter; @PostConstruct public void init() { // 获取当前 RequestMappingHandlerAdapter 全部的 Resolver 对象 List<HandlerMethodArgumentResolver> resolvers = requestMappingHandlerAdapter.getArgumentResolvers(); List<HandlerMethodArgumentResolver> newResolvers = new ArrayList<>(resolvers.size() + 1); // 添加 PropertiesHandlerMethodArgumentResolver 到集合首位 newResolvers.add(new PropertiesHandlerMethodArgumentResolver()); // 添加 已注册的 Resolver 对象集合 newResolvers.addAll(resolvers); // 从新设置 Resolver 对象集合 requestMappingHandlerAdapter.setArgumentResolvers(newResolvers); } }
题外话:能够经过实现自定义自动把Tid一类信息注入到参数里,下面用一个简单的demo进行展现:
按需把请求头参数写入方法参数的Spring MVC扩展处理器
HandlerMethodReturnValueHandler
能够用来对程序方法运行结束后的返回值进行处理,转换成咱们所须要的格式并返回。
package org.springframework.web.method.support; public interface HandlerMethodReturnValueHandler { //检验是否支持本处理器处理 boolean supportsReturnType(MethodParameter returnType); //具体处理方法 void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception; }
与HandlerMethodArgumentResolver
同样,系统已经提供了多种不一样的 Handler
了,自定义的Handler
添加进去优先级都会在内置Handler
以后。常规配置的方式经过 WebConfig
进行配置,若是要配置高于系统内置的自定义 Handler
时,能够参照下方的配置方式,与参数解释器的配置方式基本相同。
@Configuration public class RestWebMvcConfigurer implements WebMvcConfigurer { @Autowired private RequestMappingHandlerAdapter requestMappingHandlerAdapter; @PostConstruct public void init() { // 获取当前 HandlerMethodReturnValueHandler 全部的 Handler 对象 List<HandlerMethodReturnValueHandler> handlers = requestMappingHandlerAdapter.getReturnValueHandlers(); List<HandlerMethodReturnValueHandler> newHandlers = new ArrayList<>(handlers.size() + 1); // 添加 PropertiesHandlerMethodReturnValueHandler 到集合首位 newHandlers.add(new PropertiesHandlerMethodReturnValueHandler()); // 添加 已注册的 Handler 对象集合 newHandlers.addAll(handlers); // 从新设置 Handler 对象集合 requestMappingHandlerAdapter.setReturnValueHandlers(newHandlers); } }
选择 Handler
进行处理的流程:
// class: HandlerMethodReturnValueHandlerComposite private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) { //遍历Handler for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) { //找到并返回 if (handler.supportsReturnType(returnType)) { return handler; } } return null; }
HttpMessageConveter
是用来处理请求和响应数据的,咱们常常会用到@RequestBody
和@ResponseBody
,经过这两个注解,能够在 Controller 中直接使用 Java 对象做为请求参数和返回内容,而完成这之间转换做用的即是HttpMessageConverter
。Spring 为咱们内置了大量的HttpMessageConverter
,例如, MappingJackson2HttpMessageConverter
、StringHttpMessageConverter
等。
已包含经常使用的消息转换器:
名称 | 做用 | 读支持MediaType | 写支持MediaType |
---|---|---|---|
MappingJackson2HttpMessageConverter | 使用Jackson的ObjectMapper转换Json数据 | application/json | application/json |
StringHttpMessageConverter | 数据与String类型的相互转换 | text/* | text/plain |
ByteArrayHttpMessageConverter | 数据与字节数组的相互转换 | / | application/octet-stream |
下面是HttpMessageConverter
接口,实现Http消息转换就必须实现这个接口,不过咱们一般不会直接实现这个接口,而是经过继承它的子类进行扩展处理,默认提供的Converter
对视经过继承它的抽象类进行扩展。
public interface HttpMessageConverter<T> { //判断是否对接收请求进行处理 boolean canRead(Class<?> clazz, MediaType mediaType); //判断是否对响应进行处理 boolean canWrite(Class<?> clazz, MediaType mediaType); //返回此转换器支持的MediaType对象列表 List<MediaType> getSupportedMediaTypes(); //对接收的请求进行处理 T read(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException; //对响应进行处理 void write(T t, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException; }
一般被继承的抽象类是AbstractHttpMessageConverter
,例如经常使用的MappingJackson2HttpMessageConverter
就是对它的继承,这样能减小编写处理方法的工做量,若是咱们要进行扩展,一般会在系统已有Converter
上进行配置,少数时候会进行继承扩展。
配置方式见上方 configureMessageConverters
配置消息转换器和 extendMessageConverters
扩展消息转换器的说明。
注:目前在目前开发中的开发中,对请求的消息和响应进行彻底自定义的场合很少,多数都是对已有的MessageConverter
进行设置和加强。
Formatter
和Converter
相似, 是将一种类型转换成另外一种类型, 可是, Formatter
的源类型必须是一个String
, 目标类型是java类型。在SpringMVC中,处理的多数输入都是文本方式的输入,所以, 选择Formatter
比选择Converter
更合适。
Formatter接口的结构以下:
package org.springframework.format; public interface Formatter<T> extends Printer<T>, Parser<T> { } public interface Printer<T> { String print(T var1, Locale var2); } public interface Parser<T> { T parse(String var1, Locale var2) throws ParseException; }
这里的T表示输入字符串要转换的目标类型。parse方法利用指定的Locale将一个String解析成目标类型。print方法相反,它是返回目标对象的字符串表示法。
配置方式见上方addFormatters
。