第一篇:詳解WebMvcConfigurer接口

接口源碼如下:

package org.springframework.web.servlet.config.annotation;

import java.util.List;

import org.springframework.format.FormatterRegistry;

import org.springframework.http.converter.HttpMessageConverter;

import org.springframework.validation.MessageCodesResolver;

import org.springframework.validation.Validator;

import org.springframework.web.method.support.HandlerMethodArgumentResolver;

import org.springframework.web.method.support.HandlerMethodReturnValueHandler;

import org.springframework.web.servlet.HandlerExceptionResolver;

public interface WebMvcConfigurer {

? ? void configurePathMatch(PathMatchConfigurer var1);

? ? void configureContentNegotiation(ContentNegotiationConfigurer var1);

? ? void configureAsyncSupport(AsyncSupportConfigurer var1);

? ? void configureDefaultServletHandling(DefaultServletHandlerConfigurer var1);

? ? void addFormatters(FormatterRegistry var1);

? ? void addInterceptors(InterceptorRegistry var1);

? ? void addResourceHandlers(ResourceHandlerRegistry var1);

? ? void addCorsMappings(CorsRegistry var1);

? ? void addViewControllers(ViewControllerRegistry var1);

? ? void configureViewResolvers(ViewResolverRegistry var1);

? ? void addArgumentResolvers(List<HandlerMethodArgumentResolver> var1);

? ? void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> var1);

? ? void configureMessageConverters(List<HttpMessageConverter<?>> var1);

? ? void extendMessageConverters(List<HttpMessageConverter<?>> var1);

? ? void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> var1);

? ? void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> var1);

? ? Validator getValidator();

? ? MessageCodesResolver getMessageCodesResolver();

}

接下來我們著重找?guī)讉€方法講解一下:

/* 攔截器配置 */

void addInterceptors(InterceptorRegistry var1);

/* 視圖跳轉(zhuǎn)控制器 */

void addViewControllers(ViewControllerRegistry registry);

/*靜態(tài)資源處理*/

void addResourceHandlers(ResourceHandlerRegistry registry);

/* 默認靜態(tài)資源處理器 */

void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer);

/* 這里配置視圖解析器*/

void configureViewResolvers(ViewResolverRegistry registry);

/* 配置內(nèi)容裁決的一些選項*/

void configureContentNegotiation(ContentNegotiationConfigurer configurer);


1、addInterceptors(InterceptorRegistry registry)

此方法用來專門注冊一個Interceptor,如HandlerInterceptorAdapter

? ? @Configuration

