在日常的开发过程中,我们或多或少的会使用过滤器Filter或者拦截器Interceptor实现一些功能,但是好像并没有特别深入了解这两种机制。
- 过滤器:广泛用于对请求和响应进行预处理和后处理的场景。例如,可以在过滤器中进行日志记录、字符编码转换、权限验证等操作。过滤器在请求进入Servlet容器或在响应返回客户端之前起作用。
- 拦截器:主要用于在请求处理的不同阶段进行拦截和处理。在Spring等框架中,拦截器常用于对控制器的方法进行前后处理,如日志记录、权限检查、性能监控等。
1、自定义一个Filter类
public class LoginAuthFilter implements Filter {
Logger logger = LoggerFactory.getLogger(LoginAuthFilter.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//在Filter生命周期只会调用一次,可以读取配置文件
logger.info("【过滤器】初始化");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
logger.info("【过滤器】开始执行");
filterChain.doFilter(servletRequest, servletResponse);
logger.info("【过滤器】执行结束");
}
}
2、注册过滤器
@Configuration
public class LoginAuthConfig {
/**
* 注册过滤器
* @return
*/
@Bean
public FilterRegistrationBean getLoginAuthFilter(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
LoginAuthFilter filter = new LoginAuthFilter();
registrationBean.setFilter(filter);
//urlPatterns配置哪些路径的请求进入过滤器,/*表示所有
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
}
3、项目演示
@RestController
public class SysUserController {
Logger logger = LoggerFactory.getLogger(SysUserController.class);
@GetMapping(value = "/sysUser/getSysUserById")
public String getSysUserById(String userId) {
logger.info("Controller:进入方法getSysUserById()");
return "皮卡丘";
}
}
启动项目,SpringBoot初始化的时候,我们可以看到LoginAuthFilter类中的init()方法执行了一次
接着我们使用postman请求接口,在控制台看到:
在接口请求进入controller层前后,可以在过滤器Filter中处理一些前置后置逻辑。
1、自定义一个Interceptor类
@Component
public class MyInterceptor implements HandlerInterceptor {
Logger logger = LoggerFactory.getLogger(MyInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.info("【拦截器】控制器方法调用前");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.info("【拦截器】控制器方法调用后,视图渲染前");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.info("【拦截器】整个请求完成后调用");
}
}
2、注册拦截器
@Configuration
public class InterceptorConfig implements WebMvcConfigurer{
@Autowired
private MyInterceptor myInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor);
}
}
启动项目,使用postman请求接口,控制台输出如下:
在接口请求进入controler层的前后,拦截器Interceptor中,都有对应的方法,可以处理二开逻辑。
当我们的项目中,既有过滤器Filter,又有拦截器Interceptor的时候,又出现什么的现象呢?
通过断点和日志输出,引出过滤器Filter和拦截器Interceptor一个区别
1、触发时机不同
过滤器Filter是在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后。
拦截器 Interceptor 是在请求进入DispathServlet后,在进入Controller之前进行预处理的,视图渲染之后请求结束。我们在自定义类Interceptor的方法中打上断点,就可以看到都是在什么时机进入。
2、过滤器Filter中无法注入Bean,但是拦截器Interceptor中可以
我们写一个Service类,分别在过滤器Filter和拦截器Interceptor中注入,看下结果:
@Service
public class TestServiceImp {
public void test(){
System.out.println("测试方法");
}
}
可以看到拦截器Interceptor中,是可以成功注入的,过滤器Filter中,testServiceImpl的值为null,无法注入。
这是因为我们在注册过滤器Filter的时候,使用的是new LoginAuthFilter的方式,过滤器是Servlet规范的一部分,而不是Spring框架的组件,需要通过new 关键字创建实例。Servlet容器并不了解Spring容器的上下文,因此它无法自动注入Spring的Bean到过滤器中。
而实例化拦截器Interceptor的时候,使用的@Autowired注解的方式。拦截器是Spring MVC框架的一部分,由Spring容器管理,因此可以通过@Autowired注解直接注入。
那如果我在LoginAuthFilter上,同样加上注解@Component,在配置类中使用@Autowired注入过滤器类,那LoginAuthFilter中的bean 可以成功注入吗?
答案是肯定的,可以成功注入bean。
但是如果你的注册过滤器的类LoginAuthConfig中,还是通过new LoginAuthFilter方法而不是@Autowired注入LoginAuthFilter,你会发现在拦截请求进入LoginAuthFilter中的时候,testServiceImp的bean还是为null 。