使用WebMvcConfigurer配置SpringMVC過程
一、前言
WebMvcConfigurer配置類是使用Java代碼代替?zhèn)鹘y(tǒng)的xml配置文件,對SpirngMvc進(jìn)行配置的一種方式,需要?jiǎng)?chuàng)建一個(gè)配置類@Configuration并實(shí)現(xiàn)WebMvcConfigurer 接口(推薦)。
二、使用方式
官方推薦使用實(shí)現(xiàn)WebMvcConfigurer接口(推薦)或者繼承WebMvcConfigurationSupport類來實(shí)現(xiàn)代碼配置。
1.配置類實(shí)現(xiàn)WebMvcConfigurer接口
@Configuration
public class WebMvcConfg implements WebMvcConfigurer {}2.配置類繼承WebMvcConfigurationSupport
@Configuration
public class WebMvcConfg extends WebMvcConfigurationSupport {}三、WebMvcConfigurer接口常用方法
addInterceptors:攔截器
- addInterceptor:需要一個(gè)實(shí)現(xiàn)HandlerInterceptor接口的攔截器實(shí)例
- addPathPatterns:用于設(shè)置攔截器的過濾路徑規(guī)則;- addPathPatterns(“/**”)對所有請求都攔截
- excludePathPatterns:用于設(shè)置不需要攔截的過濾規(guī)則
- 攔截器主要用途:進(jìn)行用戶登錄狀態(tài)的攔截,日志的攔截等。
addCorsMappings:跨域
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*") //瀏覽器允許所有的域訪問 / 注意 * 不能滿足帶有cookie的訪問,Origin 必須是全匹配
.allowCredentials(true) // 允許帶cookie訪問
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("token")
.maxAge(3600);
}configureContentNegotiation:默認(rèn)內(nèi)容協(xié)商配置
內(nèi)容協(xié)商:在 HTTP 協(xié)議中,內(nèi)容協(xié)商是這樣一種機(jī)制,通過為同一 URI 指向的資源提供不同的展現(xiàn)形式,可以使用戶代理選擇與用戶需求相適應(yīng)的最佳匹配
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
// 是否通過請求Url的擴(kuò)展名來決定media type
configurer.favorPathExtension(false);
//不檢查Accept請求頭 設(shè)置默認(rèn)的media type
configurer.ignoreAcceptHeader(true).defaultContentType(MediaType.APPLICATION_JSON_UTF8);
}上面代碼說白了就是告訴系統(tǒng)什么類型用什么來標(biāo)識。
addViewControllers:跳轉(zhuǎn)指定頁面
以前寫SpringMVC的時(shí)候,如果需要訪問一個(gè)頁面,必須要寫Controller類,然后再寫一個(gè)方法跳轉(zhuǎn)到頁面,感覺好麻煩,其實(shí)重寫WebMvcConfigurer中的addViewControllers方法即可達(dá)到效果了。
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 綁定指定URL訪問的view名稱,這行的意思是在訪問/路徑時(shí)會自動(dòng)跳轉(zhuǎn)到index.html;如/login則不受影響
registry.addViewController("/").setViewName("forward:/index.html");
}resourceViewResolver:視圖解析器
configureMessageConverters:信息轉(zhuǎn)換器
配置在接收request (請求)和返回response (響應(yīng))時(shí)的數(shù)據(jù)轉(zhuǎn)換器,最常用到的就是fastJson的轉(zhuǎn)換,配置如下所示
該方法會替換所有默認(rèn)的MessageConverters,使用自定義添加converters,在實(shí)際項(xiàng)目中應(yīng)用比例也比較??;
/**
* 消息內(nèi)容轉(zhuǎn)換配置
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
//StringHttpMessageConverter默認(rèn)使用的字符集是ISO-8859-1,改為輸出的JSON字符串為UTF-8字符集
converters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8));
//添加fastjson消息轉(zhuǎn)換器
converters.add(fastJsonHttpMessageConverters());
}
/**
* 引入Fastjson解析json,不使用默認(rèn)的jackson,必須在pom.xml引入fastjson的jar包,并且版必須大于1.2.10
*
* @return
*/
@Bean
public FastJsonHttpMessageConverter fastJsonHttpMessageConverters() {
//1、定義一個(gè)convert轉(zhuǎn)換消息的對象
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
//2、添加fastjson的配置信息
FastJsonConfig fastJsonConfig = new FastJsonConfig();
SerializerFeature[] serializerFeatures = new SerializerFeature[]{
// 輸出key是包含雙引號
//SerializerFeature.QuoteFieldNames,
// 是否輸出為null的字段,若為null 則顯示該字段
SerializerFeature.WriteMapNullValue,
// 數(shù)值字段如果為null,則輸出為0
SerializerFeature.WriteNullNumberAsZero,
// List字段如果為null,輸出為[],而非null
SerializerFeature.WriteNullListAsEmpty,
// 字符類型字段如果為null,輸出為"",而非null
SerializerFeature.WriteNullStringAsEmpty,
// Boolean字段如果為null,輸出為false,而非null
SerializerFeature.WriteNullBooleanAsFalse,
// Date的日期轉(zhuǎn)換器
SerializerFeature.WriteDateUseDateFormat,
// 循環(huán)引用(如果不配置有可能會進(jìn)入死循環(huán))
SerializerFeature.DisableCircularReferenceDetect,
// 格式化JSON
SerializerFeature.PrettyFormat
};
fastJsonConfig.setSerializerFeatures(serializerFeatures);
fastJsonConfig.setCharset(Charset.forName("UTF-8"));
//3.解決亂碼問題。定義響應(yīng)的MIME類型,設(shè)置響應(yīng)的content-type為application/json;charset=UTF-8
List<MediaType> fastMediaType = new ArrayList<>();
fastMediaType.add(MediaType.valueOf(MediaType.APPLICATION_JSON_VALUE));
//4.converter消息轉(zhuǎn)換器添加配置信息
fastConverter.setSupportedMediaTypes(fastMediaType);
//5、在convert中添加配置信息
fastConverter.setFastJsonConfig(fastJsonConfig);
return fastConverter;
}extendMessageConverters:信息轉(zhuǎn)換器
1.使用
springboot2.0版本以后推薦使用extendMessageConverters來進(jìn)行web配置,這個(gè)方法不會覆蓋springmvc已默認(rèn)添加的HttpMessageConverter
即:在默認(rèn)加載的消息轉(zhuǎn)換器基礎(chǔ)上繼續(xù)添加自定義的converters實(shí)例
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(customerMappingJackson2HttpMessageConverter());
}
@Bean
public MappingJackson2HttpMessageConverter customerMappingJackson2HttpMessageConverter(){
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
//設(shè)置日期格式
ObjectMapper objectMapper = messageConverter.getObjectMapper();
SimpleDateFormat smt = new SimpleDateFormat("yyyy-MM-dd");
objectMapper.setDateFormat(smt);
messageConverter.setObjectMapper(objectMapper);
//設(shè)置中文編碼格式
List<MediaType> list = new ArrayList<MediaType>();
list.add(MediaType.APPLICATION_JSON);
messageConverter.setSupportedMediaTypes(list);
return messageConverter;
}應(yīng)用場景
空值處理
- 請求和返回的數(shù)據(jù)有很多空值,這些值有時(shí)候并沒有實(shí)際意義,我們可以過濾掉和不返回,或設(shè)置成默認(rèn)值。
- 比如通過重寫
getObjectMapper方法,將返回結(jié)果的空值不進(jìn)行序列化:
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter
mappingJackson2HttpMessageConverter =
new MappingJackson2HttpMessageConverter();
// 調(diào)整ObjectMapper對象序列化行為
ObjectMapper objectMapper = mappingJackson2HttpMessageConverter.getObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mappingJackson2HttpMessageConverter.setObjectMapper(objectMapper);
converters.add(0,mappingJackson2HttpMessageConverter);
}響應(yīng)字段格式調(diào)整
將Long類型轉(zhuǎn)為字符串方式返回,Date格式改為時(shí)間戳返回(底層原理:調(diào)整MappingJackson2HttpMessageConverter類中ObjectMapper序列化行為)
@Configuration
public class JacksonConfig implements WebMvcConfigurer {
@Value("${xss.enabled:false}")
private boolean enableXss;
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = jackson2HttpMessageConverter.getObjectMapper();
SimpleModule simpleModule = new SimpleModule();
//Long類型序列化成字符串,避免Long精度丟失
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
// simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
// XSS序列化
if (enableXss) {
simpleModule.addDeserializer(String.class, new XssJacksonDeserializer());
}
// Date
simpleModule.addSerializer(Date.class, new JacksonDateSerializer());
simpleModule.addDeserializer(Date.class, new JacksonDateDeserializer());
simpleModule.addSerializer(LocalDateTime.class, new JacksonLocalDateTimeSerializer());
simpleModule.addDeserializer(LocalDateTime.class, new JacksonLocalDateTimeDeserializer());
objectMapper.registerModule(simpleModule)
.registerModule(new JavaTimeModule())
.registerModule(new ParameterNamesModule())
.registerModule(new Jdk8Module());
jackson2HttpMessageConverter.setObjectMapper(objectMapper);
//放到第一個(gè)
converters.add(0, jackson2HttpMessageConverter);
}
}addResourceHandlers:靜態(tài)資源addResoureHandler:請求靜態(tài)資源的路徑addResourceLocations:靜態(tài)資源存放位置,可以配置多個(gè),按先后順序查找
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 10 days
addCacheControl(registry, "img", 864000);
addCacheControl(registry, "vendor", 864000);
// 1 day
addCacheControl(registry, "scripts", 86400);
addCacheControl(registry, "styles", 86400);
addCacheControl(registry, "views", 86400);
}
private void addCacheControl(ResourceHandlerRegistry registry, String folder, int cachePeriod) {
registry.addResourceHandler(String.format("/%s/**", folder))
.addResourceLocations(String.format("classpath:/static/%s/", folder))
.setCachePeriod(cachePeriod);
}可以對靜態(tài)資源做緩存控制之類的。
HttpMessageConverter接口
- Http請求響應(yīng)報(bào)文其實(shí)都是
字符串,當(dāng)請求報(bào)文到j(luò)ava程序會被封裝為一個(gè)ServletInputStream流,供開發(fā)人員讀取請求報(bào)文,響應(yīng)報(bào)文則通過ServletOutputStream流,來輸出響應(yīng)報(bào)文。從流中只能讀取到原始的字符串報(bào)文,同樣輸出流也是。 - 那么在報(bào)文SpringMVC 輸入和輸出都存在
一個(gè)字符串到j(luò)ava對象的轉(zhuǎn)化問題。這一過程,在SpringMVC是通過HttpMessageConverter來解決的。 - 在SpringMVC的處理過程中,
一次請求報(bào)文和一次響應(yīng)報(bào)文,分別被抽象為一個(gè)請求消息HttpInputMessage和一個(gè)響應(yīng)消息HttpOutputMessage。處理請求時(shí),由合適的消息轉(zhuǎn)換器將請求報(bào)文綁定為方法中的形參對象,在這里同一個(gè)對象就有可能出現(xiàn)多種不同的消息形式,如json、xml。同樣響應(yīng)請求也是同樣道理。

