目录
一、拦截器Interceptor介绍
拦截是AOP的一种实现策略。在Webwork的中文文档的解释为:拦截器是动态拦截Action调用的对象。它提供了一种机制可以使开发者可以定义在一个action执行的前后执行的代码,也可以在一个action执行前阻止其执行。同时也是提供了一种可以提取action中可重用的部分的方式。
拦截器和过滤器的区别:
过滤器 和 拦截器 均体现了AOP的编程思想(都用到了反射),都可以实现诸如日志记录、登录鉴权等功能,但二者也存在区别:
Filter拦截的目标是servlet的执行,而拦截器拦截的是Spring MVC定义的概念,叫Handler(常见的就是我们用RequestMapping定义出来的HandlerMethod)。觉得它相似是因为Spring的Handler就是DispatcherServlet使用的,而后者就是一Servlet。Filter包围着DispatcherServlet,而它自己也想去执行一个目标Handler,并在执行周围包裹着拦截器,好处是拦截器可以被容器管理,从而获得被容器赋予的能力,而Filter不行(这样一想和代理Filter使用ApplicationFilterChain很类似,只是拦截器更贴近执行业务的方法)。没有什么其它更本质上的区别。
拦截器链(Interceptor Chain),拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。
二、拦截器定义的步骤-MVC方式
第一步:编写一个普通类实现 HandlerInterceptor 接口
/**
* @author swadian
* @date 2021/2/8
* @Version 1.0
* @describetion
*/
public class HandlerInterceptorDemo implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("preHandle 拦截器拦截了");
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle 方法执行了");
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System.out.println("afterCompletion 方法执行了");
}
}
第二步:在springmvc.xml中配置拦截器
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean id="handlerInterceptorDemo" class="com.test.controller.HandlerInterceptorDemo"></bean>
</mvc:interceptor>
</mvc:interceptors>
编写测试方法
/**
* 拦截器测试方法
*/
@RequestMapping("/testHandle")
public String testRedirect() {
System.out.println("AccountController 的 testHandle 方法执行 了。。。。");
return "success";
}
测试结果,拦截器在方法前后实现拦截
三、拦截器的细节
拦截器的放行
放行的含义是指,如果有下一个拦截器就执行下一个,如果该拦截器处于拦截器链的最后一个,则执行控制器中的方法。
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("preHandle 拦截器拦截了");
return true; // 为true时进行放行,为false时进行拦截,后边程序不再执行
}
拦截器执行流程图示
拦截器接口
// 拦截器接口
public interface HandlerInterceptor {
/**
* 在控制器的处理请求方法前调用
*/
boolean preHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;
/**
* 在控制器的处理请求方法调用之后,解析视图之前执行
*/
void postHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3, ModelAndView var4) throws Exception;
/**
* 在控制器的处理请求方法执行完成后执行,即视图渲染结束之后执行
*/
void afterCompletion(HttpServletRequest var1, HttpServletResponse var2, Object var3, Exception var4) throws Exception;
}
拦截器的作用路径
作用路径可以通过在配置文件中配置。
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 用于指定拦截的 url -->
<mvc:mapping path="/**"/>
<!-- 用于指定排除的 url-->
<mvc:exclude-mapping path="/login/**"/>
<bean id="handlerInterceptorDemo" class="com.test.controller.HandlerInterceptorDemo"></bean>
</mvc:interceptor>
</mvc:interceptors>
多个拦截器的执行顺序
多个拦截器是按照配置的顺序决定的
四、拦截器链的实现-基于SpringBoot
1、定义拦截器
首先定义两个拦截器,LoginInterceptor.java,VisitInterceptor.java;实现HandlerInterceptor接口,代码如下:
拦截器一:LoginInterceptor.java用于验证用户的登录
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author swadian
* @date 2022/2/21
* @Version 1.0
* 拦截器
*/
public class LoginInterceptor implements HandlerInterceptor {
@Override //进入Controller之前执行该方法
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object object = request.getSession().getAttribute("userName");
System.out.println("LoginInterceptor执行...");
if (object == null) {
throw new Exception("用户没有登录!");
}
return true;//为true时进行放行,为false时进行拦截,后边程序不再执行
}
}
拦截器二:VisitInterceptor.java
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author swadian
* @date 2022/2/21
* @Version 1.0
* 拦截器
*/
public class VisitInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("VisitInterceptor执行...");
return true;
}
}
2、配置拦截器
创建一个配置类InterceptorConfig,并实现WebMvcConfigurer接口, 覆盖接口中的addInterceptors方法,并为该配置类添加@Configuration注解,标注此类为一个配置类,让Spring Boot 扫描到,这里的操作就相当于SpringMVC的注册拦截器 ,@Configuration就相当于一个applicationContext-mvc.xml
import com.swadian.spring_learn.LoginInterceptor;
import com.swadian.spring_learn.VisitInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author swadian
* @date 2022/2/21
* @Version 1.0
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry) {
//需要拦截的路径,/**表示需要拦截所有请求
String[] addPathPatterns = {"/**"};
//不需要拦截的路径
String[] excludePathPaterns = {
"/login/submit",
"/login/logout"
};
//注册一个登录拦截器
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns(addPathPatterns)
.excludePathPatterns(excludePathPaterns);
//注册一个权限拦截器 如果有多个拦截器,只需要添加以下一行代码
registry.addInterceptor(new VisitInterceptor())
.addPathPatterns(addPathPatterns)
.excludePathPatterns(excludePathPaterns);
/**
* 拦截器的顺序根据你加入的顺序来的
* VisitInterceptor,LoginInterceptor就是拦截器具体逻辑的内容的2个类,需要实现implements HandlerInterceptor
* 拦截路径个过滤路径都是自己看业务定义
*/
}
}
测试:接下来写一个Controller类可以用来测试程序。
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* @author swadian
* @date 2022/2/21
* @Version 1.0
*/
@RestController
@RequestMapping("/login")
public class LoginController {
//登录
@RequestMapping("/submit")
public String login(HttpServletRequest request, HttpServletResponse response) throws Exception {
String userName = request.getParameter("userName");
if (!StringUtils.isEmpty(userName)) {
request.getSession().setAttribute("userName", userName);
return "登录成功!";
}
return "登录失败";
}
//退出
@RequestMapping("/logout")
public String logout(HttpSession session) throws Exception {
// session 过期
session.invalidate();
return "退出登录";
}
//退出
@RequestMapping("/visit")
public String visit(String visitParam) throws Exception {
// session 过期
return "访问成功:" + visitParam;
}
}
访问/submit方法进行登录,如果没有登录,拦截器中的异常会抛出
然后访问/visit方法,成功登录
拦截器执行顺序,LoginInterceptor先配置,先执行