如果需要,需要哪种配置?这不推荐吗?
带注释的类:
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;
}
}
失败的依赖项bean:
package com.springbug.beanfactorydependencyissue;
import org.springframework.stereotype.Component;
@Component
public class DependencyBean {
}
测试用例:
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();
}
}
具有“错误”依赖性的自定义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 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>
为什么BeanFactoryPostProcessorConfiguration
无法引用DependantBean
?
DependantBean
中生成的AppTest
实例不为空,即它是由spring创建的,但其依赖项(DependencyBean
)为空。 Spring没有抱怨的事实让我相信这是春天的一个错误。是否应该支持这种用例?
答案 0 :(得分:5)
也许更简单和描述性的答案:
是的,可以将@Component
bean用作BeanFactoryPostProcessor
依赖项。
然而,在BeanFactoryPostProcessor
处于活动状态之前,BeanPostProcessor
的每个依赖关系都将被实例化。这些包括:
CommonAnnotationBeanPostProcessor
- 负责@PostConstruct
,@Resource
和其他一些注释AutowiredAnnotationBeanPostProcessor
- 负责@Autowired
和@Value
注释所以总结一下:
是的,可以使用@Component
bean作为BeanFactoryPostProcessor
依赖项,但它们不能使用基于注释的注入(@Autowired
,@Resource
,{{ 1}},...)以及@WebServiceRef
s。
您的示例的解决方法可能是按照您的建议创建BeanPostProcessor
层次结构:
其他方法可能(我更喜欢):
ApplicationContext
bean上使用BeanFactoryAware
接口并自行提取依赖关系(因为Spring不会注入它)。@Component
或BeanFactoryPostProcessor
中定义与XML
相关联的bean(即不要对这些bean使用@Configuration
)。答案 1 :(得分:3)
感谢对Spring的一些认真调试,我们发现了DependantBean
参数
到BeanFactoryPostProcessorConfiguration
引起了其他(缝合不相关的)bean的急切初始化。
但是当春天处于BeanFactoryPostProcessor
阶段时,BeanPostProcessors
尚未准备就绪。
阅读BeanFactoryPostProcessor的javadoc(感谢@Pavel指出这一点)完全解释了这个问题:
BeanFactoryPostProcessor可以与bean定义交互并修改bean定义,但绝不能与bean实例交互。 这样做可能会导致bean过早实例化,违反容器并导致意外的副作用。 如果需要bean实例交互,请考虑实现{@link BeanPostProcessor}。
解决方案:
稍加修改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>
新的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>
新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();
}
}
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
{
}
};
}
}
让它工作的最后一件事是将DependantBean
和DependencyBean
移到bootstrap
包中。
实现了从数据库中读取@Value
属性的目标。在重用bean的旧定义而不重复bean时。
答案 2 :(得分:0)
你需要像这样给你的组件一个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...
}
然后使用引用
<ref bean="myClass">
答案 3 :(得分:0)
尝试使用Spring Util名称空间并指定value-type。请参阅此question