Spring Framework源码(十一):SpringMVC之URL匹配

    要了解SpringMVC中数据是怎么绑定到Controller的参数上的之前我们需要知道SpringMVC是从哪里开始解析数据的。首先我们回顾下DispatcherServlet中的doDispatch中的如下代码:   

mappedHandler = getHandler(processedRequest);
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

    首先SpringMVC要充HandlerMapping中获取对应的HandlerExecutionChain类型的handler,那么这是怎么获取的呢?我们以SpringMVC使用的默认HandlerMapping即org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping为例进行讲解。

public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
	@Override
	protected String[] determineUrlsForHandler(String beanName) {
		List<String> urls = new ArrayList<String>();
		if (beanName.startsWith("/")) {
			urls.add(beanName);
		}
		String[] aliases = getApplicationContext().getAliases(beanName);
		for (String alias : aliases) {
			if (alias.startsWith("/")) {
				urls.add(alias);
			}
		}
		return StringUtils.toStringArray(urls);
	}
}

      BeanNameUrlHandlerMapping仅仅实现了一个determineUrlsForHandler方法。在此之前它在doDispatch方法中被调用的getHandler方法是在父类AbstractHandlerMapping中实现的。

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		//获取内部Handler
		Object handler = getHandlerInternal(request);
		//内部Handler不存在的情况下获取默认handler
		if (handler == null) {
			handler = getDefaultHandler();
		}
		if (handler == null) {
			return null;
		}
		//如果返回的是一个bean name则转化成一个bean
		if (handler instanceof String) {
			String handlerName = (String) handler;
			handler = getApplicationContext().getBean(handlerName);
		}
		//根据获取的handler封装成一个执行链,这个执行链实际是一个由N个interceptor和一个controller组成的链式结构
		return getHandlerExecutionChain(handler, request);
	}

     子类AbstractUrlHandlerMapping中的getHandlerInternal(HttpServletRequest)方法。

	protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
		//没配置使用全路径的情况下获取相对路径如http://www.xxx.com/path/aaa/bbb的相对路径是/path/aaa/bbb
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		//根据request的url获取handler,这里的handler就是一个controller。Spring的在创建ApplicationContext时会
		//根据Controller的注解或bean定义以<url,controller>的键值对的形式将所有controller的路径映射信息保存到一个map中
		//lookupHandler会利用url从map中get到这个controller
		Object handler = lookupHandler(lookupPath, request);
		if (handler == null) {
			Object rawHandler = null;
			//如果访问web应用首页映射到根目录处理器
			if ("/".equals(lookupPath)) {
				rawHandler = getRootHandler();
			}
			//如果没有controller则使用默认controller
			if (rawHandler == null) {
				rawHandler = getDefaultHandler();
			}
			if (rawHandler != null) {
				//如果是bean name转化成controller
				if (rawHandler instanceof String) {
					String handlerName = (String) rawHandler;
					rawHandler = getApplicationContext().getBean(handlerName);
				}
				validateHandler(rawHandler, request);
				//增加一个过滤器,用于暴露URI_TEMPLATE_VARIABLES_ATTRIBUTE属性
				handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
			}
		}
		//省略代码若干...
		return handler;
	}

    看完以上代码我们大致了解了在执行完getHandler后我们得到了一个由N个interceptor和一个controller构成的HandlerExecutionChain,接着我们要找到对应的HandlerAdapter

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		for (HandlerAdapter ha : this.handlerAdapters) {
			if (logger.isTraceEnabled()) {
				logger.trace("Testing handler adapter [" + ha + "]");
			}
			//如果该种Adapter支持Handler则返回Adapter
			if (ha.supports(handler)) {
				return ha;
			}
		}
		throw new ServletException("No adapter for handler [" + handler +
				"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
	}

    它会挨个调用默认的Adapter的support方法判断是否支持该种handler如果支持则返回。返回Adapter后会直接调用handle方法获取ModelAndView。由于通常我们是使用注解来标示控制器的,这里我们以org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter为例来看看Adapter是怎么形式其责任的。

public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
                //获取controller的class
		Class<?> clazz = ClassUtils.getUserClass(handler);
                //获取注解信息
		Boolean annotatedWithSessionAttributes = this.sessionAnnotatedClassesCache.get(clazz);
		if (annotatedWithSessionAttributes == null) {
			annotatedWithSessionAttributes = (AnnotationUtils.findAnnotation(clazz, SessionAttributes.class) != null);
			this.sessionAnnotatedClassesCache.put(clazz, annotatedWithSessionAttributes);
		}

		if (annotatedWithSessionAttributes) {
			// Always prevent caching in case of session attribute management.
			checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
			// Prepare cached set of session attributes names.
		}
		else {
			// Uses configured default cacheSeconds setting.
			checkAndPrepare(request, response, true);
		}

		// Execute invokeHandlerMethod in synchronized block if required.
		if (this.synchronizeOnSession) {
			HttpSession session = request.getSession(false);
			if (session != null) {
				Object mutex = WebUtils.getSessionMutex(session);
				synchronized (mutex) {
					return invokeHandlerMethod(request, response, handler);
				}
			}
		}
                //调用注解下面的方法
		return invokeHandlerMethod(request, response, handler);
	}

	protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
                //获取方法处理器
		ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
                //获取controller相应的方法
		Method handlerMethod = methodResolver.resolveHandlerMethod(request);
		ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
                //包装request和response
		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		ExtendedModelMap implicitModel = new BindingAwareModelMap();
                //调用方法
		Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
		//返回ModelAndView对象
                ModelAndView mav =
				methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
		methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
		return mav;
	}

          这样用户传入的url就正确地匹配到了具体的方法中了。



 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值