java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > springmvc组件HandlerMapping

springmvc组件中的HandlerMapping解析

作者:还没秃的小菜鸡

这篇文章主要介绍了springmvc九大组件中的HandlerMapping解析,HandlerMapping表示的是一个URL与一个Handler(可以简单的理解为Controller中有@RequestMapping注解的方法)之间的映射关系,需要的朋友可以参考下

HandlerMapping

HandlerMapping在Spring MVC框架的jar包下面,他是处理映射器,为用户发送的请求找到合适的Handler Adapter,它将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器)对象,同时通过这种策略模式,很容易添加新的映射策略。SpringMVC在请求到handler处理器的分发这步就是通过HandlerMapping模块解决的,handlerMapping还处理拦截器,同时Spring MVC也提供了一系列HandlerMapping的实现,根据一定的规则选择controller。

public interface HandlerMapping {
   // 返回请求的一个处理程序handler和拦截器interceptors
   @Nullable
   HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

HandlerMapping执行流程如下:

private void initHandlerMappings(ApplicationContext context) {
    this.handlerMappings = null;
    //默认为true,可通过DispatcherServlet的init-param参数进行设置
    if (this.detectAllHandlerMappings) {
        //在ApplicationContext中找到所有的handlerMapping, 包括父级上下文
        Map<String, HandlerMapping> matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.handlerMappings = new ArrayList<>(matchingBeans.values());
            //排序,可通过指定order属性进行设置,order的值为int型,数越小优先级越高
            AnnotationAwareOrderComparator.sort(this.handlerMappings);
        }
    } else {
        try {
            //从ApplicationContext中取id(或name)="handlerMapping"的bean,此时为空
            HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
            // 将hm转换成list,并赋值给属性handlerMappings
            this.handlerMappings = Collections.singletonList(hm);
        } catch (NoSuchBeanDefinitionException ex) {
        }
    }
    //如果没有自定义则使用默认的handlerMappings
    //默认的HandlerMapping在DispatcherServlet.properties属性文件中定义,
    // 该文件是在DispatcherServlet的static静态代码块中加载的
    // 默认的是:BeanNameUrlHandlerMapping和RequestMappingHandlerMapping
    if (this.handlerMappings == null) {
        this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);//默认策略
        if (logger.isTraceEnabled()) {
            logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
                    "': using default strategies from DispatcherServlet.properties");
        }
    }
}
  1. 如果缓存中存在,则从缓存中获取,并进行排序
  2. 从ApplicationContext中取id(或name)="handlerMapping"的bean
  3. 如果没有自定义则使用默认的handlerMappings
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
    String key = strategyInterface.getName();//HandlerMappings
    //获取HandlerMappings为key的value值,defaultStrategies在static块中读取DispatcherServlet.properties
    String value = defaultStrategies.getProperty(key);
    if (value != null) {
        //逗号,分割成数组
        String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
        List<T> strategies = new ArrayList<>(classNames.length);
        for (String className : classNames) {
            try {
                //反射加载并存储strategies
                Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
                //通过容器创建bean 触发后置处理器    
                Object strategy = createDefaultStrategy(context, clazz);
                strategies.add((T) strategy);
            }
     。。。
        }
        return strategies;
    }
    else {
        return new LinkedList<>();
    }
}
protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) {
    //通过容器创建bean
    return context.getAutowireCapableBeanFactory().createBean(clazz);
}
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
   org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
   org.springframework.web.servlet.function.support.RouterFunctionMapping

HandlerMapping是处理器映射器,根据请求找到处理器Handler,但并不是简单的返回处理器,而是将处理器和拦截器封装,形成一个处理器执行链(HandlerExecuteChain)。

AbstractHandlerMapping

获取处理器执行链的过程在AbstractHandlerMapping中的getHandler方法

protected void initApplicationContext() throws BeansException {
	//模板方法,用于给子类提供一个添加Interceptors的入口。
	extendInterceptors(this.interceptors);
	//将SpringMvc容器和父容器中所有的MappedInterceptor类型的Bean添加到mappedInterceptors的属性
	detectMappedInterceptors(this.mappedInterceptors);
    //用于初始化Interceptor,将Interceptors属性里所包含的对象按类型添加到mappedInterceptors和adaptedInterceptors.
	initInterceptors();
}

