Spring MVC的handlermapping之BeanNameUrlHandlerMapping初始化

一卷旌收千骑虏,万全身出百重围。这篇文章主要讲述Spring MVC的handlermapping之BeanNameUrlHandlerMapping初始化相关的知识,希望能为你提供帮助。
先介绍一下:
BeanNameUrlHandlerMapping是基于配置文件的方式; 所有处理器需要在XML文件中,以Bean的形式配置。
缺点:配置繁琐; 如果多个URL对应同一个处理器,那么需要配置多条,同时也会实例化多个对象等等。。。
因为springmvc 是基于spring的,所以他的初始化肯定是在spring容器初始化之后才进行的。
先上类图:

Spring MVC的handlermapping之BeanNameUrlHandlerMapping初始化

文章图片

可以看到BeanNameUrlHandlerMapping父类最终实现了ApplicationContextAware接口,所以Spring容器会自动注入ApplicationContext,方法为:
1public final void setApplicationContext(ApplicationContext context) throws BeansException { 2if (context == null & & !this.isContextRequired()) { 3this.applicationContext = null; 4this.messageSourceAccessor = null; 5} else if (this.applicationContext == null) { 6if (!this.requiredContextClass().isInstance(context)) { 7throw new ApplicationContextException("Invalid application context: needs to be of type [" + this.requiredContextClass().getName() + "]"); 8} 9 10this.applicationContext = context; 11this.messageSourceAccessor = new MessageSourceAccessor(context); 12this.initApplicationContext(context); //这块实际上是一个钩子方法,供子类去覆盖! 进行初始化工作 13} else if (this.applicationContext != context) { 14throw 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 :同样重写这个方法,实现自己的逻辑
1public void initApplicationContext() throws ApplicationContextException { 2super.initApplicationContext(); 3this.detectHandlers(); 4} 5 6protected void detectHandlers() throws BeansException { 7if (this.logger.isDebugEnabled()) { 8this.logger.debug("Looking for URL mappings in application context: " + this.getApplicationContext()); 9} 10// 扫描应用下所有的Object类 11String[] beanNames = this.detectHandlersInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.getApplicationContext(), Object.class) : this.getApplicationContext().getBeanNamesForType(Object.class); 12String[] arr$ = beanNames; 13int len$ = beanNames.length; 14 15for(int i$ = 0; i$ < len$; ++i$) { //遍历每一个扫描出来的类 16String beanName = arr$[i$]; 17String[] urls = this.determineUrlsForHandler(beanName); //钩子方法,让子类去实现,通过handler解析url 18if (!ObjectUtils.isEmpty(urls)) { 19this.registerHandler(urls, beanName); //返回的URL进行注册,实际上就是放到AbstractUrlHandlerMapping的一个map中 20} else if (this.logger.isDebugEnabled()) { 21this.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:中注册处理器的方法:
1protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException { 2Assert.notNull(urlPath, "URL path must not be null"); 3Assert.notNull(handler, "Handler object must not be null"); 4Object resolvedHandler = handler; 5if (!this.lazyInitHandlers & & handler instanceof String) { 6String handlerName = (String)handler; 7if (this.getApplicationContext().isSingleton(handlerName)) { 8resolvedHandler = this.getApplicationContext().getBean(handlerName); 9} 10} 11 12Object mappedHandler = this.handlerMap.get(urlPath); 13if (mappedHandler != null) { 14if (mappedHandler != resolvedHandler) { //不允许存在相同url不同handler,否则抛异常 15throw new IllegalStateException("Cannot map " + this.getHandlerDescription(handler) + " to URL path [" + urlPath + "]: There is already " + this.getHandlerDescription(mappedHandler) + " mapped."); 16} 17} else if (urlPath.equals("/")) { 18if (this.logger.isInfoEnabled()) { 19this.logger.info("Root mapping to " + this.getHandlerDescription(handler)); 20} 21 22this.setRootHandler(resolvedHandler); 23} else if (urlPath.equals("/*")) { 24if (this.logger.isInfoEnabled()) { 25this.logger.info("Default mapping to " + this.getHandlerDescription(handler)); 26} 27 28this.setDefaultHandler(resolvedHandler); 29} else { //这才是正常的存储逻辑 30this.handlerMap.put(urlPath, resolvedHandler); 31if (this.logger.isInfoEnabled()) { 32this.logger.info("Mapped URL path [" + urlPath + "] onto " + this.getHandlerDescription(handler)); 33} 34} 35 36}

【Spring MVC的handlermapping之BeanNameUrlHandlerMapping初始化】到这里实际上处理器映射器的保存工作就算完事了。 
实际上handlerMapping这一块,主要思路就是 写一个模板类,来处理公共的方法,如初始化拦截器,然后留下钩子方法,让子类去实现自己的逻辑就好了。

    推荐阅读