8

如果需要,需要哪种配置?这不推荐吗?

注释类:

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 {

            }
        };
    }
}

应用程序上下文.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

结果DependantBeanin 的实例AppTest不为空,即它是由 spring 创建的,但它的依赖项 ( DependencyBean) 为空。Spring 完全不抱怨的事实使我相信这是 Spring 中的一个错误。是否应支持此用例?

顺便说一句,我正在使用 spring-*-3.1.1.RELEASE.jar 顺便说一句 2:重现错误的代码也可以在这里找到。

4

4 回答 4

7

也许更简单和描述性的答案:

是的,可以使用@Componentbean 作为BeanFactoryPostProcessor依赖项。

但是, a 的每个依赖项BeanFactoryPostProcessor都将在任何依赖项处于活动状态之前被实例化BeanPostProcessor。其中包括:

  • CommonAnnotationBeanPostProcessor- 负责@PostConstruct@Resource以及其他一些注释
  • AutowiredAnnotationBeanPostProcessor- 负责@Autowired@Value注释
  • ...还有很多...

所以你总结一下:

是的,可以使用@Componentbean 作为BeanFactoryPostProcessor依赖项,但它们不能使用基于注解的注入 ( @Autowired, @Resource, @WebServiceRef, ...) 和BeanPostProcessors 提供的其他功能。


您的示例的解决方法可能是ApplicationContext按照您的建议创建层次结构:

  • 每个上下文都会初始化并应用自己的后处理器基础架构,您仍然可以在其中引用来自父上下文的依赖项。

其他方法可能是(我更喜欢):

  • 在您的 bean 上使用BeanFactoryAware接口并@Component自己提取您的依赖项(因为 Spring 不会注入它)。
  • BeanFactoryPostProcessor在上下文配置中定义与 s 连接的 beanXML@Configuration(即不@Component用于这些 bean)。
于 2013-05-28T18:18:59.443 回答
3

由于对 spring 进行了一些认真的调试,我们发现DependantBean参数 toBeanFactoryPostProcessorConfiguration导致了其他(紧密不相关的)bean 的急切初始化。但由于春天在BeanFactoryPostProcessor舞台上,BeanPostProcessors还没有准备好。

阅读BeanFactoryPostProcessor的 javadoc (感谢@Pavel 指出这一点)准确地解释了这个问题:

BeanFactoryPostProcessor 可以与 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:(请注意,引导程序是常规应用程序上下文的父上下文)

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
            {

            }
        };
    }
}

使它工作的最后一件事是移动DependantBeanDependencyBean进入bootstrap包装。目标是@Value从数据库中读取属性。在重用 bean 的旧定义并且不复制 bean 的同时。

于 2013-05-20T10:30:15.543 回答
0

尝试使用 Spring Util 命名空间并指定值类型。参考这个问题

于 2013-05-15T19:07:14.927 回答
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">
于 2013-05-15T15:30:31.843 回答