? ? public class MyWebMvcConfigurer implements WebMvcConfigurer {

? ? @Override

? ? ? ? public void addInterceptors(InterceptorRegistry registry) {

? ? ? ? ? ? registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/emp/toLogin","/emp/login","/js/**","/css/**","/images/**");

? ? ? ? }

? ? }

addPathPatterns("/**")對所有請求都攔截颂郎,但是排除了/toLogin和/login請求的攔截。

當spring boot版本升級為2.x時蛤肌,訪問靜態(tài)資源就會被HandlerInterceptor攔截,網(wǎng)上有很多處理辦法都是如下寫法

.excludePathPatterns("/index.html","/","/user/login","/static/**");

可惜本人在使用時一直不起作用,查看請求的路徑里并沒有/static/如圖:

于是我改成了"/js/**","/css/**","/images/**"這樣頁面內(nèi)容就可以正常訪問了批狱,我的項目結(jié)構(gòu)如下:

2. 頁面跳轉(zhuǎn)addViewControllers

以前寫SpringMVC的時候裸准,如果需要訪問一個頁面,必須要寫Controller類赔硫,然后再寫一個方法跳轉(zhuǎn)到頁面炒俱,感覺好麻煩,其實重寫WebMvcConfigurer中的addViewControllers方法即可達到效果了

? ? /**

? ? ? ? * 以前要訪問一個頁面需要先創(chuàng)建個Controller控制類爪膊,再寫方法跳轉(zhuǎn)到頁面

? ? ? ? * 在這里配置后就不需要那么麻煩了权悟,直接訪問http://localhost:8080/toLogin就跳轉(zhuǎn)到login.jsp頁面了

? ? ? ? * @param registry

? ? ? ? */

? ? ? ? @Override

? ? ? ? public void addViewControllers(ViewControllerRegistry registry) {

? ? ? ? ? ? registry.addViewController("/toLogin").setViewName("login");


? ? ? ? }

值的指出的是,在這里重寫addViewControllers方法推盛,并不會覆蓋WebMvcAutoConfiguration中的addViewControllers(在此方法中峦阁,Spring Boot將“/”映射至index.html),這也就意味著我們自己的配置和Spring Boot的自動配置同時有效耘成,這也是我們推薦添加自己的MVC配置的方式榔昔。

3. 自定義資源映射addResourceHandlers

比如驹闰,我們想自定義靜態(tài)資源映射目錄的話,只需重寫addResourceHandlers方法即可撒会。

注:如果繼承WebMvcConfigurationSupport類實現(xiàn)配置時必須要重寫該方法疮方,具體見其它文章

? ? @Configuration

? ? public class MyWebMvcConfigurerAdapter implements WebMvcConfigurer {

? ? ? ? /**

? ? ? ? * 配置靜態(tài)訪問資源

? ? ? ? * @param registry

? ? ? ? */

? ? ? ? @Override

? ? ? ? public void addResourceHandlers(ResourceHandlerRegistry registry) {

? ? ? ? ? ? registry.addResourceHandler("/my/**").addResourceLocations("classpath:/my/")

? ? ? ? }

? ? }

通過addResourceHandler添加映射路徑,然后通過addResourceLocations來指定路徑茧彤。我們訪問自定義my文件夾中的elephant.jpg 圖片的地址為 http://localhost:8080/my/elephant.jpg

如果你想指定外部的目錄也很簡單,直接addResourceLocations指定即可疆栏,代碼如下:

? ? @Override

? ? ? ? public void addResourceHandlers(ResourceHandlerRegistry registry) {

? ? ? ? ? ? registry.addResourceHandler("/my/**").addResourceLocations("file:E:/my/");

?}

addResourceLocations指的是文件放置的目錄曾掂,addResoureHandler指的是對外暴露的訪問路徑

4. configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer)

  用法:

? ? ? ? @Override

? ? ? ? public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {

? ? ? ? ? ? configurer.enable();

? ? ? ? ? ? configurer.enable("defaultServletName");

? ? ? ? }

  此時會注冊一個默認的Handler:DefaultServletHttpRequestHandler,這個Handler也是用來處理靜態(tài)文件的壁顶,它會嘗試映射/珠洗。當DispatcherServelt映射/時(/ 和/ 是有區(qū)別的),并且沒有找到合適的Handler來處理請求時若专,就會交給DefaultServletHttpRequestHandler 來處理许蓖。注意:這里的靜態(tài)資源是放置在web根目錄下,而非WEB-INF 下调衰。

  可能這里的描述有點不好懂(我自己也這么覺得)膊爪,所以簡單舉個例子,例如:在webroot目錄下有一個圖片:1.png 我們知道Servelt規(guī)范中web根目錄(webroot)下的文件可以直接訪問的嚎莉,但是由于DispatcherServlet配置了映射路徑是:/ 米酬,它幾乎把所有的請求都攔截了,從而導致1.png 訪問不到趋箩,這時注冊一個DefaultServletHttpRequestHandler 就可以解決這個問題赃额。其實可以理解為DispatcherServlet破壞了Servlet的一個特性(根目錄下的文件可以直接訪問),DefaultServletHttpRequestHandler是幫助回歸這個特性的叫确。

5跳芳、configureViewResolvers(ViewResolverRegistry registry)

  從方法名稱我們就能看出這個方法是用來配置視圖解析器的,該方法的參數(shù)ViewResolverRegistry 是一個注冊器竹勉,用來注冊你想自定義的視圖解析器等飞盆。ViewResolverRegistry 常用的幾個方法:

? ? ? 1).enableContentNegotiation()

? ? /** 啟用內(nèi)容裁決視圖解析器*/

? ? public void enableContentNegotiation(View... defaultViews) {

? ? ? ? initContentNegotiatingViewResolver(defaultViews);

? ? }

  該方法會創(chuàng)建一個內(nèi)容裁決解析器ContentNegotiatingViewResolver ,該解析器不進行具體視圖的解析饶米,而是管理你注冊的所有視圖解析器桨啃,所有的視圖會先經(jīng)過它進行解析,然后由它來決定具體使用哪個解析器進行解析檬输。具體的映射規(guī)則是根據(jù)請求的media types來決定的照瘾。

? ? ? ? 2).  UrlBasedViewResolverRegistration()

? ? ? ? public UrlBasedViewResolverRegistration jsp(String prefix, String suffix) {

? ? ? ? ? ? InternalResourceViewResolver resolver = new InternalResourceViewResolver();

? ? ? ? ? ? resolver.setPrefix(prefix);

? ? ? ? ? ? resolver.setSuffix(suffix);

? ? ? ? ? ? this.viewResolvers.add(resolver);

? ? ? ? ? ? return new UrlBasedViewResolverRegistration(resolver);

? ? ? ? }

  該方法會注冊一個內(nèi)部資源視圖解析器InternalResourceViewResolver 顯然訪問的所有jsp都是它進行解析的。該方法參數(shù)用來指定路徑的前綴和文件后綴丧慈,如:

