標籤:springmvc
接<SpringMVC源碼分析(5)剖析重要組件HandlerMapping>,繼續剖析HandlerMapping,DefaultAnnotationHandlerMapping是SpringMVC 中最重要的HandlerMapping組件。雖然它在spring3.1版本後被廢棄了。
包括2部分內容
DefaultAnnotationHandlerMapping剖析
HandlerMapping的攔截器
1.DefaultAnnotationHandlerMapping剖析
鑒於它的重要地位,貼下結構圖
650) this.width=650;" src="http://s1.51cto.com/wyfs02/M01/8B/03/wKioL1hCKnWx4gYCAACX0CQYtFA954.png" title="111.png" alt="wKioL1hCKnWx4gYCAACX0CQYtFA954.png" />
public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandlerMapping { //是否使用尾碼註冊url(比如如果註冊了/users,同時也會註冊 /users.* 和/users/ private boolean useDefaultSuffixPattern = true; //緩衝handler和requestMapping條件關係,驗證時使用 private final Map<Class, RequestMapping> cachedMappings = new HashMap<Class, RequestMapping>(); ... }
1.1. 重寫determineUrlsForHandler方法
AbstractDetectingUrlHandlerMapping的子類,重寫determineUrlsForHandler方法;
initApplicationContext時被調用。為determineUrlsForHandler調用上下文。在springmvc容器初始化過程中調用。
650) this.width=650;" src="http://s4.51cto.com/wyfs02/M02/8B/03/wKioL1hCK1yhXCRbAABFzPcjfXs824.png" title="233.png" alt="wKioL1hCK1yhXCRbAABFzPcjfXs824.png" />
在上篇文章中總結過,HandlerMapping的主要職責
註冊Handler.可以是註解,可以是XML聲明。
產生url,有很多策略。beanName,首碼,包名稱都可以作為參考
維護mapping關係
url的匹配能力
DefaultAnnotationHandlerMapping也是如此,determineUrlsForHandler方法,根據方法名就可以猜到,“為Handler匹配URL”。和ControllerClassNameHandlerMapping,ControllerBeanNameHandlerMapping之流是辦的事一樣的。區別就是他們是基於XML定義的,靈活性不足。DefaultAnnotationHandlerMapping靈活性和功能上更強大而已,名稱從RequestMapping註解中擷取。具體可以參考determineUrlsForHandlerMethods方法。
//按方法逐個產生URLprotected String[] determineUrlsForHandlerMethods(Class<?> handlerType, final boolean hasTypeLevelMapping) { String[] subclassResult = determineUrlsForHandlerMethods(handlerType); if (subclassResult != null) { return subclassResult; } final Set<String> urls = new LinkedHashSet<String>(); Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>(); handlerTypes.add(handlerType); handlerTypes.addAll(Arrays.asList(handlerType.getInterfaces())); for (Class<?> currentHandlerType : handlerTypes) { ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() { public void doWith(Method method) { RequestMapping mapping = AnnotationUtils.findAnnotation(method, RequestMapping.class); if (mapping != null) { String[] mappedPatterns = mapping.value(); if (mappedPatterns.length > 0) { for (String mappedPattern : mappedPatterns) { if (!hasTypeLevelMapping && !mappedPattern.startsWith("/")) { mappedPattern = "/" + mappedPattern; } addUrlsForPath(urls, mappedPattern); } } else if (hasTypeLevelMapping) { // empty method-level RequestMapping urls.add(null); } } } }, ReflectionUtils.USER_DECLARED_METHODS); } return StringUtils.toStringArray(urls);}
2.HandlerMapping的攔截器
2.1 Interceptor位置
從下面的結構圖中可以看出,攔截器分布在2個位置,分為2類。
MappedInterceptor是與url綁定的,對合格URL進行攔截;
Interceptor是屬於全域範圍的,對所有請求進行進行攔截。
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M02/8B/07/wKiom1hCMDzyA3KAAAA1iMyumJU462.png" title="1.png" alt="wKiom1hCMDzyA3KAAAA1iMyumJU462.png" />
2.2 Interceptor的類結構
650) this.width=650;" src="http://s1.51cto.com/wyfs02/M01/8B/03/wKioL1hCMruCfElNAACC0tMAcZI252.png" title="2.png" alt="wKioL1hCMruCfElNAACC0tMAcZI252.png" />
| UserRoleAuthorizationInterceptor |
檢查目前使用者的授權
|
| PathExposingHandlerInterceptor |
暴露bestMatchingPattern |
ConversionServiceExposingInterceptor
|
暴露 ConversionService |
| ThemeChangeInterceptor |
支援主題切換
|
| LocaleChangeInterceptor |
支援切換語言
|
| UriTemplateVariablesHandlerInterceptor |
暴露請求變數
|
| WebContentInterceptor |
檢查,準備請求和響應
|
值得關注的是WebRequestInterceptor。需要專門開闢一章研究。
2.3 預設intercepter配置
<mvc:interceptors> <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" /> <bean class="org.springframework.web.servlet.handler.UserRoleAuthorizationInterceptor"/> <mvc:interceptor> <mvc:mapping path="/account"/> <bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor"></bean> </mvc:interceptor></mvc:interceptors>
這是一段最普通的聲明
650) this.width=650;" src="http://s4.51cto.com/wyfs02/M00/8B/04/wKioL1hCT1-ByAozAAByb2_2KaM218.png" title="3222.png" alt="wKioL1hCT1-ByAozAAByb2_2KaM218.png" />
我有了個誤解:<mvc:interceptors>的子標籤<bean>聲明的攔截器會設定在interceptor中,結果不是如此。
ConversionServiceExposingInterceptor是在解析標籤時,預設註冊的。<SpringMVC源碼分析(1)標籤解析>文章中提到過。
2.4 沒事找事型的配置
瞭解了spring mvc的標籤解析過程,很容易配置一個自訂程度比較高的處理器類。確點就是很繁瑣
如下
這一段代碼雖然可以運行,但缺少類型轉換攔截器,需要配置。
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> <property name="interceptors"> <array> <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" /> </array> </property> <property name="mappedInterceptors"> <list> <bean class="org.springframework.web.servlet.handler.MappedInterceptor"> <constructor-arg index="0"> <list> <value>/account</value> </list> </constructor-arg> <constructor-arg index="1"> <bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor"></bean> </constructor-arg> </bean> </list> </property></bean>
如此聲明,一定要把 <mvc:annotation-driven />注釋掉,否則系統會存在兩個DefaultAnnotationHandlerMapping。
個人感覺這一段和上面的<mvc:interceptors>效果是一樣的。
區別
僅是LocaleChangeInterceptor這樣公用的攔截器設定在了interceptors屬性上,
而不是mappedInterceptors。
源碼解析,一定不要停留在設定表面,要洞察底層細節。
當然,是預設配置好,還是原生態的配置好,一個是簡介透明,一個自訂程度高,各取所需。
本文出自 “簡單” 部落格,請務必保留此出處http://dba10g.blog.51cto.com/764602/1879103
SpringMVC源碼分析(6)剖析DefaultAnnotationHandlerMapping