SpringMVC - Role DelegatingFilterProxy with SpringBoot environment usage (shiro [resolved] when configuring Filters, why should configure proxy?)

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

DelegatingFilterProxyExistence of spring-webthe 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.

Here Insert Picture Description

There we can see on the chart, DelegatingFilterProxyclass 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 initthe 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)

  1. Find proxy class is configured in the spring idand assigned to targetBeanName.
  2. Use found idfrom springfound 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 filterarranged 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 filterin fact we configured filterthe filter-nametag filterNameof

<filter-name>filterName</filter-name>

# doFilter

We take a look at doFilterthe specific implementation method, which mainly use 被代理的filter, and call the invokeDelegatemethod,
the implementation 被代理filterof doFilterthe 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);
 }
Published 501 original articles · won praise 112 · views 20000 +

Guess you like

Origin blog.csdn.net/LawssssCat/article/details/105027791