registry.jsp("/WEB-INF/jsp/", ".jsp");

 對于以上配置析命,假如返回的視圖名稱是example主卫,它會返回/WEB-INF/jsp/example.jsp給前端,找不到則報404鹃愤。

? ? ? ? 3).  beanName()

? ? ? ? public void beanName() {

? ? ? ? ? ? BeanNameViewResolver resolver = new BeanNameViewResolver();

? ? ? ? ? ? this.viewResolvers.add(resolver);

? ? ? ? }

  該方法會注冊一個BeanNameViewResolver 視圖解析器簇搅,這個解析器是干嘛的呢?它主要是將視圖名稱解析成對應的bean软吐。什么意思呢瘩将?假如返回的視圖名稱是example,它會到spring容器中找有沒有一個叫example的bean凹耙,并且這個bean是View.class類型的姿现?如果有,返回這個bean肖抱。

? ? ? ? 4).  viewResolver()

? ? ? ? public void viewResolver(ViewResolver viewResolver) {

? ? ? ? ? ? if (viewResolver instanceof ContentNegotiatingViewResolver) {

? ? ? ? ? ? ? ? throw new BeanInitializationException(

? ? ? ? ? ? ? ? ? ? ? ? "addViewResolver cannot be used to configure a ContentNegotiatingViewResolver. Please use the method enableContentNegotiation instead.");

? ? ? ? ? ? }

? ? ? ? ? ? this.viewResolvers.add(viewResolver);

? ? ? ? }

  這個方法想必看名字就知道了备典,它就是用來注冊各種各樣的視圖解析器的,包括自己定義的意述。

6. configureContentNegotiation(ContentNegotiationConfigurer configurer)

  上面我們講了configureViewResolvers 方法提佣,假如在該方法中我們啟用了內(nèi)容裁決解析器,那么configureContentNegotiation(ContentNegotiationConfigurer configurer) 這個方法是專門用來配置內(nèi)容裁決的一些參數(shù)的荤崇。這個比較簡單拌屏,我們直接通過一個例子看:

? ? public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {

? ? ? ? ? /* 是否通過請求Url的擴展名來決定media type */

? ? ? ? ? ? configurer.favorPathExtension(true)?

? ? ? ? ? ? ? ? ? ? /* 不檢查Accept請求頭 */

? ? ? ? ? ? ? ? ? ? .ignoreAcceptHeader(true)

? ? ? ? ? ? ? ? ? ? .parameterName("mediaType")

? ? ? ? ? ? ? ? ? ? /* 設(shè)置默認的media yype */

? ? ? ? ? ? ? ? ? ? .defaultContentType(MediaType.TEXT_HTML)

? ? ? ? ? ? ? ? ? ? /* 請求以.html結(jié)尾的會被當成MediaType.TEXT_HTML*/

? ? ? ? ? ? ? ? ? ? .mediaType("html", MediaType.TEXT_HTML)

? ? ? ? ? ? ? ? ? ? /* 請求以.json結(jié)尾的會被當成MediaType.APPLICATION_JSON*/

? ? ? ? ? ? ? ? ? ? .mediaType("json", MediaType.APPLICATION_JSON);

? ? ? ? }

到這里我們就可以舉個例子來進一步熟悉下我們上面講的知識了,假如我們MVC的配置如下:

? ? @EnableWebMvc

? ? ? ? @Configuration

? ? ? ? public class MyWebMvcConfigurerAdapte extends WebMvcConfigurerAdapter {


? ? ? ? ? ? @Override

? ? ? ? ? ? public void configureViewResolvers(ViewResolverRegistry registry) {

? ? ? ? ? ? ? ? registry.jsp("/WEB-INF/jsp/", ".jsp");

? ? ? ? ? ? ? ? registry.enableContentNegotiation(new MappingJackson2JsonView());

? ? ? ? ? ? }


? ? ? ? ? ? @Override

? ? ? ? ? ? public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {

? ? ? ? ? ? ? ? configurer.favorPathExtension(true)

? ? ? ? ? ? ? ? ? ? ? ? .ignoreAcceptHeader(true)

? ? ? ? ? ? ? ? ? ? ? ? .parameterName("mediaType")

? ? ? ? ? ? ? ? ? ? ? ? .defaultContentType(MediaType.TEXT_HTML)

? ? ? ? ? ? ? ? ? ? ? ? .mediaType("html", MediaType.TEXT_HTML)

? ? ? ? ? ? ? ? ? ? ? ? .mediaType("json", MediaType.APPLICATION_JSON);

? ? ? ? ? ? }

? ? ? ? }

controller的代碼如下:

? ? ? ? @Controller

