spring Handler 映射

Spring 的前几个版本,开发者都需要在 web 应用的上下文定义(多个) HandlerMapping bean,用来将 web 请求映射到指定的 handler。可当引入注解控制器时,开发者基本不再需要这样配置。因为 RequestMappingHandlerMapping 会自动寻找所有 @Controller bean 中的 @RequestMapping 注解。另外提醒一下,所有从 AbstractHandlerMapping 继承过来的 HandlerMapping 类,都以通过设置以下属性来自定义其行为:

  • interceptors 拦截器链。 HandlerInterceptors 会在 Section 16.4.1, “使用 HandlerInterceptor 拦截请求” 谈论。
  • defaultHandler 默认 handler。此 handler 不影响其他 handler 的使用。
  • order order 属性 (可查看 org.springframework.core.Ordered 接口), Spring会对可匹配的 handler 进行排序,并应用第一个匹配到 handler。
  • alwaysUseFullPath 当此属性为 true 时,Spring 会使用当前 Servlet 上下文的全路径去寻找合适的 handler。当为 false 时(默认值),Spring 会使用相对路径来寻找合适的 handler。举个例子,当 某个 Servlet 映射 /testing/* 请求时,若 alwaysUseFullPath 属性为 true,会使用 /testing/viewPage.html;反之,使用 /viewPage.html
  • urlDecode 从 Spring 2.5 开始,此属性默认为 true。如果你更需要编码路基路径,可将此属性设置为 true。然而,HttpServletRequest 总会暴露解码后的 Servlet 路径。另外注意的是,当比较编码后的路径时,Servlet 路径是不会再匹配的。

如下例子,演示了如何配置一个拦截器:

<beans>
    <bean id="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
        <property name="interceptors">
            <bean class="example.MyInterceptor"/>
        </property>
    </bean>
<beans>

16.4.1 使用 HandlerInterceptor 拦截请求

Spring 的 handler 映射机制包含了 handler 拦截器。使用handler 拦截器,可以在某些的请求中应用的特殊的功能,比如说,检查权限。

handler 映射的拦截器必须实现 HandlerInterceptor 接口(此节接口位于 org.springframework .web.servlet 包中)。这个接口定义了三个方法:preHandle(..)在 handler 执行前调用;postHandle(..) 在handler 执行后调用;afterCompletion(..) 在整一个请求完成后调用。这三个方法基本足够应对各种预处理和后处理的状况。

preHandle(..) 方法返回一个 boolean 值。你可以使用这个方法来中断或继续处理 handler 执行链。当此方法返回 true 时,hadler 执行链会继续执行;反之,DispatcherServlet 会认为此拦截器已处理完成该请求(和渲染一个视图),之后不再执行余下的拦截器,也不在执行 handler 执行链。

可以使用 interceptors 属性配置拦截器。所有从 AbstractHandlerMapping 继承过来的 HandlerMapping 类都拥有此属性。演示例子如下:

<beans>
    <bean id="handlerMapping"
            class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
        <property name="interceptors">
            <list>
                <ref bean="officeHoursInterceptor"/>
            </list>
        </property>
    </bean>

    <bean id="officeHoursInterceptor"
            class="samples.TimeBasedAccessInterceptor">
        <property name="openingTime" value="9"/>
        <property name="closingTime" value="18"/>
    </bean>
<beans>
package samples;

public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {

    private int openingTime;
    private int closingTime;

    public void setOpeningTime(int openingTime) {
        this.openingTime = openingTime;
    }

    public void setClosingTime(int closingTime) {
        this.closingTime = closingTime;
    }

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
            Object handler) throws Exception {
        Calendar cal = Calendar.getInstance();
        int hour = cal.get(HOUR_OF_DAY);
        if (openingTime <= hour && hour < closingTime) {
            return true;
        }
        response.sendRedirect("http://host.com/outsideOfficeHours.html");
        return false;
    }
}

通过这样的配置,所有请求 handler 都会被 TimeBasedAccessInterceptor 拦截。如果当前时间是下班时间,用户会重定向到一个静态页面,换句话说就是,你只能在上班时间访问该网站。

[提示]

当使用 RequestMappingHandlerMapping 时,真实的 handler 是一个HandlerMethod 实例,该实例指定了会被调用的控制器方法。

如你所见,Spring 的适配器类 HandlerInterceptorAdapter,使继承 HandlerInterceptor 接口变得更加简单。

[Tip]

在上述例子中,所配置的拦截器会应用到所有带注解的请求处理器。如果需要缩窄拦截器的拦截 url 路径范围,可以使用 MVC 命名空间或 MVC Java 配置,或声明 MappedInterceptor 类型的 bean 来缩窄拦截器的拦截范围,详情可参考 Section 16.16.1, “启用 MVC Java 配置或 MVC XML 命名空间”

注意,HandlerInterceptor 的 postHandle 方法不一定适用于@ResponseBodyResponseEntity方法。在这种情况下,HttpMessageConverter 实例会在 postHandle 方法执行之前就将数据写到 response 并提交 response,所以 postHandle 方法不可能再处理 response(如添加一个 Header)。相反,应用程序可以实现 ResponseBodyAdvice ,将其声明为 @ControllerAdvice bean 或将其直接在 RequestMappingHandlerAdapter 中配置它。


原文地址:https://www.cnblogs.com/chenny3/p/10226176.html