SpringMVC初级:拦截器实战

74 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第36天,点击查看活动详

1 (11).jpg

一、概述

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

  • 拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用
  • 拦截器只会拦截访问的控制器方法, 如果访问的是jsp/html/css/image/js是不会进行拦截的

自定义拦截器需要实现org.springframework.web.servlet.HandlerInterceptor接口。

HandlerInterceptor接口中定义了3个方法:

  •   preHandle():预处理方法,在目标方法执行前被执行。返回true表示拦截器放行;返回false表示不再继续执行后续的拦截器或目标方法,可以借助response参数给客户端返回应答,此后仅调用它之前拦截器的afterCompletion方法。

  • postHandle():后处理方法,在处理器方法之后,渲染视图之前被执行。可以通过modelAndView参数对模型数据或视图进行修改,modelAndView可能为null。

  •   afterCompletion():整个请求处理完毕后即视图渲染完毕时回调,可以进行资源清理。只有拦截器中preHandle方法返回true的拦截器的此方法才会被执行。

拦截器的配置:

拦截器都是基于HandlerMapping进行拦截的,可以将拦截器bean注入到映射器的interceptors属性中

推荐以下方式:

image.png

<mvc:mapping path="/**"/>表示拦截所有请求,<mvc:exclude-mapping path=""/>表示排除特定的请求。

面试题:拦截器和过滤器区别

  • 拦截器是基于java的反射机制的,而过滤器是基于函数回调。

  • 拦截器不依赖于servlet容器,过滤器依赖于servlet容器。

  • 拦截器可以获取IOC容器中的bean实现业务逻辑,过滤器不行,很重要。

触发时机不同:拦截器是被包裹在过滤器之中的,是spring容器的。过滤器是在请求进入容器后,进入servlet前进行预处理。

image.png

image.png

总结:
Spring的拦截器与Servlet的Filter二者都是AOP编程思想的体现,都能实现权限检查、日志记录等。不同的是:

  •  使用范围不同: Filter 是Servlet规范规定的,只能用于Web程序中。而拦截器既可以用于Web程序,也可以用于Application、Swing 程序中。

  •  规范不同: Filter是在Servlet 规范中定义的,是Servlet容器支持的。而拦截器是在Spring容器内的,是Spring框架支持的。

  •  使用的资源不同:  拦截器是一个Spring 组件,归Spring管理,配置在Spring文件中,因此能使用Spring里的任何资源、对象,例如Service对象、数据源、事务管理等,通过loC注入到拦截器即可:而Filter则不能。

  •  深度不同: Filter 在只在Servlet前后起作用。而拦截器能够深入到方法前后、异常抛出前后等,因此拦截器的使用具有更大的弹性。所以在Spring构架的程序中,要优先使用拦截器。

二、自定义拦截器

创建新的Web模块,搭建maven工程,添加项目所需依赖

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.1.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
</dependencies>

配置web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <filter>
        <filter-name>encoding</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

配置SpringMVC 全局配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 自动扫描指定的包,下面所有注解类交给IOC容器管理 -->
    <context:component-scan base-package="com.zhao.controller"/>
    <mvc:annotation-driven/>
    <!--静态资源过滤-->
    <mvc:default-servlet-handler />

    <!--解决json乱码问题-->
    <mvc:annotation-driven>
        <mvc:message-converters register-defaults="true">
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <constructor-arg value="UTF-8"/>
            </bean>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                        <property name="failOnEmptyBeans" value="false"/>
                    </bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

    <!-- 视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          id="internalResourceViewResolver">
        <!-- 前缀 -->
        <property name="prefix" value="/WEB-INF/jsp/" />
        <!-- 后缀 -->
        <property name="suffix" value=".jsp" />
    </bean>
  
</beans>

编写一个拦截器

public class MyInterceptor implements HandlerInterceptor {
    //return true; 执行下一个拦截器 放行。
    //return false; 不执行下一个拦截器
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("=========处理前==========");
        return false;
    }
    //拦截日志
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("=========处理后==========");
    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("=========清理==========");
    }
}

在springmvc的配置文件中配置拦截器

<!--拦截器-->
<mvc:interceptors>
    <mvc:interceptor>
        <!--包括这个请求下的所有请求-->
        <mvc:mapping path="/**"/>
        <bean class="com.zhao.config.MyInterceptor" />
    </mvc:interceptor>
</mvc:interceptors>

编写一个Controller,接收请求

@RestController
public class TestController {
    @GetMapping("/t1")
    public String test(){
        System.out.println("Controller执行成功!");
        return "ok";
    }
}

前端 index文件

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
    <a href="${pageContext.request.contextPath}/t1">拦截器测试</a>
  </body>
</html>

测试一下

image.png

image.png

image.png 从上图可以看出,拦截器中的方法都执行了,测试成功。

三、拦截器应用(登陆认证)

编写一个登陆页面  login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<%--web-INF下的所有资源或者页面只能通过controller 或者servlet进行访问--%>
<body>
    <h1>登陆页面</h1>

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

编写一个Controller处理请求

@Controller
@RequestMapping("/user")
public class LoginController {
    @RequestMapping("/main")
    public String main(){
        return "main";
    }
    @RequestMapping("/goLogin")
    public String goLogin(){
        return "login";
    }

    @RequestMapping("/login")
    public String login(String username, String password, HttpSession session, Model model){
        //把用户的信息存到session中
        session.setAttribute("userLoginInfo",username);
        model.addAttribute("username",username);
        return "login";
    }

    @RequestMapping("/goOut")
    public String goOut(String username, String password, HttpSession session, Model model){
        //把用户的信息存到session中
        session.removeAttribute("userLoginInfo");
        return "main";
    }
}

登陆成功进入首页main.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
        <h1>首页</h1>
<span>${username}</span>
<p>
    <a href="${pageContext.request.contextPath}/user/goOut">注销</a>
</p>
</body>
</html>

使用index页面完成跳转

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
    <h1><a href="${pageContext.request.contextPath}/user/login">登录页面</a></h1>
    <h1><a href="${pageContext.request.contextPath}/user/main">主页面</a> </h1>
  </body>
</html>

编写用户登录拦截器

public class LoginInterceptor implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        //放行
        if (request.getRequestURI().contains("goLogin")){
            return true;
        }
        if (request.getRequestURI().contains("login")){
            return true;
        }

        if (session.getAttribute("userLoginInfo")!=null){
            return true;
        }


        //判断什么情况没有登录
        request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);

        return false;
    }
}

注册拦截器

<mvc:interceptor>
    <!--包括这个请求下的所有请求-->
    <mvc:mapping path="/user/**"/>
    <bean class="com.zhao.config.LoginInterceptor" />
</mvc:interceptor>

测试一下,先进入主页面

image.png

发现是未登录状态,直接跳到登录页面

image.png

输入账号密码进行登录

image.png

登录成功进入首页,测试完成。

image.png