过滤器(Filter)
过滤器,顾名思义就是用于过滤某些我们不需要的东西,留下符合我们要求的东西。
拦截器与过滤器的区别
:
1、过滤器执行由Servlet容器回调完成,拦截器则是基于Java的反射机制(动态代理)实现的。
2、Filter是依赖于Servlet容器,属于Servlet规范的一部分,也就是说过滤器Filter 的使用要依赖于Tomcat等容器,导致它只能在web程序中使用。而拦截器是一个Spring组件,并由Spring容器管理,并不依赖Tomcat等容器,是可以单独使用的。
3、过滤器的执行是在过滤器之前的,请求需要先通过了过滤器的过滤,然后进入Servlet,拦截器才会起作用。
补充一个拦截器的知识点:
当我们想在拦截器注入其他bean的时候,我们会发现会抛出nested exception is java.lang.NullPointerException异常。这是因为加载顺序导致的,拦截器加载的时间点在springcontext之前,而Bean又是由spring进行管理。
而解决方法也很简单:我们在注册拦截器之前,先将Interceptor 手动进行注入。
注意:此时addInterceptor方法注入的实例就是我们的注册方法testInterceptor()
修改一下我们上一篇文章的拦截器后,就可以在拦截器注入bean了:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public TestInterceptor testInterceptor(){
return new TestInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(testInterceptor()).addPathPatterns("/**");
}
}
自定义过滤器
自定义过滤很简单,只需要实现 javax.Servlet.Filter 接口,然后重写里面的方法就可以了
下面是Filter的源码
public interface Filter {
default void init(FilterConfig filterConfig) throws ServletException {
}
void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
default void destroy() {
}
}
init() :该方法在容器启动初始化过滤器时被调用,它在 Filter 的整个生命周期只会被调用一次。
doFilter() :对请求进行过滤
FilterChain 接口
该接口用于定义一个 Filter 链的对象应该对外提供的方法,这个接口只定义了一个 doFilter 方法。
public void doFilter(ServletRequest request, ServletResponse response) throws java.io.IOException.ServletException
FilterChain 接口的 doFilter 方法用于通知 Web 容器把请求交给 Filter 链中的下一个 Filter 去处理,如果当前调用此方法的 Filter 对象是Filter 链中的最后一个 Filter,那么将把请求交给目标 Servlet 程序去处理。
destroy():当容器销毁过滤器实例时调用该方法,一般在方法中销毁或关闭资源,在过滤器 Filter 的整个生命周期也只会被调用一次
过滤器的工作流程
1、当 Servlet 容器开始调用某个 Servlet 程序时,如果发现已经注册了一个 Filter 程序来对该 Servlet 进行拦截,那么容器不再直接调用 Servlet 的 service 方法,而是调用 Filter 的 doFilter 方法,再由 doFilter 方法决定是否去激活 service 方法。
2、但在 Filter.doFilter 方法中不能直接调用 Servlet 的 service 方法,而是调用 FilterChain.doFilter 方法来激活目标 Servlet 的 service 方法,FilterChain 对象时通过 Filter.doFilter 方法的参数传递进来的。
3、只要在 Filter.doFilter 方法中调用 FilterChain.doFilter 方法的语句前后增加某些程序代码,这样就可以在 Servlet 进行响应前后实现某些特殊功能。
4、如果在 Filter.doFilter 方法中没有调用 FilterChain.doFilter 方法,则目标 Servlet 的 service 方法不会被执行,这样通过 Filter 就可以阻止某些非法的访问请求。
示意图:
配置过滤器
配置过滤器有两种方法,一种是在配置类进行配置,另一种是通过@WebFilter注解进行配置
1、配置类配置
创建一个自定义配置类,用@Component将过滤器交给spring管理
@Component
public class TestFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("过滤器开始执行:"+System.currentTimeMillis());
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("过滤器处理响应:"+System.currentTimeMillis());
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
创建配置类,配置过滤器
@Configuration
public class MyFilterConfig {
@Autowired
TestFilter testFilter;
@Bean
public FilterRegistrationBean<TestFilter> thirdFilter() {
FilterRegistrationBean<TestFilter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(testFilter);
filterRegistrationBean.setOrder(1);
filterRegistrationBean.setUrlPatterns(new ArrayList<>(Arrays.asList("/*")));
return filterRegistrationBean;
}
}
filterRegistrationBean 为过滤器的注册bean
setFilter表示设置过滤器
setUrlPatterns表示过滤的路径
setOrder表示Filter 的执行顺序,越小越先执行
2、注解配置
在自定义过滤器上加上@WebFilter注解,filterName表示过滤器名称,urlPatterns 表示过滤的路径
@WebFilter(filterName = "MyFilterWithAnnotation", urlPatterns = "/*")
public class TestFilter implements Filter {
....
}
在启动类上加上@ServletComponentScan注解,使spring扫描到@WebFilter
@SpringBootApplication
@ServletComponentScan
public class DemoApplication
测试
我在上一篇拦截器的代码基础上加上过滤器,大家看一下输出的结果
可以看到,过滤器是在拦截器的preHandle方法之前执行。