AbstractHandlerMapping通过initApplicationContext()方法进行自动初始化,它的创建其实就是初始化上面三个interceptor,其一般是由父类执行ApplicationContextAware#setApplicationContext()方法间接调用,主要是获取springmvc上下文中的拦截器集合MappedInterceptor。

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	//getHandlerInternal(request)方法为抽象方法,供子类实现
	//获取到的handler对象一般为bean/HandlerMethod
	Object handler = getHandlerInternal(request);
	//上述找不到则使用默认的处理类,没有设定则返回null,则会返回前台404错误
	if (handler == null) {
		handler = getDefaultHandler();
	}
	if (handler == null) {
		return null;
	}
	// Bean name or resolved handler?
	if (handler instanceof String) {
		String handlerName = (String) handler;
		handler = getApplicationContext().getBean(handlerName);
	}
	//创建处理链对象
	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
	//针对cros跨域请求的处理,此处就不分析了
	if (CorsUtils.isCorsRequest(request)) {
		CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
		CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
		CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
		executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
	}
	return executionChain;
}
//针对HandlerMethod的获取
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
	//获取访问的路径,一般类似于request.getServletPath()返回不含contextPath的访问路径
	String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
	//获取读锁
	this.mappingRegistry.acquireReadLock();
	try {
		//获取HandlerMethod作为handler对象,这里涉及到路径匹配的优先级
		//优先级:精确匹配>最长路径匹配>扩展名匹配
		HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
		//HandlerMethod内部含有bean对象,其实指的是对应的Controller
		return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
	}
	finally {
		//释放读锁
		this.mappingRegistry.releaseReadLock();
	}
}
//针对beanName的获取
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
	String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
	//从handlerMap查找路径对应的beanName
	Object handler = lookupHandler(lookupPath, request);
	if (handler == null) {
		// We need to care for the default handler directly, since we need to
		// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
		Object rawHandler = null;
		if ("/".equals(lookupPath)) {
			rawHandler = getRootHandler();
		}
		if (rawHandler == null) {
			rawHandler = getDefaultHandler();
		}
		if (rawHandler != null) {
			// Bean name or resolved handler?
			if (rawHandler instanceof String) {
				String handlerName = (String) rawHandler;
				rawHandler = getApplicationContext().getBean(handlerName);
			}
			validateHandler(rawHandler, request);
			handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
		}
	}
	return handler;
}

HandlerMapping是通过getHandler方法来获取处理器Handler和拦截器Interceptors的,AbstractHandlerMapping实现获取handler。

在获取handler对象时是由其子类实现的,分别为AbstractHandlerMethodMapping,AbstractUrlHandlerMapping

