Spring MVC的handlermapping之BeanNameUrlHandlerMapping初始化

Posted 郝二驴

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring MVC的handlermapping之BeanNameUrlHandlerMapping初始化相关的知识,希望对你有一定的参考价值。

先介绍一下:

  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初始化的主要内容,如果未能解决你的问题,请参考以下文章

spring mvc 关键接口 HandlerMapping HandlerAdapter

Spring MVC 梳理 - 四种HandlerMapping

Spring MVC:handlermapping

Spring MVC 学习笔记 handlerMapping和handlerAdapter

Spring MVC的handlermapping之RequestMappingHandlerMapping初始化

Spring mvc之源码 handlerMapping和handlerAdapter分析