Spring:代理Filter:DelegatingFilterProxy原理和作用

 DelegatingFilterProxy就是一個對於servlet filter的代理,用這個類的好處主要是通過Spring容器來管理servlet filter的生命週期

  • 還有就是如果filter中需要一些Spring容器的實例,可以通過spring直接注入
  • 另外讀取一些配置文件這些便利的操作都可以通過Spring來配置實現。

Spring web在設計的時候考慮到某些功能的實現是通過Filter來攔截進行實現的,如果直接的簡單的實現幾個Filter好像也不是不可以(平時我們就是這麼用的),但是Spring框架最核心的是IOC容器,和Spring框架最好的實現就是將要實現的Filter功能註冊到IOC容器的一個Bean,這樣就可以和Spring IOC容器進行完美的融合,所以Spring Web設計了DelegatingFilterProxy。

本質上來說DelegatingFilterProxy就是一個Filter,其間接實現了Filter接口,但是在doFilter中其實調用的從Spring 容器中獲取到的代理Filter的實現類delegate。

DelegatingFilterProxy原理:

1、DelegatingFilterProxy根據targetBeanName從Spring 容器中獲取被注入到Spring 容器的Filter實現類,在DelegatingFilterProxy配置時一般需要配置屬性targetBeanName

@Override
protected void initFilterBean() throws ServletException {
	synchronized (this.delegateMonitor) {
		if (this.delegate == null) {
			// If no target bean name specified, use filter name.
                        //當Filter配置時如果沒有設置targentBeanName屬性,則直接根據Filter名稱來查找
			if (this.targetBeanName == null) {
				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) {
                                //從Spring容器中獲取注入的Filter的實現類
				this.delegate = initDelegate(wac);
			}
		}
	}
}
 
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
		//從Spring 容器中獲取注入的Filter的實現類
		Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
		if (isTargetFilterLifecycle()) {
			delegate.init(getFilterConfig());
		}
		return delegate;
	}


2、在DelegatingFilterProxy的實現方法doFilter中,其實最終調用的是委派的類delegate

@Override
protected void initFilterBean() throws ServletException {
	synchronized (this.delegateMonitor) {
		if (this.delegate == null) {
			// If no target bean name specified, use filter name.
                        //當Filter配置時如果沒有設置targentBeanName屬性,則直接根據Filter名稱來查找
			if (this.targetBeanName == null) {
				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) {
                                //從Spring容器中獲取注入的Filter的實現類
				this.delegate = initDelegate(wac);
			}
		}
	}
}
 
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
		//從Spring 容器中獲取注入的Filter的實現類
		Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
		if (isTargetFilterLifecycle()) {
			delegate.init(getFilterConfig());
		}
		return delegate;
	}


總結:Spring web通過提高DelegatingProxyFilter類給開發者提供了便利

 

DelegatingFilterProxy運用

需求

在springboot中 使用了filter, 並且注入了業務工具類(APIUtil ),但注入是null

所以問題就來了:springboot中使用filter時注入bean爲null的解決辦法 

其實Spring中,web應用啓動的順序是:listener->filter->servlet,先初始化listener,然後再來就filter的初始化,再接着纔到我們的dispathServlet的初始化

解決辦法1:

public class TokenAuthFilter implements Filter {

    private final static Logger log = LoggerFactory.getLogger(TokenAuthFilter.class);

    @Autowired
    private APIUtil apiUtil;
}

新增一個config類,用來手工創建filter的bean, 例如:

@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;
    }

}

解決辦法2:

過濾器是servlet規範中定義的,並不歸spring容器管理,也無法直接注入spring中的bean(會報錯)

初始化時通過spring上下文獲取,進行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);
 }


參考鏈接:https://blog.csdn.net/qq924862077/article/details/81583739 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章