1. 程式人生 > >Spring MVC的handlermapping之BeanNameUrlHandlerMapping初始化

Spring MVC的handlermapping之BeanNameUrlHandlerMapping初始化

ger pin different final 解析 equal sina sed XML

先介紹一下:

  BeanNameUrlHandlerMapping是基於配置文件的方式; 所有處理器需要在XML文件中,以Bean的形式配置。

  缺點:配置繁瑣; 如果多個URL對應同一個處理器,那麽需要配置多條,同時也會實例化多個對象等等。。。

  因為springmvc 是基於spring的,所以他的初始化肯定是在spring容器初始化之後才進行的。

先上類圖:

  技術分享圖片

可以看到BeanNameUrlHandlerMapping父類最終實現了ApplicationContextAware接口,所以Spring容器會自動註入ApplicationContext,方法為:

 1     public
final void setApplicationContext(ApplicationContext context) throws BeansException { 2 if (context == null && !this.isContextRequired()) { 3 this.applicationContext = null; 4 this.messageSourceAccessor = null; 5 } else if (this.applicationContext == null
) { 6 if (!this.requiredContextClass().isInstance(context)) { 7 throw new ApplicationContextException("Invalid application context: needs to be of type [" + this.requiredContextClass().getName() + "]"); 8 } 9 10 this.applicationContext = context;
11 this.messageSourceAccessor = new MessageSourceAccessor(context); 12 this.initApplicationContext(context); //這塊實際上是一個鉤子方法,供子類去覆蓋! 進行初始化工作 13 } else if (this.applicationContext != context) { 14 throw new ApplicationContextException("Cannot reinitialize with different application context: current one is [" + this.applicationContext + "], passed-in one is [" + context + "]"); 15 } 16 17 }

AbstractHandlerMapping: 這個類就是復寫了這個方法 進行了攔截器的初始化

    protected void initApplicationContext() throws BeansException {
        this.extendInterceptors(this.interceptors); //供子類擴展攔截器
        this.detectMappedInterceptors(this.mappedInterceptors);//掃描應用下的MappedInterceptor,並添加到mappedInterceptors
        this.initInterceptors();//歸集MappedInterceptor,並適配HandlerInterceptor和WebRequestInterceptor
    }

AbstractDetectingUrlHandlerMapping :同樣重寫這個方法,實現自己的邏輯

 1     public void initApplicationContext() throws ApplicationContextException {
 2         super.initApplicationContext();
 3         this.detectHandlers();
 4     }
 5     
 6     protected void detectHandlers() throws BeansException {
 7         if (this.logger.isDebugEnabled()) {
 8             this.logger.debug("Looking for URL mappings in application context: " + this.getApplicationContext());
 9         }
10      // 掃描應用下所有的Object類
11         String[] beanNames = this.detectHandlersInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.getApplicationContext(), Object.class) : this.getApplicationContext().getBeanNamesForType(Object.class);
12         String[] arr$ = beanNames;
13         int len$ = beanNames.length;
14 
15         for(int i$ = 0; i$ < len$; ++i$) { //遍歷每一個掃描出來的類
16             String beanName = arr$[i$];
17             String[] urls = this.determineUrlsForHandler(beanName); //鉤子方法,讓子類去實現,通過handler解析url
18             if (!ObjectUtils.isEmpty(urls)) {
19                 this.registerHandler(urls, beanName); //返回的URL進行註冊,實際上就是放到AbstractUrlHandlerMapping的一個map中
20             } else if (this.logger.isDebugEnabled()) {
21                 this.logger.debug("Rejected bean name ‘" + beanName + "‘: no URL paths identified");
22             }
23         }
24 
25     }

BeanNameUrlHandlerMapping:實際上就實現了determineUrlsForHandler這個方法:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.web.servlet.handler;

import java.util.ArrayList;
import java.util.List;
import org.springframework.util.StringUtils;

public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
    public BeanNameUrlHandlerMapping() {
    }

    protected String[] determineUrlsForHandler(String beanName) {
        List<String> urls = new ArrayList();
        if (beanName.startsWith("/")) { //只有一點需要註意 就是bean id 必須是以‘/‘開頭
            urls.add(beanName);
        }

        String[] aliases = this.getApplicationContext().getAliases(beanName);
        String[] arr$ = aliases;
        int len$ = aliases.length;

        for(int i$ = 0; i$ < len$; ++i$) {
            String alias = arr$[i$];
            if (alias.startsWith("/")) {
                urls.add(alias);
            }
        }

        return StringUtils.toStringArray(urls);
    }
}

AbstractUrlHandlerMappin:中註冊處理器的方法:

 1     protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
 2         Assert.notNull(urlPath, "URL path must not be null");
 3         Assert.notNull(handler, "Handler object must not be null");
 4         Object resolvedHandler = handler;
 5         if (!this.lazyInitHandlers && handler instanceof String) {
 6             String handlerName = (String)handler;
 7             if (this.getApplicationContext().isSingleton(handlerName)) {
 8                 resolvedHandler = this.getApplicationContext().getBean(handlerName);
 9             }
10         }
11 
12         Object mappedHandler = this.handlerMap.get(urlPath);
13         if (mappedHandler != null) {
14             if (mappedHandler != resolvedHandler) { //不允許存在相同url不同handler,否則拋異常
15                 throw new IllegalStateException("Cannot map " + this.getHandlerDescription(handler) + " to URL path [" + urlPath + "]: There is already " + this.getHandlerDescription(mappedHandler) + " mapped.");
16             }
17         } else if (urlPath.equals("/")) {
18             if (this.logger.isInfoEnabled()) {
19                 this.logger.info("Root mapping to " + this.getHandlerDescription(handler));
20             }
21 
22             this.setRootHandler(resolvedHandler);
23         } else if (urlPath.equals("/*")) {
24             if (this.logger.isInfoEnabled()) {
25                 this.logger.info("Default mapping to " + this.getHandlerDescription(handler));
26             }
27 
28             this.setDefaultHandler(resolvedHandler);
29         } else { //這才是正常的存儲邏輯
30             this.handlerMap.put(urlPath, resolvedHandler);
31             if (this.logger.isInfoEnabled()) {
32                 this.logger.info("Mapped URL path [" + urlPath + "] onto " + this.getHandlerDescription(handler));
33             }
34         }
35 
36     }

到這裏實際上處理器映射器的保存工作就算完事了。

實際上handlerMapping這一塊,主要思路就是 寫一個模板類,來處理公共的方法,如初始化攔截器,然後留下鉤子方法,讓子類去實現自己的邏輯就好了。

Spring MVC的handlermapping之BeanNameUrlHandlerMapping初始化