(1)HttpInputMessage 將請求的信息先轉(zhuǎn)為 InputStream 對象,InputStream 再由 HttpMessageConverter 轉(zhuǎn)換為 SpringMVC 需要的java對象;
(2)SpringMVC 返回一個(gè) java 對象, 并通過 HttpMessageConverter 轉(zhuǎn)為響應(yīng)信息,接著 HttpOutputMessage 將響應(yīng)的信息轉(zhuǎn)換為OutputStream,接著給出響應(yīng)。
簡單說就是 HTTP request (請求)和response (響應(yīng))數(shù)據(jù)的轉(zhuǎn)換器
通常實(shí)現(xiàn)HttpMessageConverter接口的轉(zhuǎn)換器有以下幾種:
ByteArrayHttpMessageConverter: 負(fù)責(zé)讀取二進(jìn)制格式的數(shù)據(jù)和寫出二進(jìn)制格式的數(shù)據(jù);StringHttpMessageConverter: 負(fù)責(zé)讀取字符串格式的數(shù)據(jù)和寫出二進(jìn)制格式的數(shù)據(jù);ResourceHttpMessageConverter:負(fù)責(zé)讀取資源文件和寫出資源文件數(shù)據(jù);FormHttpMessageConverter: 負(fù)責(zé)讀取form提交的數(shù)據(jù)(能讀取的數(shù)據(jù)格式為 application/x-www-form-urlencoded,不能讀取multipart/form-data格式數(shù)據(jù));負(fù)責(zé)寫入application/x-www-from-urlencoded和multipart/form-data格式的數(shù)據(jù);MappingJacksonHttpMessageConverter: 負(fù)責(zé)讀取和寫入json格式的數(shù)據(jù);SourceHttpMessageConverter: 負(fù)責(zé)讀取和寫入 xml 中javax.xml.transform.Source定義的數(shù)據(jù);Jaxb2RootElementHttpMessageConverter: 負(fù)責(zé)讀取和寫入xml 標(biāo)簽格式的數(shù)據(jù);AtomFeedHttpMessageConverter: 負(fù)責(zé)讀取和寫入Atom格式的數(shù)據(jù);RssChannelHttpMessageConverter: 負(fù)責(zé)讀取和寫入RSS格式的數(shù)據(jù)
在SpringMVC / SpringBoot中@ResponseBody這類注解默認(rèn)使用的是jackson來解析json
3.HttpMessageConverter請求信息轉(zhuǎn)換器執(zhí)行流程
當(dāng)用戶發(fā)送請求后,@Requestbody 注解會讀取請求body中的數(shù)據(jù),默認(rèn)的請求轉(zhuǎn)換器HttpMessageConverter通過獲取請求頭Header中的Content-Type來確認(rèn)請求頭的數(shù)據(jù)格式,從而來為請求數(shù)據(jù)適配合適的轉(zhuǎn)換器。
- 例如contentType=applicatin/json,那么轉(zhuǎn)換器會適配
MappingJacksonHttpMessageConverter - 響應(yīng)時(shí)候的時(shí)候同理,
@Responsebody注解會啟用HttpMessageConverter,通過檢測Header中Accept屬性來適配的響應(yīng)的轉(zhuǎn)換器。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
MyBatis自定義映射resultMap的實(shí)現(xiàn)
本文主要介紹了MyBatis自定義映射resultMap的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03
Java 用反射設(shè)置對象的屬性值實(shí)例詳解
這篇文章主要介紹了Java 用反射設(shè)置對象的屬性值實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-05-05
微服務(wù)進(jìn)行遠(yuǎn)程調(diào)用其他服務(wù)方式
文章主要介紹了如何將本地服務(wù)注冊到Nacos地址上,并詳細(xì)描述了服務(wù)調(diào)用和負(fù)載均衡的實(shí)現(xiàn)方法,包括使用`@EnableDiscoveryClient`注解開啟服務(wù)調(diào)用、編寫調(diào)用接口、創(chuàng)建RestTemplate配置類以及使用負(fù)載均衡依賴進(jìn)行服務(wù)調(diào)用2026-01-01
Springcloud整合stream,rabbitmq實(shí)現(xiàn)消息驅(qū)動(dòng)功能
官方定義SpringCloud?Stream?是一個(gè)構(gòu)建消息驅(qū)動(dòng)微服務(wù)的框架。我們只需要搞清楚如何與Spring?Cloud?Stream?交互就可以方便使用消息驅(qū)動(dòng)的方式。本文將通過Springcloud整合stream,rabbitmq實(shí)現(xiàn)消息驅(qū)動(dòng)功能,需要的可以參考一下2022-02-02

