Spring:无法从父上下文自动装配 bean
Posted
技术标签:
【中文标题】Spring:无法从父上下文自动装配 bean【英文标题】:Spring: Cannot autowire beans from parent context 【发布时间】:2016-08-24 11:12:47 【问题描述】:我有一个 Spring Boot (1.4.0) 应用程序,它在初始化期间启动第二个上下文(我需要它,因为我必须使用特定类型的授权发布 Web 服务,而父上下文发布不同的服务)。
我像这样创建了一个子上下文:
@Configuration
@ConditionalOnClass(Servlet.class, DispatcherServlet.class)
@ConditionalOnWebApplication
public class ChildContextConfiguration implements ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>
private final Logger logger = LoggerFactory.getLogger(ChildContextConfiguration.class);
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
this.applicationContext = applicationContext;
private void createChildContext()
final AnnotationConfigEmbeddedWebApplicationContext childContext = new AnnotationConfigEmbeddedWebApplicationContext(ChildConfiguration.class);
childContext.setParent(this.applicationContext);
childContext.setId(this.applicationContext.getId() + ":child");
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent)
logger.info("creating child context");
createChildContext();
子上下文的配置类如下所示:
@Configuration
@ComponentScan(basePackages = "com.example.child")
@PropertySource("file:some-config.properties")
@ConfigurationProperties(prefix = "child")
@EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class)
public class ChildConfiguration
private Integer port;
private String keyStore;
private String keyStorePass;
private String keyPass;
private String trustStore;
private String trustStorePass;
private String packageBase;
public void setPort(Integer port)
this.port = port;
public void setKeyStore(String keyStore)
this.keyStore = keyStore;
public void setKeyStorePass(String keyStorePass)
this.keyStorePass = keyStorePass;
public void setKeyPass(String keyPass)
this.keyPass = keyPass;
public void setTrustStore(String trustStore)
this.trustStore = trustStore;
public void setTrustStorePass(String trustStorePass)
this.trustStorePass = trustStorePass;
public void setPackageBase(String packageBase)
this.packageBase = packageBase;
@Bean
public Jaxb2Marshaller swpMarshaller()
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setPackagesToScan(packageBase);
return marshaller;
@Bean
public Unmarshaller swpUnmarshaller() throws JAXBException
JAXBContext jaxbContext = JAXBContext.newInstance(packageBase);
return jaxbContext.createUnmarshaller();
@Bean
public Filter encodingFilter()
CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
encodingFilter.setEncoding("UTF-8");
return encodingFilter;
@Bean
public ServerProperties serverProperties()
ServerProperties props = new ServerProperties();
props.setPort(port);
props.setSsl(ssl());
return props;
private Ssl ssl()
Ssl ssl = new Ssl();
ssl.setEnabled(true);
ssl.setKeyStore(keyStore);
ssl.setKeyStorePassword(keyStorePass);
ssl.setKeyStoreType("JKS");
ssl.setKeyPassword(keyPass);
ssl.setTrustStore(trustStore);
ssl.setTrustStorePassword(trustStorePass);
ssl.setClientAuth(Ssl.ClientAuth.NEED);
return ssl;
到目前为止,这是可行的。但是当我尝试从父上下文中自动装配一个 bean 时,我收到一条错误消息,指出没有候选者。
另一个有趣的事情是,当我使用 ApplicationContextAware 接口将(子)上下文注入到我的子上下文的一个 bean 中时,该上下文的 getParent() 属性当时为空。
我现在所做的是实现如下 getter 函数:
private SomeBean getSomeBean()
if (this.someBean == null)
this.someBean = applicationContext.getParent().getBean(SomeBean.class);
return this.someBean;
总结一下:在构建子上下文的bean期间,没有设置父上下文,所以我不能使用自动装配。
有什么方法可以让我的设置使用 autowire 吗?
【问题讨论】:
【参考方案1】:构造函数在内部注册刷新上下文 - 尝试设置类并在设置父上下文后手动刷新。
private void createChildContext()
final AnnotationConfigEmbeddedWebApplicationContext childContext = new AnnotationConfigEmbeddedWebApplicationContext();
childContext.setParent(this.applicationContext);
childContext.setId(this.applicationContext.getId() + ":child");
childContext.register(ChildConfiguration.class);
childContext.refresh();
【讨论】:
这无疑是朝着正确方向迈出的一步。不幸的是,我陷入了刷新循环。第一个 ContextRefreshedEvent 是由父上下文触发的,以下是来自子上下文的。所以我只在根上下文发出事件时才调用 createChildContext() 。这似乎可行,但随后子上下文的 tomcat 尝试使用 SSL(为子上下文配置,而不是为父级配置)绑定在根上下文的端口上 更新:当我注释掉 childContext.setParent(this.applicationContext);我只得到一个 ContextRefreshedEvent 并且其他一切都有效(除了从父上下文访问 bean,因为没有)。 我将一个示例项目推送到 github:github.com/thomashoell/SpringBootChildContext。有趣的部分位于 ChildContextConfig 类中。当我设置父上下文 BEFORE 调用刷新时,它不起作用。从我注意到的情况来看,子上下文似乎使用了父上下文的 tomcat。 目前的问题是什么? - 您的示例通过了单一测试,并且成功运行 - 在 9090 和 9091 上有两个 Web 侦听器。 在应用程序的当前状态下,我在子上下文上调用 refresh() 后设置了父上下文。它启动了,但我不能使用自动装配,因为在子上下文启动期间未设置父上下文。当您更改订单时 (setParent() before refresh()) ,或调用 createChildContext_failing();而不是 createChildContext_working();在 ChildContextConfig 中,应用程序不会启动。以上是关于Spring:无法从父上下文自动装配 bean的主要内容,如果未能解决你的问题,请参考以下文章