了解拦截器?

本文详细介绍了SpringMVC中的拦截器,包括其概念、与过滤器的区别、执行时机和应用场景。通过一个入门案例展示了如何创建和配置自定义拦截器,并探讨了多拦截器链的执行顺序。此外,还提供了一个登录验证的开发示例,说明了拦截器在权限控制中的应用。
摘要由CSDN通过智能技术生成

目录

1. 拦截器介绍

1.1 什么是拦截器

1.2 与web过滤器的区别

1.3 执行时机

1.3 应用场景

2. 入门案例

2.1 创建自定义拦截器

2.2 配置文件

2.3 创建一个Controller

3. 多连接器

3.1 创建拦截器

3.2 配置文件

3.3 查看执行结果

4. 开发示例


1. 拦截器介绍

1.1 什么是拦截器

SpringMVC的处理器拦截器,类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。
依赖于web框架,在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个 controller生命周期之内可以多次调用。

1.2 与web过滤器的区别

依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等。

过滤器:

  • 依赖与servlet容器,基于在实现上基于函数回调,可以对几乎所有请求进行过滤
  • 基于servlet标准,只要是web工程即可使用,通用性强
  • 过滤器的出现早已拦截器

拦截器:

  • 属于springmvc技术,必须依赖于springmvc环境采用使用
  • springmvc拦截器通常对处理器(controller)进行拦截
  • 拦截器只能拦截dispatcherServlet处理的请求
  • 可以使用spring提供的容器,及强大的依赖注入

1.3 执行时机

来自官方文档): 执行处理程序之前的拦截点。在HandlerMapping确定适当的处理程序对象之后,但在HandlerAdapter调用处理程序之前调用。DispatcherServlet处理执行链中的处理程序,该执行链由任意数量的拦截器组成,处理程序本身位于末尾。使用此方法,每个拦截器可以决定中止执行链,通常发送HTTP错误或写入自定义响应。

1.3 应用场景

1)日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等。
2)权限检查:如登录检测,进入处理器检测是否登录,如果没有直接返回到登录页面;
3)性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录);
4)通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个Controller中的处理方法都需要的,我们就可以使用拦截器实现。

2. 入门案例

2.1 创建自定义拦截器

public class CustomInterceptor implements HandlerInterceptor {


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

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("CustomInterceptor  postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("CustomInterceptor  afterCompletion");
    }
}

关于拦截器接口中定义的方法的说明:

  • preHandle方法
    作用:用于对拦截到的请求进行预处理,方法接收布尔(true,false)类型的返回值,返回true:放行,false:不放行。
    执行时机:在处理器方法执行前执行
    方法参数:
    1)request请求对象
    2)response响应对象
    3)handler拦截到的方法处理

  • postHandle方法
    作用:用于对拦截到的请求进行后处理,可以在方法中对模型数据和视图进行修改
    执行时机:在处理器的方法执行后,视图渲染之前
    方法参数:
    1)request请求对象
    2)response响应对象
    3)handler拦截到的处理器方法
    4)ModelAndView处理器方法返回的模型和视图对象,可以在方法中修改模型和视图

  • afterCompletion方法
    作用:用于在整个流程完成之后进行最后的处理,如果请求流程中有异常,可以在方法中获取对象
    执行时机:视图渲染完成后(整个流程结束之后)
    方法参数:
    1)request请求参数
    2)response响应对象
    3)handler拦截到的处理器方法
    4)ex异常对象

拦截器的处理流程

 

为更好的理解拦截器接口中定义的方法的参数,可以使用如下代码:

   @Override
   public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
       if(handler instanceof HandlerMethod){
           HandlerMethod handlerMethod = (HandlerMethod) handler;
           log.info("当前拦截的方法为:{}",handlerMethod.getMethod().getName());
           log.info("当前拦截的方法参数长度为:{}",handlerMethod.getMethod().getParameters().length);
           log.info("当前拦截的Bean为:{}",handlerMethod.getBean().getClass().getName());
           System.out.println("开始拦截---------");
           String uri = request.getRequestURI();
           System.out.println("拦截的uri:"+uri);
       }
       log.info("视图名称:"+modelAndView.getViewName());
       log.info("传给视图的模式:"+modelAndView.getModel().toString());
       System.out.println("CustomInterceptor02 postHandle");
   }

