Spring中@Autowired注入PropertySourcesPlaceholderConfigurer子类为空的原因
自定义类PropertyManager继承自PropertySourcesPlaceholderConfigurer,标注了@Component与@PropertySource注解。类中通过@Autowired注入ContextHolder实例时始终返回null,但调用ContextHolder的静态方法却能正常获取上下文。猜测是因为PropertySourcesPlaceholderConfigurer属于BeanFactoryPostProcessor,初始化时ContextHolder实例尚未创建,想请教为何@Autowired注入失效。
相关代码:
/* 这个Bean注入后始终为null */ @Component public class ContextHolder implements ApplicationContextAware, ServletContextAware { private static ApplicationContext springContext; private static ServletContext servletContext; @Override public void setServletContext(ServletContext servletContext) { this.servletContext = servletContext; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.springContext = applicationContext; } public static ApplicationContext getSpringContext() { return springContext; } public static ServletContext getServletContext() { return servletContext; } } @Component @PropertySource("classpath:appProps/globals-${spring.profiles.active}.properties") public class PropertyManager extends PropertySourcesPlaceholderConfigurer { @Autowired ContextHolder contextHolder; // 此处注入始终为null public String get(String key) { // 抛出空指针异常,因为contextHolder为null // 但ContextHolder.getSpringContext()静态调用正常,想知道@Autowired为何失效 return contextHolder.getSpringContext().getEnvironment().getProperty(key); } }
核心原因
你的猜测完全正确,PropertySourcesPlaceholderConfigurer是BeanFactoryPostProcessor的实现类,它的初始化时机远早于普通Bean:
- Spring容器启动时,会先实例化所有
BeanFactoryPostProcessor类型的Bean,并立即执行它们的postProcessBeanFactory方法,这个阶段还未开始普通Bean的实例化和依赖注入流程。 ContextHolder是普通的@ComponentBean,它的实例化、依赖注入以及ApplicationContextAware接口的回调,都发生在BeanFactoryPostProcessor执行完成之后。- 所以当Spring创建
PropertyManager实例时,ContextHolder还没被实例化,自然无法完成@Autowired注入,导致contextHolder为null。
而静态方法能正常工作,是因为ContextHolder的静态变量springContext是在Spring后续处理普通Bean时,通过setApplicationContext方法赋值的,此时PropertyManager虽然已经存在,但静态变量是全局的,后续赋值后就能通过静态方法访问到。
解决方案
方案1:直接使用静态方法(最简单)
既然静态调用已经能正常工作,直接在PropertyManager中使用ContextHolder.getSpringContext()即可,无需依赖实例注入:
@Component @PropertySource("classpath:appProps/globals-${spring.profiles.active}.properties") public class PropertyManager extends PropertySourcesPlaceholderConfigurer { public String get(String key) { return ContextHolder.getSpringContext().getEnvironment().getProperty(key); } }
方案2:避免继承PropertySourcesPlaceholderConfigurer
如果一定要用实例注入,可以调整设计:不要让PropertyManager继承PropertySourcesPlaceholderConfigurer,而是单独用一个类处理属性加载,PropertyManager作为普通Bean来使用:
// 单独配置属性源 @Configuration @PropertySource("classpath:appProps/globals-${spring.profiles.active}.properties") public class PropertyConfig {} // 普通Bean,正常注入依赖 @Component public class PropertyManager { @Autowired private ContextHolder contextHolder; public String get(String key) { return contextHolder.getSpringContext().getEnvironment().getProperty(key); } }
方案3:通过BeanFactoryPostProcessor的回调获取上下文
如果必须继承PropertySourcesPlaceholderConfigurer,可以重写processProperties方法,直接从属性解析器中获取环境对象,不需要依赖ContextHolder:
@Component @PropertySource("classpath:appProps/globals-${spring.profiles.active}.properties") public class PropertyManager extends PropertySourcesPlaceholderConfigurer { private ConfigurableEnvironment environment; @Override protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, ConfigurablePropertyResolver propertyResolver) throws BeansException { this.environment = (ConfigurableEnvironment) propertyResolver; super.processProperties(beanFactoryToProcess, propertyResolver); } public String get(String key) { return environment.getProperty(key); } }
内容的提问来源于stack exchange,提问作者jijonbo