//容器启动时会运行此方法,完成handlerMethod的注册操作  
@Override  
public void afterPropertiesSet() {  
	initHandlerMethods();  
}
protected void initHandlerMethods() {
   //获取上下文中所有bean的name,不包含父容器
   for (String beanName : getCandidateBeanNames()) {
      if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
         processCandidateBean(beanName);
      }
   }
   //日志记录HandlerMethods的总数量
   handlerMethodsInitialized(getHandlerMethods());
}
protected void processCandidateBean(String beanName) {
   Class<?> beanType = null;
   try {
      //根据name找出bean的类型
      beanType = obtainApplicationContext().getType(beanName);
   }
   catch (Throwable ex) {
      // An unresolvable bean type, probably from a lazy bean - let's ignore it.
      if (logger.isTraceEnabled()) {
         logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
      }
   }
   //处理Controller和RequestMapping
   if (beanType != null && isHandler(beanType)) {
      detectHandlerMethods(beanName);
   }
}
//整个controller类的解析过程
protected void detectHandlerMethods(Object handler) {
   //根据name找出bean的类型
   Class<?> handlerType = (handler instanceof String ?
         obtainApplicationContext().getType((String) handler) : handler.getClass());
   if (handlerType != null) {
      //获取真实的controller,如果是代理类获取父类
      Class<?> userType = ClassUtils.getUserClass(handlerType);
      //对真实的controller所有的方法进行解析和处理  key为方法对象,T为注解封装后的对象RequestMappingInfo
      Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
            (MethodIntrospector.MetadataLookup<T>) method -> {
               try {
                  // 调用子类RequestMappingHandlerMapping的getMappingForMethod方法进行处理,
                  // 即根据RequestMapping注解信息创建匹配条件RequestMappingInfo对象
                  return getMappingForMethod(method, userType);
               }
               catch (Throwable ex) {
                  throw new IllegalStateException("Invalid mapping on handler class [" +
                        userType.getName() + "]: " + method, ex);
               }
            });
      if (logger.isTraceEnabled()) {
         logger.trace(formatMappings(userType, methods));
      }
      methods.forEach((method, mapping) -> {
         //找出controller中可外部调用的方法
         Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
         //注册处理方法
         registerHandlerMethod(handler, invocableMethod, mapping);
      });
   }
}
后续主要是通过RequestMappingHandlerMapping  完成:
  1. 通过方法isHandler(beanType)判断是否处理Controller和RequestMapping
  2. 调用子类RequestMappingHandlerMapping的getMappingForMethod方法进行处理,即根据RequestMapping注解信息创建匹配条件RequestMappingInfo对象getMappingForMethod(method, userType);
  3. 注册处理方法registerHandlerMethod(handler, invocableMethod, mapping)
//注册url和Bean的map,将具体的Handler注入到url对应的map中
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
   Assert.notNull(urlPath, "URL path must not be null");
   Assert.notNull(handler, "Handler object must not be null");
   Object resolvedHandler = handler;
   // Eagerly resolve handler if referencing singleton via name.
   // 不是懒加载,默认为false,即不是,通过配置SimpleUrlHandlerMapping属性lazyInitHandlers的值进行控制
   // 如果不是懒加载并且handler为单例,即从上下文中查询实例处理,此时resolvedHandler为handler实例对象;
   // 如果是懒加载或者handler不是单例,即resolvedHandler为handler逻辑名
   if (!this.lazyInitHandlers && handler instanceof String) {
      String handlerName = (String) handler;
      ApplicationContext applicationContext = obtainApplicationContext();
      // 如果handler是单例,通过bean的scope控制
      if (applicationContext.isSingleton(handlerName)) {
         resolvedHandler = applicationContext.getBean(handlerName);
      }
   }
   Object mappedHandler = this.handlerMap.get(urlPath);
   if (mappedHandler != null) {
      if (mappedHandler != resolvedHandler) {
         throw new IllegalStateException(
               "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
               "]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
      }
   }
   else {
      if (urlPath.equals("/")) {
         if (logger.isTraceEnabled()) {
            logger.trace("Root mapping to " + getHandlerDescription(handler));
         }
         //"/"-->设置为roothandler
         setRootHandler(resolvedHandler);
      }
      else if (urlPath.equals("/*")) {
         if (logger.isTraceEnabled()) {
            logger.trace("Default mapping to " + getHandlerDescription(handler));
         }
         //对"/*"的匹配设置默认的handler
         setDefaultHandler(resolvedHandler);
      }
      else {
         //其余的路径绑定关系则存入handlerMap
         this.handlerMap.put(urlPath, resolvedHandler);
         if (logger.isTraceEnabled()) {
            logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));
         }
      }
   }
}

RequestMappingHandlerMapping

