轉載地址 :http://blog.csdn.net/j080624/article/details/56278461
為了降低文章篇幅,使得文章更目標化,簡潔化,我們就不例舉各種@RequestMapping的用法等內容了.
文章主要說明以下問題:
-
-
Spring怎樣處理@RequestMapping(怎樣將請求路徑映射到控制器類或方法)
-
Spring怎樣將請求分派給正確的控制器類或方法
-
Spring如何實現靈活的控制器方法的
-
在Spring MVC 3.1 之前的版本中,Spring默認使用 DefaultAnnotationHandlerMapping,AnnotationMethodHandlerAdapter來處理 @RequestMapping注解和請求方法調用,而從3.1開始提供了一組新的API完成這些工作。相比之下,新的API更加的合理完善,開放,易拓 展,面向對象。這篇文章便是基於3.1的新API進行剖析的。
一、概念解析
在開始之前我們先了解下新的API中引入的新接口或者類,這會有助於后面的處理過程的理解。不得不說新的API提供了更多漂亮的抽象,你能感受到面向對象的魅力。
-
RequestMappingInfo 這個類是對請求映射的一個抽象,它包含了請求路徑,請求方法,請求頭等信息。其實可以看做是@RequestMapping的一個對應類。
-
HandlerMethod這個類封裝了處理器實例(Controller Bean)和 處理方法實例(Method)以及方法參數數組(MethodParameter[])
-
MethodParameter 這個類從2.0就有了,它封裝了方法某個參數的相關信息及行為,如該參數的索引,該參數所屬方法實例或構造器實例,該參數的類型等。
-
HandlerMapping 該接口的實現類用來定義請求和處理器之前的映射關系,其中只定義了一個方法getHandler。
-
AbstractHandlerMethodMapping 這是HandlerMapping的一個基本實現類,該類定義了請求與HandlerMethod實例的映射關系。
-
RequestMappingInfoHandlerMapping這個是AbstractHandlerMethodMapping的實現類,他維護了一個RequestMappingInfo和HandlerMethod的Map屬性。
-
RequestMappingHandlerMapping 這個是RequestMappingInfoHandlerMapping的子類,它將@RequestMapping注解轉化為RequestMappingInfo實例,並為父類使用。也就是我們處理@RequestMapping的終點。
-
InitializingBean 這個接口定義了其實現Bean在容器完成屬性設置后可以執行自定義初始化操作,我們的AbstractHandlerMethodMapping便實現了這個接口,並且定義了一組自定義操作,就是用來檢測處理我們的@RequestMapping注解。
概念講的太多總不是什么好事。但明白了上述概念基本上就成功一半了,其中的實現相對@Autowired那篇簡單多了。
二、InitialiZingBean.afterPropertySet()
我們從頭開始,看看到底Spring是怎樣檢測並處理我們@RequestMapping注解的。不知大家還記不記的這段代碼:
- Object exposedObject = bean;
- try {
- populateBean(beanName, mbd, instanceWrapper);
- if (exposedObject != null) {
- exposedObject = initializeBean(beanName, exposedObject, mbd);
- }
- }
這是BeanFactory創建Bean過程中需要執行的一段代碼,其中populateBean方法便是@Autowired注解的處理過程,執行的屬性的自動注入等操作。因為initializeBean方法當時與主題無關沒有講,不過這時它便是我們關注的焦點了。
上面概念中我們講到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。
三、檢測@RequestMapping
我們再看它是怎樣判斷是否是處理器的,以及怎么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());
- }<span style="white-space:pre;"> </span>
很清晰,對每一個元素都進行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怎樣處理客戶發來的請求,以及方法調用的。