Filter过滤器 Interceptor拦截器
1.过滤器和拦截器的区别
1.过滤器和拦截器底层实现方式不同
过滤器是基于函数回调,拦截器则是基于Java的反射机制(动态代理)实现的。
在我们自定义的过滤器中都会实现一个doFilter()方法,这个方法有一个FilterChain参数,而实际上他是一个回调接口。
ApplicationFilterChain是他的实现类,这个实现类内部也有一个doFIlter()方法就是回调方法。
public interface FilterChain {
void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}
ApplicationFilterChain里面能拿到我们自定义的xxxFilter类,在其内部回调方法diFilter()里调用各个自定义xxxFilter过滤器,并执行doFilter()方法
public final class ApplicationFilterChain implements FilterChain {
@Override
public void doFilter(ServletRequest request, ServletResponse response) {
...//省略
internalDoFilter(request,response);
}
private void internalDoFilter(ServletRequest request, ServletResponse response){
if (pos < n) {
//获取第pos个filter
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = filterConfig.getFilter();
...
filter.doFilter(request, response, this);
}
}
}
而每个xxxFilter会先执行自身的doFilter()过滤逻辑,最后在执行结束前会执行filterChain.doFilter()方法,也就是回调ApplicationFilterChain的doFilter()方法,以此循环执行实现函数回调
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
filterChain.doFilter(servletRequest, servletResponse);
}
2.使用范围不同
我们看到过滤器实现的是Javax.servlet.Filter接口,而这个接口是在Servlet规范中定义的,也就是说过滤器Filter的使用要依赖于Tomcat等容器,导致它只能在web程序中使用。
而拦截器(Interceptor)他是一个Spring组件,并由Spring容器管理,并不依赖Tomcat等容器,是可以单独使用的。不仅能应用在web程序中,也可以用于Application,Swing等程序中
3.触发时机不同
过滤器Filter是在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后。拦截器Interceptor是在请求进入servlet后,在进入Controller之前进行预处理的,Controller中渲染了对应的视图之后请求结束
4.拦截的请求范围不同
同时配置过滤器和拦截器
@Controller
@RequestMapping()
public class Test {
@RequestMapping("/test1")
@ResponseBody
public String test1(String a) {
System.out.println("我是controller");
return null;
}
}
项目启动过程中发现,过滤器的init()方法,随着容器的启动进行了初始化
控制台打印日志如下
执行顺序:Filter init --Filter处理中(doFilter) -- Interceptor前置(preHanlder) -- 我是Controller(controller) -- Interceptor(postHanlder) -- Interceptor处理后
5.注入Bean情况不同
在实际业务场景中,应用到过滤器或拦截器,为处理业务逻辑难免会引入一些service服务
@Component
public class TestServiceImpl implements TestService {
@Override
public void a() {
System.out.println("我是方法A");
}
}
过滤器中注入service,发起请求测试,入职正常打印出我是方法A
@Autowired
private TestService testService;
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("Filter 处理中");
testService.a();
filterChain.doFilter(servletRequest, servletResponse);
}
打印结果
Filter 处理中
我是方法A
Interceptor 前置
我是controller
Interceptor 处理中
Interceptor 后置
在拦截器中注入service,为null
这是因为加载顺序导致的问题,蓝机器加载的时间点在springcontext之前,而Bean又是由Spring进行管理
拦截器:老子今天要进洞房;
Spring:兄弟别闹,你媳妇我还没生出来呢!
解决方法:在注册拦截器之前,先将Interceptor手动进行注入。
注意:在registry.addInterceptor()注册的是getMyInterceptor()实例
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Bean
public MyInterceptor getMyInterceptor(){
System.out.println("注入了MyInterceptor");
return new MyInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
//此时注册的是 getMyInterceptor实例 并添加拦截路径
registry.addInterceptor(getMyInterceptor()).addPathPatterns("/**");
}
}
6.控制顺序不同
实际开发过程中,会出现多个过滤器或拦截器同时存在的情况,我们希望过滤器或者拦截器或拦截器能按照自定义顺序执行
过滤器用@Order注解控制执行顺序,通过@Order控制过滤器级别,值越小级别越高,越先执行
@Order(Ordered.HIGHEST_PRECEDENCE)
@Component
public class MyFilter2 implements Filter {}
拦截器默认执行顺序,就是它的注册顺序,也可以通过Order手动设置控制,值越小,越先执行
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**").order(2);
registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**").order(1);
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").order(3);
}
看到输出结果发现,先声明的拦截器preHandle()方法先执行,而postHandler()方法反而会后执行。
2.测试demo
1.myconfig
package com.web.config;
@Configuration
public class MyMVCConfig implements WebMvcConfigurer {
@Bean
public MyFilter getFilter(){
System.out.println("注入过滤器");
return new MyFilter();
}
@Bean
public MyInterceptor getInterceptor(){
System.out.println("注入拦截器");
return new MyInterceptor();
}
@Bean
public MyInterceptor1 getInterceptor1(){
System.out.println("注入拦截器1");
return new MyInterceptor1();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor( getInterceptor()).addPathPatterns("/**");
registry.addInterceptor(getInterceptor1()).addPathPatterns("/**");
}
}
2.MyAppContext
public class MyAppContext implements ApplicationContextAware {
//静态变量获取bean对象
static ApplicationContext applicationContext = null;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
MyAppContext.applicationContext = applicationContext;
}
public static <T> T getBean(Class<T> aClass) throws BeansException {
return MyAppContext.applicationContext.getBean(aClass);
}
}
3.MyFilter
public class MyFilter implements Filter {
@Resource
Myservice myservice;
//项目启动加载
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("Filter:前置设置");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("Filter:处理中");
myservice.test();
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
System.out.println("Filter:后置设置");
}
}
4.MyInterceptor
@Order(1)
public class MyInterceptor implements HandlerInterceptor {
@Resource
Myservice myservice;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
myservice.test();
System.out.println("Interceptor: 处理");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("Interceptor: 处理modleandview");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("释放资源");
}
}
@Order(2)
public class MyInterceptor1 implements HandlerInterceptor {
@Resource
Myservice myservice;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
myservice.test();
System.out.println("Interceptor1: 处理");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("Interceptor1: 处理modleandview");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("1释放资源");
}
}
5.service,impl,controller
public interface Myservice {
public void test();
}
@Service
public class MyServiceImpl implements Myservice {
int a = 1;
@Override
public void test() {
System.out.println("执行方法第" + a + "次");
a ++ ;
}
}
@RequestMapping("/a")
@RestController
public class MyController {
@GetMapping("/a")
public void test(){
System.out.println("myController");
}
}
6.运行结果
2022-06-29 10:38:33.149 INFO 9316 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1648 ms
注入过滤器
Filter:前置设置
注入拦截器
注入拦截器1
2022-06-29 10:38:33.334 INFO 9316 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
Filter:处理中
执行方法第1次
执行方法第2次
Intercepotor:处理
执行方法第3次
Interceptor1: 处理
myController
Interceptor1: 处理modleandview
Interceptor: 处理modleandview
1释放资源
释放资源