spingMVC模块handlerMapping的初始化过程

来源:互联网 发布:灯牌设计软件 编辑:程序博客网 时间:2024/04/29 08:28

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。
0 0