一、拦截器概念 & 拦截器与过滤器Filter的区别
二、拦截器入门
2.1、我们在没有使用拦截器的时候,我们的增删改查项目代码写法如下所示:
config包:
ServletContainersInitConfig类:
package com.itheima.config;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import javax.servlet.Filter;
/**
* 让tomcat服务器获取到的客户端请求全交给springmvc管理
*/
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
// 交给springmvc管理
protected String[] getServletMappings() {
return new String[]{"/"};
}
//乱码处理
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
}
SpringMvcConfig类:
package com.itheima.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration // 定义SpringMvc容器注解
@ComponentScan({"com.itheima.controller"}) // 扫描bean注解
@EnableWebMvc // JSON数据和JAVA数据相互转换注解
public class SpringMvcConfig {
}
POJO封装实体对象类:
package com.itheima.domain;
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 + '\'' +
", 价格=" + price +
'}';
}
}
表现层:
package com.itheima.controller;
import com.itheima.domain.Book;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@RestController // 响应前端数据注解 & 表现层bean注解
@RequestMapping("/books")
public class BookController {
/**
* 增
*/
@PostMapping // REST风格访问路径
public String save(@RequestBody Book book){ // @RequestBody :JSON数据格式
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'}";
}
}
测试程序是否正常执行:前端用户通过访问表现层的资源路径:
2.2、使用拦截器的步骤:
第一步:写拦截器类:
注:因为加载器是把关着表现层是否让客户端访问的,因此一般把加载器类放在controller表现层包下
package com.itheima.controller.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 拦截器类
*
* 注1:该拦截器类需要实现HandlerInterceptor类,并且重写HandlerInterceptor中的方法
* 注2:preHandle方法必须返回true,如果为false就说明拦截器直接把客户端的请求拦截下来了
* 注3:因为拦截器是为springmvc服务的(因为拦截器把关着表现层是否让客户端访问,而springmvc就是扫描处理表现层业务的
* 因此拦截器是为springmvc服务的),所以拦截器也需要被springmvc加载到,因此需要配上@Compoent bean注解,被springmvc扫描
*/
@Component // springmvc扫描该拦截器
public class ProjectInterceptor implements HandlerInterceptor {
// 决定是否让客户端访问想要的资源的方法 true:让客户端访问增删改查功能,false:不让拿
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle....");
return true; // true:代表放行客户端的访问请求,让客户端拿想要的访问数据
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle....");
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion....");
}
}
第二步:配置客户端执行什么样的请求(如执行增删改查的哪个请求时),该拦截器会执行:
做法:将SpringMvcConfig容器类实现WebMvcConfigurer,重写WebMvcConfigurer类中的
addInterceptors方法,并且把刚才第一步写的拦截器类配置成依赖关系
注意:特别注意这里面的设定的访问路径(这里面我们设定是"/books"和"/books/*"的访问路径),只有设定了访问的路径时候,客户端访问的路径如果是我们设定的这种路径的话,那么拦截器类才能生效,才能决定放不放行,如果没设定用户如"/users"格式的访问路径,那么拦截器对这种用户的资源访问路径是不生效的
package com.itheima.config;
import com.itheima.controller.interceptor.ProjectInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration // 定义SpringMvc容器注解
@ComponentScan({"com.itheima.controller"}) // 扫描bean注解
@EnableWebMvc // JSON数据和JAVA数据相互转换注解
public class SpringMvcConfig implements WebMvcConfigurer{
/**
* 拦截器类的依赖关系
*/
@Autowired // 自动装配注解(处理依赖关系的注解)
// private ProjectInterceptor projectInterceptor = new ProjectInterceptor();
// 该自动装配的注解作用就如上,新建对象,所以projectInterceptor对象就可以供下面的方法用了
private ProjectInterceptor projectInterceptor;
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
// 这句话的意思就是说:当客户端的访问表现层的资源路径是 "/books"格式或者"/books/*"格式的话,
// 那么就触发拦截器类projectInterceptor(也就是说保安出动~)然后让拦截器类中的方法判定放不放行
}
}
当客户端进行访问"/books"格式的资源路径的时候,结果如下所示:
因为拦截器类中的preHandle方法(专门决定让不让用户访问的资源调用的方法)
返回的是true,
因此就允许客户端访问调用想要的资源数据了,
但是如果该方法返回的为false时,
那么就不允许用户访问调用想要的路径资源(前提要保证在SpringMvcConfig容器中设定好了访问哪些路径时才让拦截器进行判断)
preHandle方法返回值为true时:(放行,允许用户调用/books路径下的资源)
preHandle方法返回值为false时:(不放行,不允许用户调用/books路径下的资源)
三、拦截器参数
我们从上面的演示输出的结果可以看出,当客户端访问的路径是我们设定的需要通过拦截器判断是否放行的路径的时候,是先走我们拦截器类的preHandle()方法的,而该方法中也是有参数的,那么这些参数都有哪些作用呢:
其他的几个参数,用到了再说~
四、拦截器链
也就是说:可以配置多个拦截器,因此被称为拦截器链
代码演示如下所示:
拦截器1的代码:
package com.itheima.controller.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 拦截器类
*
* 注1:该拦截器类需要实现HandlerInterceptor类,并且重写HandlerInterceptor中的方法
* 注2:preHandle方法必须返回true,如果为false就说明拦截器直接把客户端的请求拦截下来了
* 注3:因为拦截器是为springmvc服务的(因为拦截器把关着表现层是否让客户端访问,而springmvc就是扫描处理表现层业务的
* 因此拦截器是为springmvc服务的),所以拦截器也需要被springmvc加载到,因此需要配上@Compoent bean注解,被springmvc扫描
*/
@Component // springmvc扫描该拦截器
public class ProjectInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle....");
return true;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle....");
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion....");
}
}
拦截器2的代码:
package com.itheima.controller.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 拦截器类
*
* 注1:该拦截器类需要实现HandlerInterceptor类,并且重写HandlerInterceptor中的方法
* 注2:preHandle方法必须返回true,如果为false就说明拦截器直接把客户端的请求拦截下来了
* 注3:因为拦截器是为springmvc服务的(因为拦截器把关着表现层是否让客户端访问,而springmvc就是扫描处理表现层业务的
* 因此拦截器是为springmvc服务的),所以拦截器也需要被springmvc加载到,因此需要配上@Compoent bean注解,被springmvc扫描
*/
@Component // springmvc扫描该拦截器
public class ProjectInterceptor2 implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle....2222");
return true;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle....2222");
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion....2222");
}
}
配置该两个拦截器拦截路径的请求:
package com.itheima.config;
import com.itheima.controller.interceptor.ProjectInterceptor;
import com.itheima.controller.interceptor.ProjectInterceptor2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration // 定义SpringMvc容器注解
@ComponentScan({"com.itheima.controller"}) // 扫描bean注解
@EnableWebMvc // JSON数据和JAVA数据相互转换注解
public class SpringMvcConfig implements WebMvcConfigurer{
/**
* 拦截器类的依赖关系
*/
@Autowired // 自动装配注解(处理依赖关系的注解)
private ProjectInterceptor projectInterceptor; // 第一个拦截器类依赖
@Autowired
private ProjectInterceptor2 projectInterceptor2; // 第二个拦截器类依赖
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
registry.addInterceptor(projectInterceptor2).addPathPatterns("/books","/books/*");
// 这句话的意思就是说:当客户端的访问表现层的资源路径是 "/books"格式或者"/books/*"格式的话,
// 那么就触发拦截器类projectInterceptor(也就是说保安出动~)然后让拦截器类中的方法判定放不放行
}
}
客户端访问能被拦截器获取的/books路径资源时,结果如下所示:
注意:无论几个拦截器,都是先执行这几个拦截器的判断是否放行用户访问资源路径的preHandle()方法的,【上面结果的模拟压栈,可以先看成只有preHandle()方法,然后调用完数据后再有的其他方法】
并且当有很多个拦截器的时候,先进入哪个拦截器的preHandle()方法,是由下面的先后顺序决定的:
并且当第一个拦截器如果preHandle()方法返回的是false,就是代表不让客户端调用访问路径资源,那么当执行到第一个拦截器preHandle()方法后,就会结束了,后面的其他拦截器及其他代码就不会再执行了,(就比如压栈的时候,第一个进去的卡在里面了,后面的也就都进不去了)