@Override
protected boolean isHandler(Class<?> beanType) {
   //获取@Controller和@RequestMapping
   return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
         AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
public static boolean hasAnnotation(AnnotatedElement element, Class<? extends Annotation> annotationType) {
		// Shortcut: directly present on the element, with no merging needed?
		if (AnnotationFilter.PLAIN.matches(annotationType) ||
				AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) {
			return element.isAnnotationPresent(annotationType);
		}
		// Exhaustive retrieval of merged annotations...
		return findAnnotations(element).isPresent(annotationType);
	}
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
   RequestMappingInfo info = createRequestMappingInfo(method);
   if (info != null) {
      //解析方法所在类上的requestMapping
      RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
      if (typeInfo != null) {
         info = typeInfo.combine(info);//合并类和方法上的路径,比如Controller类上有@RequestMapping("/demo"),方法的@RequestMapping("/demo1"),结果为"/demo/demo1"
      }
      String prefix = getPathPrefix(handlerType);//合并前缀
      if (prefix != null) {
         info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
      }
   }
   return info;
}
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
		//找到方法上的RequestMapping注解
		RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
		//获取自定义的类型条件(自定义的RequestMapping注解)
		RequestCondition<?> condition = (element instanceof Class ?
				getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
		return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
	}
public void register(T mapping, Object handler, Method method) {
   // Assert that the handler method is not a suspending one.
   if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
      throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
   }
   this.readWriteLock.writeLock().lock();
   try {
      //beanName和method封装成HandlerMethod对象
      HandlerMethod handlerMethod = createHandlerMethod(handler, method);
      //验证RequestMappingInfo是否有对应不同的method,有则抛出异常
      validateMethodMapping(handlerMethod, mapping);
      //RequestMappingInfo和handlerMethod绑定
      this.mappingLookup.put(mapping, handlerMethod);
      List<String> directUrls = getDirectUrls(mapping);//可以配置多个url
      for (String url : directUrls) {
         //url和RequestMappingInfo绑定   可以根据url找到RequestMappingInfo,再找到handlerMethod
         this.urlLookup.add(url, mapping);
      }
      String name = null;
      if (getNamingStrategy() != null) {
         name = getNamingStrategy().getName(handlerMethod, mapping);
         addMappingName(name, handlerMethod);//方法名和Method绑定
      }
      CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
      if (corsConfig != null) {
         this.corsLookup.put(handlerMethod, corsConfig);
      }
      this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
   }
   finally {
      this.readWriteLock.writeLock().unlock();
   }
}

BeanNameUrlHandlerMapping

