码迷,mamicode.com
首页 > 移动开发 > 详细

[Java]SpringMVC工作原理之二:HandlerMapping和HandlerAdapter

时间:2017-10-18 12:33:00      阅读:178      评论:0      收藏:0      [点我收藏+]

标签:throws   get   struts2   cut   抽象方法   zed   last   tps   tcl   

一、HandlerMapping

作用是根据当前请求的找到对应的 Handler,并将 Handler(执行程序)与一堆 HandlerInterceptor(拦截器)封装到 HandlerExecutionChain 对象中,在 HandlerMapping 接口的内部只有一个方法,如下:

  • HandlerExecutionChain getHandler(HttpServletRequest request);

上面说到的 Handler 有可能是一个 HandlerMethod(封装了 Controller 中的方法)对象,也有可能是一个 Controller 对象、 HttpRequestHandler 对象或 Servlet 对象,而这个 Handler 具体是什么对象,是与所使用的 HandlerMapping 实现类有关。如下图所示,可以看到它有两个分支,分别继承自 AbstractHandlerMethodMapping(得到 HandlerMethod)和 AbstractUrlHandlerMapping(得到 HttpRequestHandler、Controller 或 Servlet),它们又统一继承于 AbstractHandlerMapping。

技术分享

先来看一下 AbstractHandlerMapping,它实现了 HandlerMapping 接口中的 getHandler() 方法,源码如下所示

@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    // 根据请求获取执行程序,具体的获取方式由子类决定,getHandlerInternal() 是抽象方法
    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);
    }
    // 将 Handler 与一堆拦截器包装到 HandlerExecutionChain 对象中
    return getHandlerExecutionChain(handler, request);
}

可以看到在这个方法中又调用了 getHandlerInternal() 方法获取到了 Handler 对象,而 Handler 对象具体内容是由它的子类去定义的。下面就来一看下 AbstractHandlerMapping 的两个分支子类

1 AbstractUrlHandlerMapping

AbstractUrlHandlerMapping 这个分支获取的 Handler 的类型实际就是一个 Controller 类,所以一个 Controller 只能对应一个请求(或者像 Struts2 那样定位到方法,使同一个业务的方法放在同一个类里),源码如下所示

protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
    // 根据当前请求获取“查找路径”
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    // 根据路径获取 Handler(即Controller),先尝试直接匹配,再尝试模式匹配
    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;
}

1.1 AbstractUrlHandlerMapping 实现类及使用

技术分享

1) ControllerClassNameHandlerMapping:根据类名访问 Controller。

<!-- 注册 HandlerMapping -->
<bean class="org.springframework.web.servlet.handler.ControllerClassNameHandlerMapping" />
<!-- 注册 Handler -->
<bean class="com.controller.TestController" />

2) ControllerBeanNameHandlerMapping:根据 Bean 名访问 Controller,与 BeanNameUrlHandlerMapping 类似,但是bean名称不用遵循URL公约。

<!-- 注册 HandlerMapping -->
<bean class="org.springframework.web.servlet.handler.ControllerBeanNameHandlerMapping" />
<!-- 注册 Handler -->
<bean id="test" class="com.controller.TestController" />

3) BeanNameUrlHandlerMapping:利用 BeanName 来作为 URL 使用。

<!-- 注册 HandlerMapping -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
<!-- 注册 Handler -->
<bean id="/test.do" class="com.controller.TestController" />

4) SimpleUrlHandlerMapping:可以将 URL 与处理器的定义分离,还可以对 URL 进行统一的映射管理。

<!-- 注册 HandlerMapping -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
        <props>
            <prop key="/test.do">testController</prop>
            <prop key="/hello.do">testController</prop>
        </props>
    </property>
</bean>
<!-- 注册 Handler -->
<bean id="testController" class="com.controller.TestController" />

1.2 Controller 类

使用 AbstractUrlHandlerMapping 的实现类时,需要让控制层的类实现 Controller 接口(一般继承 AbstractController 即可),另外还有一些已经实现了的 Controller 类,如下图所示。但是不论是自己实现 Controller 接口还是使用系统已经实现的类,都只能处理一个请求(除了 MultiActionController 可以通过参数的方式让一个类可以处理多个请求)。

