爲了下降文章篇幅,使得文章更目標化,簡潔化,咱們就不例舉各類@RequestMapping的用法等內容了.html
文章主要說明如下問題:java
Spring怎樣處理@RequestMapping(怎樣將請求路徑映射到控制器類或方法)
ios
Spring怎樣將請求分派給正確的控制器類或方法web
Spring如何實現靈活的控制器方法的spring
在Spring MVC 3.1 以前的版本中,Spring默認使用 DefaultAnnotationHandlerMapping,AnnotationMethodHandlerAdapter來處理 @RequestMapping註解和請求方法調用,而從3.1開始提供了一組新的API完成這些工做。相比之下,新的API更加的合理完善,開放,易拓 展,面向對象。這篇文章即是基於3.1的新API進行剖析的。
api
在開始以前咱們先了解下新的API中引入的新接口或者類,這會有助於後面的處理過程的理解。不得不說新的API提供了更多漂亮的抽象,你能感覺到面向對象的魅力。數組
RequestMappingInfo 這個類是對請求映射的一個抽象,它包含了請求路徑,請求方法,請求頭等信息。其實能夠看作是@RequestMapping的一個對應類。緩存
HandlerMethod 這個類封裝了處理器實例(Controller Bean)和 處理方法實例(Method)以及方法參數數組(MethodParameter[])安全
MethodParameter 這個類從2.0就有了,它封裝了方法某個參數的相關信息及行爲,如該參數的索引,該參數所屬方法實例或構造器實例,該參數的類型等。mvc
HandlerMapping 該接口的實現類用來定義請求和處理器以前的映射關係,其中只定義了一個方法getHandler。
AbstractHandlerMethodMapping 這是HandlerMapping的一個基本實現類,該類定義了請求與HandlerMethod實例的映射關係。
RequestMappingInfoHandlerMapping 這個是AbstractHandlerMethodMapping的實現類,他維護了一個RequestMappingInfo和HandlerMethod的Map屬性。
RequestMappingHandlerMapping 這個是RequestMappingInfoHandlerMapping的子類,它將@RequestMapping註解轉化爲RequestMappingInfo實例,併爲父類使用。也就是咱們處理@RequestMapping的終點。
InitializingBean 這個接口定義了其實現Bean在容器完成屬性設置後能夠執行自定義初始化操做,咱們的AbstractHandlerMethodMapping便實現了這個接口,而且定義了一組自定義操做,就是用來檢測處理咱們的@RequestMapping註解。
概念講的太多總不是什麼好事。但明白了上述概念基本上就成功一半了,其中的實現相對@Autowired那篇簡單多了。
咱們從頭開始,看看到底Spring是怎樣檢測並處理咱們@RequestMapping註解的。不知你們還記不記的這段代碼:
Object exposedObject = bean; try { populateBean(beanName, mbd, instanceWrapper); if (exposedObject != null) { exposedObject = initializeBean(beanName, exposedObject, mbd); } }
這是BeanFactory建立Bean過程當中須要執行的一段代碼,其中populateBean方法即是@Autowired註解的處理過程,執行的屬性的自動注入等操做。由於initializeBean方法當時與主題無關沒有講,不過這時它即是咱們關注的焦點了。(上一篇@Autowired 詳解)
上面概念中咱們講到InitiaizingBean接口,它的實現Bean會在容器完成屬性注入後執行一個自定義操做,這不就知足initializeBean方法的執行喚醒嘛,咱們來看它的實現:
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { invokeAwareMethods(beanName, bean); return null; } }, getAccessControlContext()); } else {//這裏檢測當前Bean是否實現一些列Aware接口,並調用相關方法,咱們不關心。 invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) {//BeanPostProcessor 的回調,不關心 wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { invokeInitMethods(beanName, wrappedBean, mbd);//這是咱們須要關心的,下面看下它的實現 } if (mbd == null || !mbd.isSynthetic()) {//BeanPostProcessor 的回調,不關心 wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
咱們接着來看下invokeInitMethods方法的實現:
protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd) throws Throwable { //是不是InitializingBean的實例 boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if (System.getSecurityManager() != null) { try { AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { public Object run() throws Exception {//利用系統安全管理器調用 ((InitializingBean) bean).afterPropertiesSet(); return null; } }, getAccessControlContext()); } } else {//調用InitializingBean的afterPropertiesSet方法。 ((InitializingBean) bean).afterPropertiesSet(); } } //調用自定義初始化方法。。。省略,不關心 }
上一篇關於<mvc:annotation-driven/>的文章,咱們說過了,當在配置文件中加上該標記後,Spring(3.1後)會默認爲咱們註冊RequestMappingHandlerMapping
等Bean定義。而RequestMappingHandlerMapping
實現了InitializingBean接口,所以,在初始化並裝配該Bean實例時,執行到上述代碼是,便會執行他的afterPropertySet方法。咱們接下來看看他的afterPropertySet方法:
public void afterPropertiesSet() { initHandlerMethods(); } //Scan beans in the ApplicationContext, detect and register handler methods. protected void initHandlerMethods() { //掃描全部註冊的Bean String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : getApplicationContext().getBeanNamesForType(Object.class)); //遍歷這些Bean,依次判斷是不是處理器,並檢測其HandlerMethod for (String beanName : beanNames) { if (isHandler(getApplicationContext().getType(beanName))){ detectHandlerMethods(beanName); } } //這個方法是個空實現,無論他 handlerMethodsInitialized(getHandlerMethods()); }
它直接調用了initHandlerMethods()方法,而且該方法被描述爲:掃描ApplicationContext中的beans,檢測並註冊處理器方法。we are close。
咱們再看它是怎樣判斷是不是處理器的,以及怎麼detect Handler Methods 的:
@Override protected boolean isHandler(Class<?> beanType) { return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) || (AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null)); }
啊哈,很簡單,就是看看有沒有被@Controller或者@RequestMapping註解標記
protected void detectHandlerMethods(final Object handler) { Class<?> handlerType = (handler instanceof String) ? getApplicationContext().getType((String) handler) : handler.getClass(); final Class<?> userType = ClassUtils.getUserClass(handlerType); Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter(){ public boolean matches(Method method) {//只選擇被@RequestMapping標記的方法 return getMappingForMethod(method, userType) != null; } }); for (Method method : methods) { //根據方法上的@RequestMapping來建立RequestMappingInfo實例。 T mapping = getMappingForMethod(method, userType); //註冊請求映射 registerHandlerMethod(handler, method, mapping); } }
整個的檢測過程大體清楚了:1)遍歷Handler中的全部方法,找出其中被@RequestMapping註解標記的方法。2)而後遍歷這些方法,生成RequestMappingInfo實例。3)將RequestMappingInfo實例以及處理器方法註冊到緩存中。
下面咱們看看細節:
@Override protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { RequestMappingInfo info = null; //獲取方法method上的@RequestMapping實例。 RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class); if (methodAnnotation != null) {//方法被註解了 RequestCondition<?> methodCondition = getCustomMethodCondition(method);//始終返回null info = createRequestMappingInfo(methodAnnotation, methodCondition);//建立MappingInfo //檢查方法所屬的類有沒有@RequestMapping註解 RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class); if (typeAnnotation != null) {//有類層次的@RequestMapping註解 RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);//null //將類層次的RequestMapping和方法級別的RequestMapping結合 info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info); } } return info; }
很清晰吧,先獲取方法上的@RequestMapping信息,而後獲取類級別上的@RequestMapping 信息,而後將二者結合,這裏咱們有必要再瞭解下怎樣建立RequestMappingInfo對象的(包括他的內部結構),以及怎樣將類級別的request mapping信息和方法級別的進行結合的?
private RequestMappingInfo createRequestMappingInfo(RequestMapping annotation, RequestCondition<?> customCondition) { return new RequestMappingInfo( new PatternsRequestCondition(annotation.value(), getUrlPathHelper(), getPathMatcher(), this.useSuffixPatternMatch, this.useTrailingSlashMatch, this.fileExtensions), new RequestMethodsRequestCondition(annotation.method()), new ParamsRequestCondition(annotation.params()), new HeadersRequestCondition(annotation.headers()), new ConsumesRequestCondition(annotation.consumes(), annotation.headers()), new ProducesRequestCondition(annotation.produces(), annotation.headers(), getContentNegotiationManager()), customCondition ); }
其中涉及到了幾個類,咱們大體瞭解下含義:
PatternRequestCondition 它其實就是URL模式的封裝,它包含了一個URL模式的Set集合。其實就是@RequestMapping註解中的value值得封裝。
RequestMethodRequestCondition 它是@RequestMapping 註解中method屬性的封裝
ParamsRequestCondition 它是@RequestMapping註解中params屬性的封裝
等等,依次類推。所以RequestMappingInfo其實就是對@RquestMapping 的封裝。
下面咱們再看看怎樣進行Combine的:
public RequestMappingInfo combine(RequestMappingInfo other) { PatternsRequestCondition patterns = this.patternsCondition.combine(other.patternsCondition); RequestMethodsRequestCondition methods = this.methodsCondition.combine(other.methodsCondition); ParamsRequestCondition params = this.paramsCondition.combine(other.paramsCondition); HeadersRequestCondition headers = this.headersCondition.combine(other.headersCondition); ConsumesRequestCondition consumes = this.consumesCondition.combine(other.consumesCondition); ProducesRequestCondition produces = this.producesCondition.combine(other.producesCondition); RequestConditionHolder custom = this.customConditionHolder.combine(other.customConditionHolder); return new RequestMappingInfo(patterns, methods, params, headers, consumes, produces, custom.getCondition()); }
很清晰,對每個元素都進行combine操做,咱們這裏只看PatternRequestCondition是怎麼結合的,就是看看怎樣合併url的。其餘沒太大必要。
public PatternsRequestCondition combine(PatternsRequestCondition other) { Set<String> result = new LinkedHashSet<String>(); if (!this.patterns.isEmpty() && !other.patterns.isEmpty()) { for (String pattern1 : this.patterns) { for (String pattern2 : other.patterns) { result.add(this.pathMatcher.combine(pattern1, pattern2)); } } } else if (!this.patterns.isEmpty()) { result.addAll(this.patterns); } else if (!other.patterns.isEmpty()) { result.addAll(other.patterns); } else { result.add(""); } return new PatternsRequestCondition(result, this.urlPathHelper, this.pathMatcher, this.useSuffixPatternMatch,this.useTrailingSlashMatch, this.fileExtensions); }
1)兩個pattern都存在是,調用PathMatcher的combine方法合併兩個pattern。
2)只有一個有時,使用這個。
3)兩個都沒有時,爲空「」。
如今真正的url拼接是由PathMatcher來完成的了。咱們就不看他的代碼了就是一串if else的組合,重點是考慮進各類狀況,咱們來看下方法的註釋吧:
清晰,全面吧,有興趣的能夠看一下代碼,這裏不講了。
上面咱們已經講了@RequestMapping的檢測和處理,而且根據@RequestMapping生成了RequestMappingInfo實例,那Spring一定須要將這些信息保存起來,以處理咱們的請求。
第三節中咱們提到一個方法尚未分析,就是registerHandlerMethod 方法:
protected void registerHandlerMethod(Object handler, Method method, T mapping) { HandlerMethod handlerMethod; if (handler instanceof String) { String beanName = (String) handler; handlerMethod = new HandlerMethod(beanName, getApplicationContext(), method); } else { handlerMethod = new HandlerMethod(handler, method); } //上面幾行是根據新的處理器實例,方法實例,RequestMappingInfo來生成新的HandlerMethod實例 //下面是從緩存中查看是否有存在的HandlerMethod實例,若是有而且不相等則拋出異常 HandlerMethod oldHandlerMethod = handlerMethods.get(mapping); if (oldHandlerMethod != null && !oldHandlerMethod.equals(handlerMethod)) { throw new IllegalStateException(); } //handlerMethods 是一個Map鍵是RequestMappingInfo對象,值是HandlerMethod實例 //所以一個HandlerMethod實例可能處理多個mapping,而一個mapping實例只能由一個method處理 this.handlerMethods.put(mapping, handlerMethod); //這裏獲取mapping實例中的全部url。 Set<String> patterns = getMappingPathPatterns(mapping); for (String pattern : patterns) { if (!getPathMatcher().isPattern(pattern)) { //urlMap也是Map,鍵是url 模式,值是RequestMappingInfo實例 //所以一個mapping實例可能對應多個pattern,可是一個pattern只能對應一個mapping實例 this.urlMap.add(pattern, mapping); } } }
這裏可能稍微有點繞,其實道理很簡單,當請求到達時,去urlMap中需找匹配的url,以及獲取對應mapping實例,而後去handlerMethods中獲取匹配HandlerMethod實例。
篇幅有些長了,超出字數限制了,只能分紅兩篇了..........................
這章只分析了咱們前面三個問題中的第一個,可是已經至關接近了。下一篇咱們來說,Spring怎樣處理客戶發來的請求,以及方法調用的。