0.引言
在web项目中写过滤器时,若想使用sprng的某些功能,可以通过DelegatingFilterProxy类来实现,该类相当于一个代理类,它会通过配置的filter-name的值去spring容器中找到对应的bean,并且执行对应的过滤逻辑。
1.使用方式
<filter>
<filter-name>filter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>filter</filter-name>
<url-pattern>/admin/*</url-pattern>
</filter-mapping>
2.源码解析
通过类关系图可知,DelegatingFilterPorxy是一个过滤器,那么它是如何调用spring容器中的bean来执行操作的呢?
1.执行过滤器的init方法
DelegatingFilterProxy类中并没有init方法的实现,该实现在他的父类GenericFilterBean当中,在父类中找到该方法。
GenericFilterBean.class
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参数设置成spring能够管理的结构
try {
PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
initBeanWrapper(bw);
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);
}
// 这里就是DelegatingFilterProxy类能够从sping容器中获取对应bean的核心
initFilterBean();
if (logger.isDebugEnabled()) {
logger.debug("Filter '" + filterConfig.getFilterName() + "' configured successfully");
}
}
initFilterBean()是在DelegatingFilterProxy类中实现的方法,代码如下:
DelegatingFilterProxy.class
@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) {
//获取filter-name的值作为待获取bean的id,如果没有将使用DelegatingFilterProxy的bean id.
this.targetBeanName = getFilterName();
}
// Fetch Spring root application context and initialize the delegate early,
// if possible. If the root application context will be started after this
// filter proxy, we'll have to resort to lazy initialization.
WebApplicationContext wac = findWebApplicationContext();
if (wac != null) {
//在这里通过targetBeanName从Spring容器中获取对应的bean
this.delegate = initDelegate(wac);
}
}
}
}
initDelegate(wac)代码如下:
DelegatingFilterProxy.class
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
//将获取的bean赋值给delegate,之后执行doFilter方法时将会调用该bean的方法。
Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
if (isTargetFilterLifecycle()) {
delegate.init(getFilterConfig());
}
return delegate;
}
通过执行init方法将对应的bean赋值给this.delegate后,过滤器将执行doFilter方法。
2.执行doFilter方法
DelegatingFilterProxy.class
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// Lazily initialize the delegate if necessary.
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;
}
}
// Let the delegate perform the actual doFilter operation.
invokeDelegate(delegateToUse, request, response, filterChain);
}
doFilter中通过调用invokeDelegate方法来执行实际的过滤逻辑。
protected void invokeDelegate(
Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
//这里将会执行从spring容器获取的bean的doFilter方法。
delegate.doFilter(request, response, filterChain);
}
4.targetFilterLifecycle的作用
通过init-param将targetFilterLifecycle设置为true,可以让DelegatingFilterProxy代理的filter bean里init和destroy方法生效。在DelegatingFilterProxy调用init时会根据该参数来决定是否调用该bean中的init方法。
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
if (isTargetFilterLifecycle()) {
delegate.init(getFilterConfig());
}
return delegate;
}
protected boolean isTargetFilterLifecycle() {
return this.targetFilterLifecycle;
}
在调用destroy时会根据该参数来决定是否调用该bean中的destroy方法。
@Override
public void destroy() {
Filter delegateToUse = this.delegate;
if (delegateToUse != null) {
destroyDelegate(delegateToUse);
}
}
protected void destroyDelegate(Filter delegate) {
if (isTargetFilterLifecycle()) {
delegate.destroy();
}
}