技术分享

另外下面所有的 Controller 均采用 SimpleUrlHandlerMapping 方式的。

1) UrlFilenameViewController:用于跳转界面,控制器根据请求的URL直接解析出视图名,省去了自己实现 Ccntroller 跳转页面。

<bean id="indexController" class="org.springframework.web.servlet.mvc.UrlFilenameViewController" />

2) ParameterizableViewController:同样用于界面跳转,控制器根据配置的参数来跳转界面,使用方式如下

<bean id="indexController" class="org.springframework.web.servlet.mvc.ParameterizableViewController">
     <property name="viewName" value="/index.jsp" />
</bean>

3) ServletForwardingController:将请求转发到 Servlet,使用方式如下

<bean id="indexController" class="org.springframework.web.servlet.mvc.ServletForwardingController">    
    <property name="servletName" value="indexServlet" />    
</bean> 

另外还要在 web.xml 中配置要转发到的 Servlet

<servlet>    
    <servlet-name>indexServlet</servlet-name>    
    <servlet-class>com.servlet.ServletForwarding</servlet-class>    
</servlet>   

4) ServletWrappingController:将某个 Servlet 包装为 Controller,所有到 ServletWrappingController 的请求实际上是由它内部所包装的这个 Servlet 实例来处理的,这样可以将这个 Servlet 隐藏起来

5) MultiActionController:一个 Controller 可以写多个方法,分别对应不同的请求,使同一业务的方法可以放在一起了。在使用时让自己的 Controller 类继承 MultiActionController 类,使用方式如下

public class IndexController extends MultiActionController {  
    public ModelAndView add(HttpServletRequest request,HttpServletResponse response) {  
        ModelAndView mv = new ModelAndView();   
        mv.addObject("message","add");   
        mv.setViewName("add");   
        return mv;   
    }  
    public ModelAndView delete(HttpServletRequest request,HttpServletResponse response) {  
        ModelAndView mv = new ModelAndView();   
        mv.addObject("message","delete");   
        mv.setViewName("delete");   
        return mv;   
    }  
}  

配置自己的 Controller 时要配置一个方法名解析器(默认是 InternalPathMethodNameResolver )

<bean id="indexController" class="com.controller.IndexController">  
      <property name="methodNameResolver">  
        <!-- InternalPathMethodNameResolver 根据请求路径解析执行方法名
             ParameterMethodNameResolver 根据参数解析执行方法名
             PropertiesMethodNameResolver 根据 key/value 列表解析执行方法名 -->
        <bean class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver">  
           <!-- 指定参数名为action -->  
           <property name="paramName" value="action" />  
        </bean>  
      </property>  
</bean>  

当我们访问 http://localhost:8080/***/indexAction.do?action=add 时,进入 add() 方法;

当我们访问 http://localhost:8080/***/indexAction.do?action=delete 时,进入 delete() 方法。

 

2 AbstractHandlerMethodMapping

AbstractHandlerMethodMapping 这个分支获取的 Handler 的类型是 HandlerMethod,即这个 Handler 是一个方法,它保存了方法的信息(如Method),这样一个 Controller 就可以处理多个请求了,源码如下所示

@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    // 根据当前请求获取“查找路径”
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    // 获取当前请求最佳匹配的处理方法(即Controller类的方法中)
    HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
    return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}

看完上述代码后有一个疑问,HandlerMethod 是怎么创建的(即怎么把 Controller 的方法变成了它),继续看一下源码,如下所示

protected void initHandlerMethods() {
    // 获取了所有已经注册的 Bean 的名称
    String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
          BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
          getApplicationContext().getBeanNamesForType(Object.class));
    for (String beanName : beanNames) {
        // isHandler()方法由子类实现,判断这个 Bean 是否拥有 @Controller 注解或 @RequestMapping 注解
        if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX) && isHandler(getApplicationContext().getType(beanName))){
            // 在这个 Bean 中查找 HandlerMethod 并添加到 Map 中
            detectHandlerMethods(beanName);
        }
    }
    handlerMethodsInitialized(getHandlerMethods());
}

