携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第18天,点击查看活动详情
1. Filter
过滤器的配置比较简单,直接实现Filter接口即可,也可以通过@WebFilter注解实现对特定URL拦截,看到Filter 接口中定义了三个方法。
以前是在
javax.servlet包下
init():该方法在容器启动初始化过滤器时被调用,它在 Filter 的整个生命周期只会被调用一次。注意:这个方法必须执行成功,否则过滤器会不起作用。doFilter():容器中的每一次请求都会调用该方法,FilterChain用来调用下一个过滤器 Filter。destroy(): 当容器销毁 过滤器实例时调用该方法,一般在方法中销毁或关闭资源,在过滤器 Filter 的整个生命周期也只会被调用一次。
在Java中就使用了链式结构。把所有的过滤器都放在FilterChain里边,如果符合条件,就执行下一个过滤器(如果没有过滤器了,就执行目标资源)。
@Component
public class MyFilter implements Filter {
@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 处理中");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
System.out.println("Filter 后置");
}
}
再在web.xml中配置
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>com.he.filter.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
可以看到,我们所以请求都可以输出doFilter里面我们配置的东西
1.1. 过滤器源码
好了我好奇,Filter在哪定义的呢,简要介绍了SpringMVC中的Filter两个基类GenericFilterBean与OncePerRequestFilter
GenericFilterBean中的init方法,
public final void init(FilterConfig filterConfig) throws ServletException {
Assert.notNull(filterConfig, "FilterConfig must not be null");
this.filterConfig = filterConfig;
// 从init parameters将属性设置进bean中
PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
if (!pvs.isEmpty()) {
try {
// 从init parameters将属性设置进bean中
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
Environment env = this.environment;
if (env == null) {
env = new StandardServletEnvironment();
}
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, env));
// 此方法没做任何操作,主要用于扩展时使用为子类实现
initBeanWrapper(bw);
// 通过PropertyValue进行设置
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
String msg = "Failed to set bean properties on filter '" +
filterConfig.getFilterName() + "': " + ex.getMessage();
logger.error(msg, ex);
throw new NestedServletException(msg, ex);
}
}
// 子类去实现
initFilterBean();
if (logger.isDebugEnabled()) {
logger.debug("Filter '" + filterConfig.getFilterName() + "' configured for use");
}
}
我们来看一下initFilterBean()方法,只有DelegatingFilterProxy才重写了这个方法:
protected void initFilterBean() throws ServletException {
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
// If no target bean name specified, use filter name.
if (this.targetBeanName == null) {
// 父类GenericFilterBean中的getFilterName
this.targetBeanName = getFilterName();
}
// 创建一个ApplicationContext
WebApplicationContext wac = findWebApplicationContext();
if (wac != null) {
// 从spring上下文中获取beanName为this.targetBeanName的bean
this.delegate = initDelegate(wac);
}
}
}
}
进入initDelegate方法看看
// 给定的上下文中将代理Filter初始化为Bean
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
String targetBeanName = getTargetBeanName();
Assert.state(targetBeanName != null, "No target bean name set");
Filter delegate = wac.getBean(targetBeanName, Filter.class); // 让spring中的bean与web中的Filter产生联系
if (isTargetFilterLifecycle()) {
delegate.init(getFilterConfig());
}
return delegate;
}
让spring中的bean与web中的Filter产生联系
自此之后DelegatingFilterProxy中的Filter就是从容器中获取得了,是一个bean,到这里GenericFilterBean中的init方法就执行结束了,接下来就是调用DelegatingFilterProxy中的doFilter了:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 这个时候的Filter全是spring容器中的bean了
Filter delegateToUse = this.delegate;
if (delegateToUse == null) {
synchronized (this.delegateMonitor) {
delegateToUse = this.delegate;
if (delegateToUse == null) {
WebApplicationContext wac = findWebApplicationContext();
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: " +
"no ContextLoaderListener or DispatcherServlet registered?");
}
delegateToUse = initDelegate(wac);
}
this.delegate = delegateToUse;
}
}
// 让代理Filter执行实际的doFilter方法
invokeDelegate(delegateToUse, request, response, filterChain);
}
先前的doFilter只是判断当前代理Filter是否是null,是的话再从bean中取出Filter,接下来才是从spring容器中取出来的Filter执行doFilter的工作了,进入invokeDelegate中:
protected void invokeDelegate(
Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
delegate.doFilter(request, response, filterChain);
}
将Web体系中的Filter的doFilter()指向一个从spring上下文获取的bean,最终调用的是该bean的doFilter(),以后用的都是这个bean而不是原生Web体系的Filter,也正是因为是一个bean,所以才可以使用@AutoWired注入spring bean。将自己的Filter创建到Spring的上下文中,又能集成到web容器的filterChain上。
放一张别人总结的图
这里就是不知道为什么这个类打不了断点,所以都是看别人分析的
1.2. 拦截器和过滤器执行顺序
直接放图
这是别人的实践博客,我自己就不实践了,单纯结果还是很好理解的
对于拦截器(Interceptor)和过滤器(Filter)的执行顺序和区别止步前行的博客-CSDN博客拦截器和过滤器的区别
HandlerInterceptor和Filter区别
| HandlerInterceptor | Filter |
|---|---|
| 拦截器是框架中的对象 | 过滤器是servlet中的对象 |
| 拦截器是实现HandlerInterceptor | 过滤器实现Filter接口的对象 |
| 拦截器是用来验证请求的,能截断请求 | 过滤器是用来设置request,response的参数,属性的,侧重对数据过滤的 |
| 过滤器是在拦截器之前执行的。 | |
| 拦截器有三个执行时间点 | 过滤器是一个执行时间点 |
| 拦截器是侧重拦截对controller的对象。如果你的请求不能被DispatcherServlet接收,这个请求不会执行拦截器内容 | 过滤器可以处理jsp, js, html等等 |
| 拦截器拦截普通类方法执行 | 过滤器过滤servlet请求响应 |