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

来源:互联网 发布:工业管道绘图软件 编辑:程序博客网 时间:2024/05/23 00:35

    要了解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 {@Overrideprotected 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 {//获取内部HandlerObject handler = getHandlerInternal(request);//内部Handler不存在的情况下获取默认handlerif (handler == null) {handler = getDefaultHandler();}if (handler == null) {return null;}//如果返回的是一个bean name则转化成一个beanif (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/bbbString lookupPath = getUrlPathHelper().getLookupPathForRequest(request);//根据request的url获取handler,这里的handler就是一个controller。Spring的在创建ApplicationContext时会//根据Controller的注解或bean定义以<url,controller>的键值对的形式将所有controller的路径映射信息保存到一个map中//lookupHandler会利用url从map中get到这个controllerObject handler = lookupHandler(lookupPath, request);if (handler == null) {Object rawHandler = null;//如果访问web应用首页映射到根目录处理器if ("/".equals(lookupPath)) {rawHandler = getRootHandler();}//如果没有controller则使用默认controllerif (rawHandler == null) {rawHandler = getDefaultHandler();}if (rawHandler != null) {//如果是bean name转化成controllerif (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则返回Adapterif (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的classClass<?> 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和responseServletWebRequest 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就正确地匹配到了具体的方法中了。



 

0 0
原创粉丝点击