? ? ? ? public class ExampleController {

? ? ? ? ? ? @RequestMapping("/test")

? ? ? ? ? ? public ModelAndView test() {

? ? ? ? ? ? ? ? Map<String, String> map = new HashMap();

? ? ? ? ? ? ? ? map.put("哈哈", "哈哈哈哈");

? ? ? ? ? ? ? ? map.put("呵呵", "呵呵呵呵");

? ? ? ? ? ? ? ? return new ModelAndView("test", map);

? ? ? ? ? ? }

? ? ? ? }

在WEB-INF/jsp目錄下創(chuàng)建一個test.jsp文件术荤,內(nèi)容隨意』笨牵現(xiàn)在啟動tomcat,在瀏覽器輸入以下鏈接:http://localhost:8080/test.json喜每,瀏覽器內(nèi)容返回如下:

? ? {

? ? ? ? "哈哈":"哈哈哈哈",

? ? ? ? "呵呵":"呵呵呵呵"

? ? }

在瀏覽器輸入http://localhost:8080/test 或者http://localhost:8080/test.html务唐,內(nèi)容返回如下:

this is test.jsp

顯然,兩次使用了不同的視圖解析器带兜,那么底層到底發(fā)生了什么枫笛?在配置里我們注冊了兩個視圖解析器:ContentNegotiatingViewResolver 和 InternalResourceViewResolver,還有一個默認視圖:MappingJackson2JsonView刚照。controller執(zhí)行完畢之后返回一個ModelAndView刑巧,其中視圖的名稱為example1。

? ? 1.返回首先會交給ContentNegotiatingViewResolver 進行視圖解析處理无畔,而ContentNegotiatingViewResolver 會先把視圖名example1交給它持有的所有ViewResolver嘗試進行解析(本實例中只有InternalResourceViewResolver)啊楚,

? ? 2.根據(jù)請求的mediaType,再將example1.mediaType(這里是example1.json 和example1.html)作為視圖名讓所有視圖解析器解析一遍浑彰,兩步解析完畢之后會獲得一堆候選的List<View> 再加上默認的MappingJackson2JsonView 恭理,

? ? 3.根據(jù)請求的media type從候選的List<View> 中選擇一個最佳的返回,至此視圖解析完畢郭变。

現(xiàn)在就可以理解上例中為何請求鏈接加上.json 和不.json 結(jié)果會不一樣颜价。當加上.json 時涯保,表示請求的media type 為MediaType.APPLICATION_JSON,而InternalResourceViewResolver 解析出來的視圖的ContentType與其不符周伦,而與MappingJackson2JsonView 的ContentType相符夕春,所以選擇了MappingJackson2JsonView 作為視圖返回。當不加.json 請求時专挪,默認的media type 為MediaType.TEXT_HTML及志,所以就使用了InternalResourceViewResolver解析出來的視圖作為返回值了。我想看到這里你已經(jīng)大致可以自定義視圖了寨腔。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末困肩,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子脆侮,更是在濱河造成了極大的恐慌,老刑警劉巖勇劣,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件靖避,死亡現(xiàn)場離奇詭異,居然都是意外死亡比默,警方通過查閱死者的電腦和手機幻捏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來命咐,“玉大人篡九,你說我怎么就攤上這事〈椎欤” “怎么了榛臼?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長窜司。 經(jīng)常有香客問我沛善,道長,這世上最難降的妖魔是什么塞祈? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任金刁,我火速辦了婚禮,結(jié)果婚禮上议薪,老公的妹妹穿的比我還像新娘尤蛮。我一直安慰自己,他們只是感情好斯议,可當我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布产捞。 她就那樣靜靜地躺著,像睡著了一般哼御。 火紅的嫁衣襯著肌膚如雪轧葛。 梳的紋絲不亂的頭發(fā)上搂抒,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天,我揣著相機與錄音尿扯,去河邊找鬼求晶。 笑死,一個胖子當著我的面吹牛衷笋,可吹牛的內(nèi)容都是我干的芳杏。 我是一名探鬼主播,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼辟宗,長吁一口氣:“原來是場噩夢啊……” “哼爵赵!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起泊脐,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤空幻,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后容客,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體秕铛,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年缩挑,在試婚紗的時候發(fā)現(xiàn)自己被綠了但两。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡供置,死狀恐怖谨湘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情芥丧,我是刑警寧澤紧阔,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站续担,受9級特大地震影響寓辱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜赤拒,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一秫筏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧挎挖,春花似錦这敬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至始衅,卻和暖如春冷蚂,著一層夾襖步出監(jiān)牢的瞬間缭保,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工蝙茶, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留艺骂,地道東北人。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓隆夯,卻偏偏與公主長得像钳恕,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蹄衷,可洞房花燭夜當晚...
    茶點故事閱讀 44,955評論 2 355