對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
。