一、拦截器概念
拦截器(Interceptor)是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行
其作用是在指定的方法调用前后执行预先设定的代码,阻止原始方法的执行,就是用来做增强的
那拦截器和过滤器有什么区别?
- 归属不同:Filter属于Servlet技术,Interceptor属于SpringMVC技术
- 拦截内容不同:Filter对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强
二、拦截器入门
1、环境准备
创建一个Web的Maven项目,配置pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>SpringMVC_09</groupId>
<artifactId>SpringMVC_09</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>SpringMVC_09 Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>16</maven.compiler.source>
<maven.compiler.target>16</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>80</port>
<path>/</path>
<uriEncoding>UTF-8</uriEncoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
创建相关配置类
SpringMVCConfig
@Configuration
@ComponentScan({"com.controller", "com.config"})
@EnableWebMvc
public class SpringMVCConfig {
}
ServletConfig
public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMVCConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
//乱码处理
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
}
创建模型类Book
public class Book {
private String name;
private double price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}
创建控制类BookController
@RestController
@RequestMapping("/books")
public class BookController {
@PostMapping
public String save(@RequestBody Book book) {
System.out.println("book save..." + book);
return "{'module':'book save'}";
}
@DeleteMapping("/{id}")
public String delete(@PathVariable Integer id) {
System.out.println("book delete..." + id);
return "{'module':'book delete'}";
}
@PutMapping
public String update(@RequestBody Book book) {
System.out.println("book update..." + book);
return "{'module':'book update'}";
}
@GetMapping("/{id}")
public String getById(@PathVariable Integer id) {
System.out.println("book getById..." + id);
return "{'module':'book getById'}";
}
@GetMapping
public String getAll() {
System.out.println("book getAll...");
return "{'module':'book getAll'}";
}
}
删除web.xml,配置tomcat服务器
最终项目结构如下
2、拦截器开发
创建拦截器类,让其实现HandlerInterceptor接口
@Component
//定义拦截器类,实现HandlerInterceptor接口
//注意当前类必须受Spring容器控制
public class ProjectInterceptor 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) throws Exception {
System.out.println("postHandle...");
}
@Override
//原始方法调用完成后执行的内容
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion...");
}
}
当设置preHandler中的返回值为false时,则不会执行原始Controller类中请求的方法和后续posrHandle、afterCompletion方法
配置拦截器类
SpringMVCSupport
@Configuration
public class SpringMVCSupport extends WebMvcConfigurationSupport {
@Autowired
private ProjectInterceptor projectInterceptor;
@Override
//配置拦截器
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(projectInterceptor).addPathPatterns("/books", "/books/*");
}
}
此处配置的/books和/books/*将以books开头的所有路径都进行了拦截
SpringMVC添加SpringMVCSupport包扫描
@Configuration
@ComponentScan({"com.controller", "com.config"})
@EnableWebMvc
public class SpringMVCConfig {
}
启动服务器,在apifox中进行测试
控制台输出
三、拦截器参数
1、前置处理方法
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle...");
return true;
}
- request:请求对象
- response:响应对象
- handler:被调用的处理器对象,本质上是一个方法对象,对反射中的Method对象进行了再包装
使用request对象可以获取请求数据中的内容,如获取请求头的Content-Type
使用handler参数可以获取方法的相关信息
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String contenType = request.getHeader("Content-Type");//获取请求头
System.out.println(contenType);
HandlerMethod handlerMethod = (HandlerMethod) handler;
String methodName = handlerMethod.getMethod().getName();//可以获取方法的名称
System.out.println(methodName);
System.out.println("preHandle...");
return true;
}
重启服务器,在apifox中测试
控制台输出
2、后置处理方法
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle...");
}
modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整(因为现在主流都是json数据,所以使用频率不高)
3、完成处理方法
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion...");
}
ex:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理
四、拦截器链
当我们项目中有多个拦截器时,执行顺序该是怎么样的?
配置多个拦截器
@Component
//定义拦截器类,实现HandlerInterceptor接口
//注意当前类必须受Spring容器控制
public class ProjectInterceptor2 implements HandlerInterceptor {
@Override
//原始方法调用前执行的内容
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle...2");
return true;
}
@Override
//原始方法调用后执行的内容
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle...2");
}
@Override
//原始方法调用完成后执行的内容
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion...2");
}
}
配置拦截器类
@Configuration
public class SpringMVCSupport extends WebMvcConfigurationSupport {
@Autowired
private ProjectInterceptor projectInterceptor;
@Autowired
private ProjectInterceptor2 projectInterceptor2;
@Override
//配置拦截器
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(projectInterceptor).addPathPatterns("/books", "/books/*");
registry.addInterceptor(projectInterceptor2).addPathPatterns("/books", "/books/*");
}
}
重启服务器,运行apifox测试
控制台输出
可以看到,preHandle顺序执行,postHandle和afterCompletion倒序执行
当设置ProjectInterceptor2中preHandle返回值为false时
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle...2");
return false;
}
重启服务器,运行apifox测试
控制台输出
即只执行了ProjectInterceptor的preHandle和afterCompletion方法和ProjectInterceptor2中的preHandle方法