1. 問題
最近在項目中添加了會話驗證過濾器,該Filter中使用@Autowired自動裝載了一些從數(shù)據(jù)庫中獲取的系統(tǒng)配置,調(diào)試的時候發(fā)現(xiàn)注入失敗,返回為null。
// RequestAuthFilter只是一個普通類
// 這段代碼處于Application.java啟動類中
@Bean
public FilterRegistrationBean contextFilterRegistrationBean() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new RequestAuthFilter());
registrationBean.addUrlPatterns("/web/*", "/wechat/*", "/core/*");
registrationBean.setName(GlobalConstants.AUTH_FILTER_NAME);
registrationBean.setOrder(1);
return registrationBean;
}
2. 處理方案
嘗試以下兩種方案均成功,本質(zhì)上都是將Filter變成讓Spring容器中的bean。
2.1 修改filter為Spring中bean
- 修改RequestAuthFilter為Component
@Component
public class RequestAuthFilter implements Filter {
- 自動裝載該類
@Autowired
RequestAuthFilter filter;
@Bean
public FilterRegistrationBean contextFilterRegistrationBean() {
...
registrationBean.setFilter(filter);
...
return registrationBean;
}
2.2 保留Filter為普通類,在Application.java中重新定義一個Bean
@Bean
public FilterRegistrationBean contextFilterRegistrationBean() {
...
registrationBean.setFilter(getFilterRegistrationBean());
...
}
@Bean
public Filter getFilterRegistrationBean() {
return new RequestAuthFilter();
}
3. 分析原因
后期通過new產(chǎn)生的實例中無法自動注入Spring容器中裝載的實例。
原理需要了解Spring依賴注入,或者說控制反轉(zhuǎn),查閱Introduction to the Spring IoC container and beans。
It is a process whereby objects define their dependencies, that is, the other objects they work with, only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean.
這些Bean被Spring IoC 容器(org.springframework.context.ApplicationContext)管理,Spring容器也就是一個Bean工廠。
所以自動注入失效,一般有以下兩種可能。
3.1 包沒有被掃描到
引用自spring-boot-and-component-scan
- If your other packages hierarchies are below your main app with the @SpringBootApplication annotation, you’re covered by implicit components scan.
- If there are beans/components in other packages which are not sub packages of the main package, you should manually add them as @ComponentScan
Spring Boot項目的Bean裝配默認規(guī)則是根據(jù)Application類(指項目入口類)所在的包位置從上往下掃描。
假設(shè)Application類在包com.comp.appname下,則只會掃描com.comp.appname包及其所有子包,如果需要自動裝載的類所在包不在com.comp.appname及其子包下,則不會被掃描,自然就沒法被注入!
3.2 調(diào)用者是使用new創(chuàng)建的
如果類A中存在成員屬性B, B是通過@Autowired自動注入,而類A的實例是通過new的方式產(chǎn)生的,那么自動注入會失效的,此時通過Spring的上下文獲取所有的Bean的方法來獲取B。
/**
* Spring上下文工具類,用以讓普通類獲取Spring容器中的Bean
*/
@Component
public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
/**
* 獲取applicationContext
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (SpringUtil.applicationContext == null) {
SpringUtil.applicationContext = applicationContext;
}
}
/**
* 通過name獲取 Bean
*/
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
}
然后在A中就可以這樣來獲取Spring容器中的B實例
BclassInterface b = (BclassInterfaceImpl) SpringUtil.getBean("bclassInterfaceImpl");