2.2 配置文件

在spring-mvc.xml文件中配置拦截器

    <mvc:interceptors>
        <mvc:interceptor>
            <!-- 该拦截器会拦截所有的请求 -->
            <mvc:mapping path="/**"/>
            <bean class="com.zking.mybatisdemo.interceptor.CustomInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

2.3 创建一个Controller

如果已经有Controller可以不创建,只是为了测试。例如下面的运行输出:

 

3. 多连接器

拦截器链的概念:如果多个拦截器能够对相同的请求进行拦截,则多个拦截器会形成一个拦截器链,主要理解拦截器链中各个拦截器的执行顺序。拦截器链中多个拦截器的执行顺序,根拦截器的配置顺序有关,先配置的先执行。

3.1 创建拦截器

public class CustomInterceptor02 implements HandlerInterceptor {

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

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("CustomInterceptor02 postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("CustomInterceptor02 afterCompletion");
    }

}

3.2 配置文件

配置spring-mvc.xml文件

    <mvc:interceptors>
        <mvc:interceptor>
            <!-- 该拦截器会拦截所有的请求 -->
            <mvc:mapping path="/**"/>
            <bean class="com.zking.mybatisdemo.interceptor.CustomInterceptor"/>
        </mvc:interceptor>
        <mvc:interceptor>
            <!-- 该拦截器会拦截指定的请求 -->
            <mvc:mapping path="/find/**"/>
            <bean class="com.zking.mybatisdemo.interceptor.CustomInterceptor02"/>
        </mvc:interceptor>
    </mvc:interceptors>

注意:为更好的查看结果,在测试用的Controller中请求路径包含find。

3.3 查看执行结果

 

4. 开发示例

登录验证, 开发步骤:

  • 开发登录功能,当用户登录成功,将用户记入session
  • 开发拦截器,如果用户访问除登录功能之外的其他资源,则验证是否登录,否则不允许访问

1) 登录页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <c:if test="${not empty error}">
        <div>
            ${error}
        </div>
    </c:if>
    <form action="<%=request.getContextPath()%>/user/login" method="post">
        用户名: <input type="text" name="name"> <br data-tomark-pass>        密码: <input type="password" name="password"><br data-tomark-pass>        <input type="submit" value="提交">
    </form>
</body>
</html>

2)登录Controller

@Controller
public class LoginController {

    @RequestMapping("/")
    public String loginPage() {
        return "login";
    }

    @PostMapping("/user/login")
    public String login(String name, String password, HttpSession session, Model model) {

        if("admin".equals(name) && "123".equals(password)) {
            session.setAttribute("user", name);
            return "index";
        }
        model.addAttribute("error", "用户名或密码错误");
        return "login";
    }

    @GetMapping("/user/logout")
    public String logout(HttpSession session) {
        session.removeAttribute("user");
        return "redirect:/";
    }
}

3)编写index.jsp页面,可以是任何内容,可用于演示即可。 在index中加入退出系统的代码(也是为方便演示)

<a href="<%=request.getContextPath()%>/user/logout">退出</a>

测试可以登录后,可以进行拦截器的编写了

4)拦截器的编写,参考代码

public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String uri = request.getRequestURI();
        String contextPath = request.getContextPath();

        //登录页面,放行
        if (uri.endsWith(contextPath+"/") || uri.indexOf("/user/login") > 0) {
            return true;
        }

        //其他情况需要验证登录
        HttpSession session = request.getSession();
        Object user = session.getAttribute("user");

        //已登录,放行
        if (Objects.nonNull(user)) {
            return true;
        }

        session.setAttribute("error", "请先登录");
        //request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);
        response.sendRedirect(contextPath+"/");

        //不再处理其他拦截器,直接返回
        return false;
    }

}

5) 将拦截器配置到spring-mvc.xml

    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.zking.mybatisdemo.interceptor.LoginInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

6)讨论: 对应登录验证这样的需求,filter和springmvc拦截器均可以满足,那个更合适? 为什么?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值