10.springMVC中的拦截器

springMVC中的拦截器

一、拦截器的作用

SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。

  1. 拦截器链】(Interceptor Chain): 将拦截器按一定的顺序连接成一条链。在访问被拦截的方法或者字段时,拦截器链中的拦截器就会按照之前定义的顺序被调用。

  2. 过滤器与拦截器的区别

拦截器是AOP思想的具体应用。

过滤器 拦截器
servlet规范中的一部分,任何java web工程都可以使用 拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用
在url-pattern中配置了/*之后,可以对所有要访问的资源进行拦截 拦截器只会拦截访问的控制器方法, 如果访问的是jsp/html/css/image/js是不会进行拦截的

二、自定义拦截器的步骤

想要自定义拦截器,必须实现 HandlerInterceptor 接口。

1). 编写一个普通类实现HandlerInterceptor接口

/** 自定义拦截器 */
public class HanderInterceptorOne implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        System.out.println("preHandle拦截器拦截了");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response,
                           Object handler,
                           ModelAndView modelAndView) {
        System.out.println("postHandle方法执行了");
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler,
                                Exception ex) {
        System.out.println("afterCompletion方法执行了");
    }
}

2). 配置拦截器

配置springMVC.xml

<!-- 配置拦截器 -->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean id="handlerInterceptorOne"
              class="com.itheima.myInterceptor.HanderInterceptorOne"/>
    </mvc:interceptor>
</mvc:interceptors>

3). 【*】测试运行结果【下面所有测试的运行的代码都使用这一套

  1. 访问的index.jsp
<%-- 测试拦截器 --%>
<a href="${pageContext.request.contextPath}/interceptor/function">拦截器测试</a>
  1. 控制器代码
/** 测试拦截器的控制器 */
@Controller
@RequestMapping("/interceptor")
public class InterceptorController {

    @RequestMapping("/function")
    public String testFunction() {
        System.out.println("控制器中的方法执行了");
        return "success";
    }
}
  1. 运行结果
preHandle拦截器拦截了
控制器中的方法执行了
postHandle方法执行了
afterCompletion方法执行了

三、拦截器的细节

1). 拦截器的放行

放行的含义是指,如果有下一个拦截器就执行下一个,如果该拦截器处于拦截器链的最后一个,则执行控制器中的方法。


拦截器放行.png

2). 拦截器中方法的说明

拦截器HandlerInterceptor接口中的三个方法详解。

1. boolean preHandle 方法

  1. 方法源码
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    return true;
}
  1. 如何调用:按照拦截器定义顺序调用

  2. 何时调用:只要配置了都会被调用

  3. 逻辑功能:如果开发者决定该拦截器对请求拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回true。如果开发者决定不需要再调用其他的组件去处理请求,则返回false。

2. void postHandle 方法

  1. 方法源码
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {}
  1. 如何调用:按照拦截器定义逆序调用

  2. 何时调用:拦截器链内所有拦截器返回成功时调用

  3. 逻辑功能:在业务处理器处理完请求后,但是DispatcherServlet向客户端返回响应前被调用,在该方法中对用户请求request进行处理?

3. void afterCompletion 方法

  1. 方法源码
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {}
  1. 如何调用:按拦截器定义逆序调用

  2. 何时调用:只有对应的preHandle返回true才会被调用

  3. 逻辑功能在DispatcherServlet完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作。

3). 拦截器的作用路径

作用路径可以通过在配置文件中配置。可以配置多个

<!-- 配置拦截器的作用范围 -->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        -- 用于指定拦截的url,可以配置多个

        <mvc:exclude-mapping path="/xxx"/>
        -- 用于指定排除的url,可以配置多个

        <bean id="handlerInterceptorOne"
              class="com.itheima.myInterceptor.HanderInterceptorOne"/>
    </mvc:interceptor>
</mvc:interceptors>

4). 多个拦截器的执行顺序

多个拦截器是按照配置的顺序决定的,下面以先后配置HandlerInterceptorDemo1/HandlerInterceptorDemo2为例:


多个拦截器中方法执行顺序及条件总览图.png

四、正常流程测试案例

1). 拦截器1代码

public class HandlerInterceptorDemo1 implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        System.out.println("拦截器1:preHandle拦截器拦截了");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        System.out.println("拦截器1:postHandle方法在DispatcherServlet响应前执行了");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        System.out.println("拦截器1:afterCompletion方法在DispatcherServlet响应后执行了");
    }
}

2). 拦截器2代码

将拦截器中的输出语句中的拦截器1换成拦截器2,类名换成HandlerInterceptorDemo2

3). 配置文件

<!-- 正常执行的拦截器配置,按照配置的顺序执行 -->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean id="handlerInterceptorDemo1" class="com.itheima.myInterceptor.HandlerInterceptorDemo1"/>
    </mvc:interceptor>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean id="handlerInterceptorDemo2" class="com.itheima.myInterceptor.HandlerInterceptorDemo2"/>
    </mvc:interceptor>
