GenericFilterBean与DelegatingFilterProxy
上一篇文章
简要介绍了SpringMVC中的Filter两个基类GenericFilterBean与OncePerRequestFilter以及其他一些常用的Filter,这篇文章详细介绍一下GenericFilterBean与DelegatingFilterProxy的作用和使用场景,
讲这个之前需要先了解一点,从web.xml中初始化的Filter并不属于spring,它是Web体系的Filter,并不是bean,在spring容器中bean初始化之前创建,
Web体系的Filter怎么创建:
通常的做法是实现Filter:
public class MyFilter2 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println(Arrays.toString(filterConfig.getInitParameter("exceptPath")
.replace(" ", "")
.replace(" ", "")
.replace("\n", "")
.replace("\r\n", "")
.split(",")));
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//在这里实现自己的过滤规则
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
然后在web.xml中配置,让自定义Filter能够初始化:
<filter>
<filter-name>myFilter2</filter-name>
<filter-class>cn.myllxy.remusic.utils.MyFilter2</filter-class>
<init-param>
<!-- 添加一些例外的路径,准备让过滤器放行 -->
<param-name>exceptPath</param-name>
<param-value>
/,*.js,*.html,*.css,*.png,*.jpg,*.ico,*.gif,
*login,*index.html
</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>myFilter2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
这是在SSM中使用Filter,在SpringBoot中自定义Filter会更简单:
GenericFilterBean实现了
javax.servlet.Filter、org.springframework.beans.factory.BeanNameAware、org.springframework.context.EnvironmentAware、org.springframework.web.context.ServletContextAware、org.springframework.beans.factory.InitializingBean、org.springframework.beans.factory.DisposableBean
目的为了对接Spring的一些功能,
好了,看源码,
GenericFilterBean#init():
@Override
public final void init(FilterConfig filterConfig) throws ServletException {
Assert.notNull(filterConfig, "FilterConfig must not be null");
if (logger.isDebugEnabled()) {
logger.debug("Initializing filter '" + filterConfig.getFilterName() + "'");
}
this.filterConfig = filterConfig;
// 从init parameters将属性设置进bean中
PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
if (!pvs.isEmpty()) {
try {
// 将GenericFilterBean封装成BeanWrapper
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 successfully");
}
}
这个init的目的是将filterConfig中的属性通过beanWrapper设置到DelegatingFilterProxy这个bean中,
我们来看看这个initFilterBean(),只有DelegatingFilterProxy才重写了这个方法:
@Override
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);
}
}
}
}
让spring中的bean与web中的Filter产生联系,Filter delegate = wac.getBean(getTargetBeanName(),Filter.class):
/**
* 在给定的上下文中将代理Filter初始化为Bean
*/
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
if (isTargetFilterLifecycle()) {
delegate.init(getFilterConfig());
}
return delegate;
}
自此之后DelegatingFilterProxy中的Filter就是从容器中获取得了,是一个bean,
到这里GenericFilterBean中的init方法就执行结束了,接下来就是调用DelegatingFilterProxy中的doFilter了:
@Override
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) {
if (this.delegate == null) {
WebApplicationContext wac = findWebApplicationContext();
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: " +
"no ContextLoaderListener or DispatcherServlet registered?");
}
this.delegate = initDelegate(wac);
}
delegateToUse = this.delegate;
}
}
// 让代理Filter执行实际的doFilter方法
invokeDelegate(delegateToUse, request, response, filterChain);
}
先前的doFilter只是判断当前代理Filter是否是null,是的话再从bean中取出Filter,接下来才是从spring容器中取出来的Filter执行doFilter的工作了:
protected void invokeDelegate(
Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
delegate.doFilter(request, response, filterChain);
}
DelegatingFilterProxy的作用介绍完了,那么要怎么启用它呢:
<filter>
<filter-name>delegatingFilterProxy</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>myFilter</param-value>
</init-param>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>delegatingFilterProxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
做到这一步,我的自定义Filter中就可以注入spring容器中的bean了,而在SpringBoot中直接在Filter上加上@Component,其他什么都不用管就可以实现了
注意这个targetFilterLifecycle默认是false,如果是true,代表会执行Filter的init方法,如果你的Filter是实现的顶层接口Filter,那么就必须实现init和destroy,
如果继承自GenericFilterBean,那么就不用,因为GenericFilterBean实现了init和destroy
看到这里,上篇文章中关于DelegatingFilterProxy的作用简介是不是就弄明白了:
将Web体系中的Filter的doFilter()指向一个从spring上下文获取的bean,最终调用的是该bean的doFilter(),以后用的都是这个bean而不是原生Web体系的Filter,也正是因为是一个bean,所以才可以使用@AutoWired注入spring bean。将自己的Filter创建到Spring的上下文中,又能集成到web容器的filterChain上。