java 是否可以将用@Component 定义的 bean 作为参数注入 BeanFactoryPostProcessor?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16569091/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is it possible to inject a bean defined with @Component as an argument to a BeanFactoryPostProcessor?
提问by jontejj
And if so which configuration is needed? Is this not recommended?
如果是这样,需要哪种配置?这不推荐吗?
The annotated class:
注释类:
package com.springbug.beanfactorydependencyissue;
import javax.annotation.Resource;
import org.springframework.stereotype.Component;
@Component
public class DependantBean {
@Resource
DependencyBean dependencyBean; // Isn't initialized correctly
public DependencyBean getDependencyBean() {
return dependencyBean;
}
}
The dependency bean that fails:
失败的依赖bean:
package com.springbug.beanfactorydependencyissue;
import org.springframework.stereotype.Component;
@Component
public class DependencyBean {
}
Testcase:
测试用例:
package com.springbug.beanfactorydependencyissue;
import static org.fest.assertions.Assertions.assertThat;
import javax.annotation.Resource;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.Test;
import com.springbug.beanfactorydependencyissue.DependantBean;
@ContextConfiguration(locations = "/applicationContext.xml")
public class AppTest extends AbstractTestNGSpringContextTests {
@Resource
private DependantBean annotatedBean;
@Test
public void testThatDependencyIsInjected() {
// Fails as dependency injection of annotatedBean.dependencyBean does not work
assertThat(annotatedBean.getDependencyBean()).isNotNull();
}
}
A custom BeanFactoryPostProcessor with the "faulty" dependency:
具有“错误”依赖项的自定义 BeanFactoryPostProcessor:
package com.springbug.beanfactorydependencyissue;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BeanFactoryPostProcessorConfiguration {
/**
* The {@link DependantBean} here causes the bug, can
* {@link BeanFactoryPostProcessor} have regular beans as dependencies?
*/
@Bean
public static BeanFactoryPostProcessor beanFactoryPostProcessor(
DependantBean dependantBean) {
return new BeanFactoryPostProcessor() {
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory)
throws BeansException {
}
};
}
}
applicationContext.xml:
应用上下文.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.springbug.beanfactorydependencyissue" />
</beans>
Why can't BeanFactoryPostProcessorConfiguration
reference DependantBean
?
为什么不能BeanFactoryPostProcessorConfiguration
参考DependantBean
?
The resulting DependantBean
instance in AppTest
is not null, i.e it's created by spring, but its dependencies (DependencyBean
) are null. The fact that Spring doesn't complain at all leads me to believe that this is a bug within spring. Should this use-case be supported or not?
DependantBean
in 中的结果实例AppTest
不为空,即它是由 spring 创建的,但其依赖项 ( DependencyBean
) 为空。Spring 根本没有抱怨的事实让我相信这是 Spring 中的一个错误。是否应该支持此用例?
Btw, I'm using spring-*-3.1.1.RELEASE.jar Btw 2: the code to reproduce the bug can also be found here.
顺便说一句,我正在使用 spring-*-3.1.1.RELEASE.jar 顺便说一句 2:重现错误的代码也可以在这里找到。
采纳答案by jontejj
Thanks to some serious debugging of spring we found out that the DependantBean
parameter
to BeanFactoryPostProcessorConfiguration
caused eager initialization of other (seamingly unrelated) beans.
But as spring was in the BeanFactoryPostProcessor
stage the BeanPostProcessors
weren't ready.
感谢 spring 的一些认真调试,我们发现该DependantBean
参数BeanFactoryPostProcessorConfiguration
导致其他(无缝无关)bean 的急切初始化。但随着春天的BeanFactoryPostProcessor
到来,BeanPostProcessors
他们还没有准备好。
Reading the javadoc for BeanFactoryPostProcessor(Thanks to @Pavel for pointing this out) explains the issue exactly:
阅读BeanFactoryPostProcessor的 javadoc (感谢@Pavel 指出这一点)准确地解释了这个问题:
BeanFactoryPostProcessor may interact with and modify bean definitions, but never bean instances. Doing so may cause premature bean instantiation, violating the container and causing unintended side-effects. If bean instance interaction is required, consider implementing {@link BeanPostProcessor} instead.
BeanFactoryPostProcessor 可以与 bean 定义进行交互和修改,但绝不能与 bean 实例交互。这样做可能会导致 bean 过早实例化、违反容器并导致意外的副作用。如果需要 bean 实例交互,请考虑实现 {@link BeanPostProcessor}。
The solution:
解决方案:
The slightly modified applicationContext.xml
:
稍作修改applicationContext.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.stackoverflow.springbug.beanfactorydependencyissue.other" />
</beans>
The new bootstrapContext.xml
: (Notice that only the packages differ)
新的bootstrapContext.xml
:(注意只有包不同)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.stackoverflow.springbug.beanfactorydependencyissue.bootstrap" />
</beans>
The new Contexts.java
: (Notice that bootstrap is parent context to the regular applicationContext)
新的Contexts.java
:(注意引导程序是常规 applicationContext 的父上下文)
package com.stackoverflow.springbug.beanfactorydependencyissue;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
public final class Contexts
{
private static Supplier<ApplicationContext> bootstrap = Suppliers.memoize(new Supplier<ApplicationContext>(){
public ApplicationContext get()
{
return new ClassPathXmlApplicationContext("/bootstrapContext.xml");
}
});
/**
* Context for beans that are needed before initializing of other beans.
*/
public static ApplicationContext bootstrap()
{
return bootstrap.get();
}
private static Supplier<ApplicationContext> applicationContext = Suppliers.memoize(new Supplier<ApplicationContext>(){
public ApplicationContext get()
{
return new ClassPathXmlApplicationContext(new String[]{"/applicationContext.xml"}, bootstrap());
}
});
public static ApplicationContext applicationContext()
{
return applicationContext.get();
}
}
The BeanFactoryPostProcessorConfiguration
without DependantBean
as a parameter:
该BeanFactoryPostProcessorConfiguration
不DependantBean
作为参数:
package com.stackoverflow.springbug.beanfactorydependencyissue.other;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.stackoverflow.springbug.beanfactorydependencyissue.Contexts;
import com.stackoverflow.springbug.beanfactorydependencyissue.bootstrap.DependantBean;
@Configuration
public class BeanFactoryPostProcessorConfiguration
{
/**
* The {@link DependantBean} here caused the bug, {@link Contexts#bootstrap()} is used as a
* workaround.
*/
@Bean
public static BeanFactoryPostProcessor beanFactoryPostProcessor()
{
final DependantBean dependantBean = Contexts.bootstrap().getBean(DependantBean.class);
System.out.println(dependantBean.getDependencyBean());
return new BeanFactoryPostProcessor(){
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
{
}
};
}
}
The last thing to make it work was to move DependantBean
and DependencyBean
into the bootstrap
package.
The goal was achieved to read @Value
properties from the database. While reusing the old definitions of the beans and without duplicating the beans.
使其工作的最后一件事是移动DependantBean
并DependencyBean
放入bootstrap
包中。实现了@Value
从数据库读取属性的目标。同时重用 bean 的旧定义而不复制 bean。
回答by Pavel Horal
Maybe more simpler and descriptive answer:
也许更简单和描述性的答案:
Yes, it is possible to use @Component
bean as BeanFactoryPostProcessor
dependency.
是的,可以使用@Component
bean 作为BeanFactoryPostProcessor
依赖项。
However every dependency of a BeanFactoryPostProcessor
will be instantiated before any BeanPostProcessor
is active. And these include:
但是, a 的每个依赖项BeanFactoryPostProcessor
都将在 anyBeanPostProcessor
处于活动状态之前被实例化。其中包括:
CommonAnnotationBeanPostProcessor
- responsible for@PostConstruct
,@Resource
and some other annotationsAutowiredAnnotationBeanPostProcessor
- responsible for@Autowired
and@Value
annotations- ...and many more...
CommonAnnotationBeanPostProcessor
- 负责@PostConstruct
,@Resource
以及其他一些注释AutowiredAnnotationBeanPostProcessor
- 负责@Autowired
和@Value
注释- ...还有很多...
So tu sum it up:
所以总结一下:
Yes, it is possible to use @Component
bean as BeanFactoryPostProcessor
dependency, but they can not use annotation based injection (@Autowired
, @Resource
, @WebServiceRef
, ...) and other features provided by BeanPostProcessor
s .
是的,可以使用@Component
bean 作为BeanFactoryPostProcessor
依赖项,但它们不能使用基于注解的注入(@Autowired
, @Resource
, @WebServiceRef
, ...)和BeanPostProcessor
s提供的其他功能。
Workaround for your example might be to create ApplicationContext
hierarchy as you have suggested:
您的示例的解决方法可能是ApplicationContext
按照您的建议创建层次结构:
- Each context initializes and applies its own post processorinfrastructure, where you still can reference dependencies from parent contexts.
- 每个上下文都会初始化并应用自己的后处理器基础结构,您仍然可以在其中引用父上下文的依赖项。
Other approaches might be (which I would prefer):
其他方法可能是(我更喜欢):
- Use
BeanFactoryAware
interface on your@Component
bean and pull your dependency yourself (as Spring will not inject it). - Define beans connected with
BeanFactoryPostProcessor
s within context configurationXML
or@Configuration
(i.e. don't use@Component
for these beans).
BeanFactoryAware
在@Component
bean上使用interface 并自己拉取依赖项(因为 Spring 不会注入它)。BeanFactoryPostProcessor
在上下文配置中定义与s连接的beanXML
或@Configuration
(即不@Component
用于这些 bean)。
回答by NullPointerException
You need to give an id to your component like this
您需要像这样为您的组件提供一个 id
@Component("myClass")
public class MyClass implements MyInterface
{
@Resource
private MyDependency myDependency; //Isn't initialized correctly when listOfMyClassBeans references myClass
//Implementation skipped for brevity's sake...
}
and then use the reference
然后使用参考
<ref bean="myClass">