</mvc:interceptors>

4). 测试代码

访问的index.jsp代码和控制器代码同上【*】测试运行结果...

5). 执行结果

拦截器1:preHandle拦截器拦截了
拦截器2:preHandle拦截器拦截了
控制器中的方法执行了
拦截器2:postHandle方法在DispatcherServlet响应前执行了
拦截器1:postHandle方法在DispatcherServlet响应前执行了
拦截器2:afterCompletion方法在DispatcherServlet响应后执行了
拦截器1:afterCompletion方法在DispatcherServlet响应后执行了

五、中断流程测试

1). 拦截器1代码

同上一个案例的拦截器1的代码

2). 拦截器2代码

public class HandlerInterceptorDemo2 implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        System.out.println("拦截器2:preHandle拦截器拦截了");
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        System.out.println("拦截器2:postHandle方法在DispatcherServlet响应前执行了");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        System.out.println("拦截器2:afterCompletion方法在DispatcherServlet响应后执行了");
    }
}

3). 配置文件

同上一个案例的配置文件

4). 测试代码

访问的index.jsp代码和控制器代码同上【*】测试运行结果...

5). 执行结果

拦截器1:preHandle拦截器拦截了
拦截器2:preHandle拦截器拦截了
拦截器1:afterCompletion方法在DispatcherServlet响应后执行了

六、拦截器的简单案例

验证用户是否登录

1). 实现思路

  1. 有一个登陆页面,需要写一个controller访问页面。
  2. 登陆页面有一提交表单的动作。需要在controller中处理。
    1. 判断用户名密码是否正确。
    2. 如果正确,向session中写入用户信息。
    3. 返回登陆成功。
  3. 拦截用户请求,判断用户是否登陆。
    1. 如果用户已经登陆。放行
    2. 如果用户未登陆,跳转到登陆页面

2). 控制器代码

@Controller
@RequestMapping("/user")
public class UserController {

    /** 跳转到登陆页面 */
    @RequestMapping("/jumplogin")
    public String jumpLogin(Model model) throws Exception {
        return "login";
    }

    /** 登陆提交 */
    @RequestMapping("/login")
    public String login(HttpSession session, String userid, String pwd) throws Exception {
        // 向session记录用户身份信息
        session.setAttribute("user", userid);
        return "success";
    }

    /** 退出登陆 */
    @RequestMapping("logout")
    public String logout(HttpSession session) throws Exception {
        // session 过期
        session.invalidate();

        return "login";
    }
}

3). 拦截器代码

public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
        // 如果是登陆页面则放行
        System.out.println("uri: " + request.getRequestURI());
        if (request.getRequestURI().contains("login")) {
            return true;
        }

        HttpSession session = request.getSession();

        // 如果用户已登陆也放行
        if(session.getAttribute("user") != null) {
            return true;
        }

        // 用户没有登陆跳转到登陆页面
        request.getRequestDispatcher("/WEB-INF/pages/login.jsp").forward(request, response);
        return false;
    }
}

4). springMVC.xml配置

<!-- 登陆案例的拦截器 -->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean id="loginInterceptor" class="com.itheima.myInterceptor.LoginInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

5). 登陆页面login.jsp

<form action="${pageContext.request.contextPath}/user/login">
    用户名:<input type="text" name="userid"> <br>
    密码: <input type="password" name="pwd"> <br>
    <input type="submit" value="提交">
</form>

6). 测试访问代码

  1. index.jsp
<%-- 拦截器的提交表单登陆测试 --%>
<a href="${pageContext.request.contextPath}/hello/hello">其他页面访问</a>
  1. 控制器示例
@Controller
@RequestMapping(path = "/hello")
public class HelloController {

    @RequestMapping(path = "/hello", method = RequestMethod.GET)
    public String loginOne() {
        System.out.println("hello");
        return "success";
    }
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Springmvc的处理器拦截器类似于Servlet 开发中的过滤器Filter,用于对处理器进行预处理和后处理。...
    Cherry300阅读 489评论 0 1
  • 对于java中的思考的方向,1必须要看前端的页面,对于前端的页面基本的逻辑,如果能理解最好,不理解也要知道几点。 ...
    神尤鲁道夫阅读 849评论 0 0
  • title: SSM注解开发的高级知识讲解tags: SSMcategories: SSM 若图片无法显示,请前往...
    codingXiaxw阅读 1,841评论 0 17
  • 拦截器是Struts2框架的核心,它主要完成解析请求参数、将请求参数赋值给Action属性、执行数据校验、文件上传...
    重山杨阅读 4,017评论 2 13
  • 昨天你为什么给他那么多溢美之词,或者说,你犯得着那么做吗?
    CNBLUEone阅读 112评论 0 0