最近看了《Spring in Action》的開頭,就被Spring注解開發(完全不寫web.xml)驚嘆了,也第一次知道了@EnableWebMvc是SpringMVC的注解
@EnableWebMvc注解
@EnableWebMvc的javaDoc注釋有點長。 從下圖得到的幾個信息: href="mailto:1.@EnableWebMvc">1.@EnableWebMvc沒有任何屬性,只是@Import了一個類叫DelegatingWebMvcConfiguration
2. 注釋中說明:將@EnableWebMvc添加給@Configuration類來導入SpringMvc的配置;3.自定義MVC配置,實現接口WebMvcConfigurer或更可能繼承WebMvcConfigurerAdapter,並且使用@EnableWebMvc;
4.如果還想要自定義配置,移除@EnableWebMvc,並且繼承WebMvcConfigurationSupport或DelegatingWebMvcConfiguration。
5.@EnableWebMvc出現自Spring3.1的版本
@EnableWebMvc的注釋文檔說的很詳細,先記錄第一點,@Import的類是干啥的!
@Import的多種用法看我這篇文檔:Spring @Import . @Import導入了一個類DelegatingWebMvcConfiguration,這個類標注了@Configuration類,這個類下方法上標注了@Bean的都會納入Spring容器管理。
DelegatingWebMvcConfiguration類如下,但是類里面搜索卻沒有@Bean標注! 在其父類WebMvcConfigurationSupport里面搜索到了19個@Bean.
這19個@Bean基本上和<mvc:annotation-driven/>實現的功能無差,甚至提供了一種很便捷的方式擴展<mvc:annotation-driven/>
舉兩個栗子,說明下:1. <mvc:annotation-driven/> @ResponseBody 返回String中文亂碼 Spring @ResponseBody String中文亂碼
@EnableWebMvc同樣存在這個問題,默認字符集為ISO-8859-1,解決方案:繼承WebMvcConfigurerAdapter,重寫extendMessageConverters方法!
栗子2:比如需要添加攔截器,原先寫法可能是<mvc:interceptors></mvc:interceptors>這種形式添加,現在不用XML,方式就是這樣:
在繼承WebMvcConfigurerAdapter基礎上,重寫addInterceptor方法。
@EnableWebMvc可擴展的結構
圖片可以從processOn拷貝,RequestMappingHandlerMapping地址:https://www.processon.com/view/5c9d8480e4b035b243ba651b
RequestMappingHandlerAdapter地址:https://www.processon.com/view/5c9d86cee4b034408de5663d 紅框代表可繼承擴展WebMvcConfigurerAdapter等擴展的方法。
想到為什么繼承WebMvcConfigurerAdapter或者實現WebMvcConfigurer,就能夠在@EnableWebMvc繼承上擴展呢?
@EnableWebMvc導入的是DelegatingWebMvcConfiguration類,和WebMvcConfigurer完全沒有聯系啊?
說明:DelegatingWebMvcConfiguration類,有這樣一個屬性WebMvcConfigurerComposite,維護着WebMvcConfigurer的集合,有點類似組合模式。
初始化DelegatingWebMvcConfiguration時如果發現了WebMvcConfigurer的實現類,就注入到WebMvcConfigurerComposite中,這樣就把我們實現了WebMvcConfigurer的類和@EnableWebMvc聯系到一起了,
就可以在<mvc:annotation-driven/>基礎上擴展.
這里很神奇,在我理解中Spring沒法自動判斷兩個Bean作為集合注入的啊! 這里起作用的就是required=false 少了這句話就拋出異常!
@EnableWebMvc默認是沒有靜態資源放行的,比如.css .js文件默認 也會被 dispatchServelt / 形式的攔截到
原來在web.xml文件中可以這樣配置,靜態資源后綴為.js .css不攔截
<servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.html</url-pattern> <url-pattern>*.htm</url-pattern> <url-pattern>*.css</url-pattern> <url-pattern>*.js</url-pattern> </servlet-mapping>
在沒有web.xml的文件中解決方案:繼承WebMvcConfigurerAdapter,並且重寫configureDefaultServletHandling方法,即可達到和上面一樣的效果,就可以訪問 .css .js等文件!
原理解析:如果這篇博客前面看過了,直接看到WebMvcConfigurationSupport,@Bean注冊了一個bean defaultServletHandlerMapping,最終configureDefaultServletHandling會制定我們上面的方法。
DefaultServletHandlerConfigurer只是個配置類,Bean實際返回的是hanlderMapping!=null?handlerMapping:new EmptyHandlerMapping(); EmptyHandlerMapping不對請求做任何處理,而configurer的getHandlerMapping方法如下: handler不為空,就會返回一個SimpleUrlHandlerMapping,而configurer.enable()方法等價於configurer.enable(null),就是初始化handler的。總的來說就是,configurer.enable()方法就是注冊了一個SimpleUrlHandlerMapping對象,handler就是DefaultServletHttpRequestHandler.
DefaultServletHttpRequestHandler如何處理靜態資源請求?
通過獲取 服務器默認的servlet進行轉發,因為/** 會匹配所有的URI,所以SimpleUrlHandlerMapping必須放在RequestMappingHandlerMapping之后;核心方式就是servletContext.getNamedDispatcher(“default”).forward(request,response);
總結@EnableWebMvc替我們做了什么?
引入了這樣一個類DelegatingWebMvcConfiguration,通過@Bean注冊了和<mvc:annotation-driven/>一樣的組件,RequestMappingHandlerMapping、RequestMappingHandlerAdatper、HandlerExceptionResolver等等,只要有個Spring管理的bean繼承WebMvcConfigurer或WebMvcConfigurerAdapter,重寫方法即可自定義<mvc:annotation-driven/>.
其實@EnableWebMvc == @Import({DelegatingWebMvcConfiguration.class})