看完上述代码后,可以知道是在 detectHandlerMethods() 方法中将指定 Bean 的方法转换为 HandlerMethod 对象,具体实现如下

protected void detectHandlerMethods(final Object handler) {
    Class<?> handlerType = (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());
    // Avoid repeated calls to getMappingForMethod which would rebuild RequestMappingInfo instances
    final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
    final Class<?> userType = ClassUtils.getUserClass(handlerType);
    Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
        @Override
        public boolean matches(Method method) {
            T mapping = getMappingForMethod(method, userType);
            if (mapping != null) {
                mappings.put(method, mapping);
                return true;
            } else {
                return false;
            }
        }
    });
    for (Method method : methods) {
        registerHandlerMethod(handler, method, mappings.get(method));
    }
}

上述代码通过 HandlerMethodSelector.selectMethods() 方法获取了 java.lang.reflect.Method 对象,即将 Bean 的方法利用反射获取出来,然后将其包装到 HandlerMethod 对象中。

1.1 AbstractHandlerMapping 实现类及使用

技术分享

AbstractHandlerMapping 只有一个实现类 RequestMappingHandlerMapping

 

二、HandlerAdapter

根据 Handler 来找到支持它的 HandlerAdapter,通过 HandlerAdapter 执行这个 Handler 得到 ModelAndView 对象。HandlerAdapter 接口中的方法如下:

  • boolean supports(Object handler); // 当前 HandlerAdapter 是否支持这个 Handler
  • ModelAndView handle(HttpServletRequest req, HttpServletResponse res, Object handler); // 利用 Handler 处理请求
  • long getLastModified(HttpServletRequest request, Object handler);

技术分享

1 RequestMappingHandlerAdapter

从上面的文章中可以知道,利用 RequestMappingHandlerMapping 获取的 Handler 是 HadnlerMethod 类型,它代表 Controller 里要执行的方法,而 RequestMappingHandlerAdapter 可以执行 HadnlerMethod 对象。

RequestMappingHandlerAdapter 的 handle() 方法是在它的父类 AbstractHandlerMethodAdapter 类中实现的,源码如下所示

@Override
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    return handleInternal(request, response, (HandlerMethod) handler);
}

handleInternal() 方法是由 RequestMappingHandlerAdapter 自己来实现的,源码如下所示

@Override
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    // 是否通过 @SessionAttributes 注释声明了 session 属性。
    if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {management.
        checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
    } else {
        checkAndPrepare(request, response, true);
    }
    // 是否需要在 synchronize 块中执行
    if (this.synchronizeOnSession) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized (mutex) {
                // 执行 HandlerMethod
                return invokeHandleMethod(request, response, handlerMethod);
            }
        }
    }
    // 执行 HandlerMethod,得到 ModelAndView
    return invokeHandleMethod(request, response, handlerMethod);
}

 

2 HttpRequestHandlerAdapter

HttpRequestHandlerAdapter 可以执行 HttpRequestHandler 类型的 Handler,源码如下

@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    ((HttpRequestHandler) handler).handleRequest(request, response);
    return null;
}

 

3 SimpleControllerHandlerAdapter

SimpleControllerHandlerAdapter 可以执行 Controller 类型的 Handler,源码如下

@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    return ((Controller) handler).handleRequest(request, response);
}

 

4 SimpleServletHandlerAdapter 

SimpleServletHandlerAdapter 可以执行 Servlet 类型的 Handler,源码如下

@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    ((Servlet) handler).service(request, response);
    return null;
}

 

三、HandlerExceptionResolver

负责处理异常的类,负责根据异常来设置 ModelAndView,然后交由 render 渲染界面。HandlerExecptionResolver 接口中只有一个方法,如下:

  • ModelAndView resolveException(HttpServletRequest req, HttpServletResponse res, Object handler, Exception ex); 

 

[Java]SpringMVC工作原理之二:HandlerMapping和HandlerAdapter

标签:throws   get   struts2   cut   抽象方法   zed   last   tps   tcl   

原文地址:http://www.cnblogs.com/tengyunhao/p/7658952.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!