spingMVC模块handlerMapping的初始化过程

spingMVC模块handlerMapping的初始化过程

一,请求的分发过程

    首先简单描述一下,请求的分发过程。一个请求到来,会走到DispatcherServlet的doDispatch方法。这个方法非常重要,封装了整个请求的分发过程,其中有一段代码如下:
//根据请求找到对应的handler       
mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}

// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
...... 调用拦截器等 ......
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
     1,mappedHandler = getHandler(processedRequest, false)这个方法根据request得到的是一个HandlerExecutionChain对象,他包含了mvc模块的拦截器即handlerInterceptor和真正处理请求的handler。这个方法最终调用的是下面的这个方法,它也在ispatcherServlet中:
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}

    终于今天要详细说明的主角来了,我们看到 HandlerExecutionChain 是从 handlerMappings中取得的,但是 handlerMappings是怎么来的呢?

二,DispatcherServlet中handlerMapping的初始化

  1. 在DispatcherServlet类下有这样一个方法
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;

if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
OrderComparator.sort(this.handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}

// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
}
}
}
        根据方法的名字可以猜出,它就是初始化handermapping的地方。而这个方法的逻辑也不是很复杂,就是向容器索要HandlerMapping的实现类。 可知这个时候handermapper的bean的定义已经在容器中了。但是,这段初始化的代码是怎么调起的呢?看下面的调用关系,原来是httpServletInit方法,它在servlet初始化的时候执行,负责context的建立以及相应的初始化工作。HandlerMapping的初始化要在context初始化之后(肯定要这样,不然还怎么向容器索要)。
    onRefresh方法(不同于 ApplicationContext 的onRefresh),它在servlet初始化的时候执行,但是要在context初始化之后:  
if (wac == null) {
wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
    // 这里才会出发HandlerMapping的初始化,而在这之间AppliContext已经建立
   onRefresh(wac);
}
    目前为止,handlerMapping的初始化过程已经了然。但是或许还有些疑问,请求是怎样根据地址分发的呢,请求地址跟请求处理器是的对应关系是怎样建立的呢?  

三,handleMapping在容器中的初始化过程


    我们在spring的配置文件中通常会加入这样一个标签  <mvc:annotation-driven />,用来支持基于注解的映射。而它的实现类是AnnotationDrivenBeanDefinitionParser,这个类又向容器中注册了RequestMappingHandlerMapping,他就是一个handlerMapping,dispatchServelet中用到的handlerMapping。

AnnotationDrivenBeanDefinitionParser的parse方法中的代码片段:
RootBeanDefinition methodMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
methodMappingDef.setSource(source);
methodMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
methodMappingDef.getPropertyValues().add("order", 0);
String methodMappingName = parserContext.getReaderContext().registerWithGeneratedName(methodMappingDef);
    RequestMappingHandlerMapping的继承关系如下  

    其中红框中的AbstractHandlerMapping实现了HandlerMapping接口,而它的子类 AbstractHandlerMethodMapping实现了容器的回调方法:afterPropertiesSet(),这就是HandlerMapping真正初始化的触发点(具体参见IOC实现源码):
public void afterPropertiesSet() {
initHandlerMethods();
}
protected void initHandlerMethods() {
if (logger.isDebugEnabled()) { logger.debug("Looking for request mappings in application context: " + getApplicationContext()); } String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : getApplicationContext().getBeanNamesForType(Object.class)); for (String beanName : beanNames) { if (isHandler(getApplicationContext().getType(beanName))){ detectHandlerMethods(beanName); } } handlerMethodsInitialized(getHandlerMethods()); }
        上面的代码就是处理controller中的各个方法的逻辑。首先得到所有的handler,对应开发者写的controller;然后查找每个handler中映射请求的方法;最后初始化这些映射方法。接下来看看是怎么查找并处理这些映射方法的。
	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) {
				return getMappingForMethod(method, userType) != null;
			}
		});

		for (Method method : methods) {
			//对于每个方法,通过注解等信息解析出映射信息,然后进行注册
	                T mapping = getMappingForMethod(method, userType);
registerHandlerMethod(handler, method, mapping);}}
   
   

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);
   }

   HandlerMethod oldHandlerMethod = handlerMethods.get(mapping);
   if (oldHandlerMethod != null && !oldHandlerMethod.equals(handlerMethod)) {
      throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + handlerMethod.getBean()
            + "' bean method \n" + handlerMethod + "\nto " + mapping + ": There is already '"
            + oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped.");
   }

   this.handlerMethods.put(mapping, handlerMethod);
   if (logger.isInfoEnabled()) {
      logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
   }

   Set<String> patterns = getMappingPathPatterns(mapping);
   for (String pattern : patterns) {
      if (!getPathMatcher().isPattern(pattern)) {
        //添加到urlMap中,查找时也会通过这个map查询
        this.urlMap.add(pattern, mapping);
} }}
    回头再来看看dispatchServlet中调用的hm.getHanler方法,它也是在AbstractHandlerMapping中定义的。
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   Object handler = getHandlerInternal(request);
   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);
   }
   return getHandlerExecutionChain(handler, request);
}

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
   HandlerExecutionChain chain = 
      (handler instanceof HandlerExecutionChain) ?
         (HandlerExecutionChain) handler : new HandlerExecutionChain(handler);
         
   chain.addInterceptors(getAdaptedInterceptors());
   
   String lookupPath = urlPathHelper.getLookupPathForRequest(request);
   for (MappedInterceptor mappedInterceptor : mappedInterceptors) {
      if (mappedInterceptor.matches(lookupPath, pathMatcher)) {
         chain.addInterceptor(mappedInterceptor.getInterceptor());
      }
   }
   
   return chain;
}

this all。
 
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值