public class HelloController implements Controller {
    public ModelAndView handleRequest(HttpServletRequest request,
                                      HttpServletResponse response) throws Exception {
        ModelAndView mav = new ModelAndView("index");
        mav.addObject("message", "默认的映射处理器示例");
        return mav;
    }
<bean id="resourceView"
    class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/"></property>
    <property name="suffix" value=".jsp"></property>
</bean>
<bean id="/name.do" class="com.zy.mvc.controller.HelloController"></bean>
//向上继承自ApplicationObjectSupport实现ApplicationContextAware接口
public abstract class ApplicationObjectSupport implements ApplicationContextAware {
    protected final Log logger = LogFactory.getLog(this.getClass());
    @Nullable
    private ApplicationContext applicationContext;
    ...
    public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException {
        if (context == null && !this.isContextRequired()) {
            this.applicationContext = null;
            this.messageSourceAccessor = null;
        } else if (this.applicationContext == null) {
            if (!this.requiredContextClass().isInstance(context)) {
                throw new ApplicationContextException("Invalid application context: needs to be of type [" + this.requiredContextClass().getName() + "]");
            }
            this.applicationContext = context;
            this.messageSourceAccessor = new MessageSourceAccessor(context);
            this.initApplicationContext(context);//模板方法,调子类
        } else if (this.applicationContext != context) {
            throw new ApplicationContextException("Cannot reinitialize with different application context: current one is [" + this.applicationContext + "], passed-in one is [" + context + "]");
        }
    }
    ...
}
	//AbstractDetectingUrlHandlerMapping
    @Override
    public void initApplicationContext() throws ApplicationContextException {
        super.initApplicationContext();//加载拦截器
        // 处理url和bean name,具体注册调用父类AbstractUrlHandlerMapping类完成
        detectHandlers();
    }
    //建立当前ApplicationContext中controller和url的对应关系
    protected void detectHandlers() throws BeansException {
        // 获取应用上下文
        ApplicationContext applicationContext = obtainApplicationContext();
        //获取ApplicationContext中的所有bean的name(也是id,即@Controller的属性值)
        String[] beanNames = (this.detectHandlersInAncestorContexts ?
                BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext,Object.class) : applicationContext.getBeanNamesForType(Object.class));
        //遍历所有beanName
        for (String beanName : beanNames) {
            // 通过模板方法模式调用BeanNameUrlHandlerMapping子类处理
            String[] urls = determineUrlsForHandler(beanName);//判断是否以/开始
            if (!ObjectUtils.isEmpty(urls)) {
                //调用父类AbstractUrlHandlerMapping将url与handler存入map
                registerHandler(urls, beanName);
            }
        }
    }
    protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
        Assert.notNull(urlPath, "URL path must not be null");
        Assert.notNull(handler, "Handler object must not be null");
        Object resolvedHandler = handler;
        // Eagerly resolve handler if referencing singleton via name.
        if (!this.lazyInitHandlers && handler instanceof String) {
            String handlerName = (String) handler;
            ApplicationContext applicationContext = obtainApplicationContext();
            if (applicationContext.isSingleton(handlerName)) {
                resolvedHandler = applicationContext.getBean(handlerName);
            }
        }
        //已存在且指向不同的Handler抛异常
        Object mappedHandler = this.handlerMap.get(urlPath);
        if (mappedHandler != null) {
            if (mappedHandler != resolvedHandler) {
                throw new IllegalStateException(
                        "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
                                "]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
            }
        }
        else {
            if (urlPath.equals("/")) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Root mapping to " + getHandlerDescription(handler));
                }
                setRootHandler(resolvedHandler);//设置根处理器,即请求/的时候
            }
            else if (urlPath.equals("/*")) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Default mapping to " + getHandlerDescription(handler));
                }
                setDefaultHandler(resolvedHandler);//默认处理器
            }
            else {
                this.handlerMap.put(urlPath, resolvedHandler);//注册进map
                if (logger.isTraceEnabled()) {
                    logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));
                }
            }
        }
    }

处理器bean的id/name为一个url请求路径,前面有"/"; 如果多个url映射同一个处理器bean,那么就需要定义多个bean,导致容器创建多个处理器实例,占用内存空间; 处理器bean定义与url请求耦合在一起。

SimpleUrlHandlerMapping

间接实现了org.springframework.web.servlet.HandlerMapping接口,直接实现该接口的是org.springframework.web.servlet.handler.AbstractHandlerMapping抽象类,映射Url与请求handler bean。支持映射bean实例和映射bean名称

public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {
    // 存储url和bean映射
    private final Map<String, Object> urlMap = new LinkedHashMap<>();
    // 注入property的name为mappings映射
    public void setMappings(Properties mappings) {
        CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap);
    }
    // 注入property的name为urlMap映射
    public void setUrlMap(Map<String, ?> urlMap) {
        this.urlMap.putAll(urlMap);
    }
    public Map<String, ?> getUrlMap() {
        return this.urlMap;
    }
    // 实例化本类实例入口
    @Override
    public void initApplicationContext() throws BeansException {
        // 调用父类AbstractHandlerMapping的initApplicationContext方法,只要完成拦截器的注册
        super.initApplicationContext();
        // 处理url和bean name,具体注册调用父类完成
        registerHandlers(this.urlMap);
    }
    // 注册映射关系,及将property中的值解析到map对象中,key为url,value为bean id或name
    protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
        if (urlMap.isEmpty()) {
            logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
        } else {
            urlMap.forEach((url, handler) -> {
                // 增加以"/"开头
                if (!url.startsWith("/")) {
                    url = "/" + url;
                }
                // 去除handler bean名称的空格
                if (handler instanceof String) {
                    handler = ((String) handler).trim();
                }
                // 调用父类AbstractUrlHandlerMapping完成映射
                registerHandler(url, handler);
            });
        }
    }
}

总结

在这里插入图片描述

到此这篇关于springmvc组件中的HandlerMapping解析的文章就介绍到这了,更多相关springmvc组件HandlerMapping内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文