https://www.cnblogs.com/zhanhaitao/p/7857245.html
Original intention
DelegatingFilterProxy
It is a proxy for the servlet filter
- The benefits of using this class is mainly through the Spring container to manage the lifecycle of servlet filter
- If there is some instances in the filter vessel Spring desired, by direct injection spring
- Also read some configuration files facilitate these operations can be configured by Spring implement
DelegatingFilterProxy
Existence of spring-web
the package, which is a rolefilter的代理
Indirectly implements the Filter interface, but in fact invoked in doFilter taken from the Spring container to the agent Filter implementation class delegate.
There we can see on the chart, DelegatingFilterProxy
class inheritance GenericFilterBean
, indirectly implements this interface Filter, and therefore belong to a class filter.
Then they would have realized the Filter init, doFilter, destroy three methods.
Acting realization
# init
First, we look at init
the method, we knowWhen the filter will perform the initialization method init
From source we can find the specific code, the method is implemented in GenericFilterBean class, specific functions are:
- The specific classes as spring-based form, easy to maintain spring
- And call initFilterBean method in subclass (for DelegatingFilterProxy) achieved, which main purpose is to find the target filter maintained in the spring, to see embodied in the following code:
/**
* Standard way of initializing this filter.
* Map config parameters onto bean properties of this filter, and
* invoke subclass initialization.
* @param filterConfig the configuration for this filter
* @throws ServletException if bean properties are invalid (or required
* properties are missing), or if subclass initialization fails.
* @see #initFilterBean
*/
@Override
public final void init(FilterConfig filterConfig) throws ServletException {
Assert.notNull(filterConfig, "FilterConfig must not be null");
this.filterConfig = filterConfig;
!将该类封装成spring特有的bean形式,方便spring维护
↓
// Set bean properties from init parameters.
PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
if (!pvs.isEmpty()) {
try {
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);
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 在子类 (DelegatingFilterProxy) 中实现
我们可以到DelegatingFilterPoxy中去看看,具体完成了那些工作?
1、找到要代理bean的id ==> targetBeanName
2、在spring,bean容器中找到具体被代理的filter ==> delegate
↓
// Let subclasses do whatever initialization they like.
initFilterBean();
if (logger.isDebugEnabled()) {
logger.debug("Filter '" + filterConfig.getFilterName() + "' configured for use");
}
}
initFilterBean()
This method performs two main functions :( code below)
- Find proxy class is configured in the spring
id
and assigned totargetBeanName
. - Use found
id
fromspring
found container specific proxied class, and assigned todelegate
@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在spring中配置的id(name)
↓
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) {
!找到具体被代理的filter
↓
this.delegate = initDelegate(wac);
}
}
}
}
getFilterName()
The effect of the method is acquired by the agent filter
arranged in the spring id
(below)
/**
* Make the name of this filter available to subclasses.
* Analogous to GenericServlet's {@code getServletName()}.
* <p>Takes the FilterConfig's filter name by default.
* If initialized as bean in a Spring application context,
* it falls back to the bean name as defined in the bean factory.
* @return the filter name, or {@code null} if none available
* @see javax.servlet.GenericServlet#getServletName()
* @see javax.servlet.FilterConfig#getFilterName()
* @see #setBeanName
*/
@Nullable
protected String getFilterName() {
!找到被代理filter在spring中配置的id
↓
return (this.filterConfig != null ? this.filterConfig.getFilterName() : this.beanName);
}
initDelegate()
Effect of this method is acquired from the container to the particular spring proxied filter
/**
* Initialize the Filter delegate, defined as bean the given Spring
* application context.
* <p>The default implementation fetches the bean from the application context
* and calls the standard {@code Filter.init} method on it, passing
* in the FilterConfig of this Filter proxy.
* @param wac the root application context
* @return the initialized delegate Filter
* @throws ServletException if thrown by the Filter
* @see #getTargetBeanName()
* @see #isTargetFilterLifecycle()
* @see #getFilterConfig()
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
*/
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
! 取出上面找到的 id
↓
String targetBeanName = getTargetBeanName();
Assert.state(targetBeanName != null, "No target bean name set");
! 找到被代理的filter
↓
Filter delegate = wac.getBean(targetBeanName, Filter.class);
if (isTargetFilterLifecycle()) {
delegate.init(getFilterConfig());
}
return delegate;
}
Here we can see, we want the agent filter
in fact we configured filter
the filter-name
tag filterName
of
<filter-name>filterName</filter-name>
# doFilter
We take a look at doFilter
the specific implementation method, which mainly use 被代理的filter
, and call the invokeDelegate
method,
the implementation 被代理filter
of doFilter
the method, the specific implementation, please see source code below:
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
! 得到被代理的filter
↓
// Lazily initialize the delegate if necessary.
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方法
↓
// Let the delegate perform the actual doFilter operation.
invokeDelegate(delegateToUse, request, response, filterChain);
}
invokeDelegate
The method is to perform the role of being a proxy filter method of doFilter
/**
* Actually invoke the delegate Filter with the given request and response.
* @param delegate the delegate Filter
* @param request the current HTTP request
* @param response the current HTTP response
* @param filterChain the current FilterChain
* @throws ServletException if thrown by the Filter
* @throws IOException if thrown by the Filter
*/
protected void invokeDelegate(
Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
delegate.doFilter(request, response, filterChain);
}
See here, I believe we all understand DelegatingFilterPoxy how it is now.
Here we look at spring + shiro is how to use this class
Use (SpringBoot environment)
demand
In springboot in the filter, and injected business tools (APIUtil), but the injection is null
So the question arises: When springboot inject bean used filter is null solution
Analysis : Spring In fact, the order of the web application is launched:
listener ⇒ filter ⇒ servlet
That is, the first initialization listener, and then come back to filter initialization, then and then only to the initialization of our dispathServlet
the reason
but Servlet filter is defined in the specification, does not return spring containers management, can not be directly injected in the spring bean (being given)
Solution # 1
public class TokenAuthFilter implements Filter {
private final static Logger log = LoggerFactory.getLogger(TokenAuthFilter.class);
@Autowired
private APIUtil apiUtil;
}
Add a config class, create a filter for the bean by hand, for example:
@Configuration
public class WebConfig {
@Bean
public Filter tokenAuthFilter() {
return new TokenAuthFilter();
}
/**
* 注册filter,统一处理api开头的请求
* @return FilterRegistrationBean
*/
@Bean
public FilterRegistrationBean tokenAuthFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
// DelegatingFilterProxy把servlet 容器中的filter同spring容器中的bean关联起来
registration.setFilter(new DelegatingFilterProxy("tokenAuthFilter"));
registration.addUrlPatterns("/api/*");
registration.setName("tokenAuthFilter");
registration.setOrder(1);
return registration;
}
}
Solution # 2 (high coupling)
Acquired by the spring context initialization, initialization for the bean:
@Override
public void init(FilterConfig filterConfig) throws ServletException {
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(filterConfig.getServletContext());
RedisTemplate demoBean = (RedisTemplate)context.getBean("redisTemplate");
System.out.println(demoBean);
}