是否可以将使用@Component定义的bean作为参数注入BeanFactoryPostProcessor?

jon*_*ejj 7 java spring annotations dependency-injection applicationcontext

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

带注释的类:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

失败的依赖bean:

package com.springbug.beanfactorydependencyissue;

import org.springframework.stereotype.Component;

@Component
public class DependencyBean {

}
Run Code Online (Sandbox Code Playgroud)

测试用例:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

具有"错误"依赖关系的自定义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 {

            }
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

为什么不能BeanFactoryPostProcessorConfiguration参考DependantBean

生成的DependantBean实例AppTest不是null,即它是由spring创建的,但它的dependencies(DependencyBean)为null.Spring没有抱怨的事实让我相信这是春天的一个错误.是否应该支持这种用例?

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

Pav*_*ral 6

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

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

但是,a的每个依赖关系BeanFactoryPostProcessor都会在任何BeanPostProcessor活动之前被实例化.这些包括:

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

所以总结一下:

是的,这是可以使用@Componentbean作为BeanFactoryPostProcessor依赖,但是他们不能使用基于注解注射(@Autowired,@Resource,@WebServiceRef,...),并提供其他功能BeanPostProcessor秒.


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

  • 每个上下文都初始化并应用自己的后处理器基础结构,您仍然可以从父上下文引用依赖项.

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

  • BeanFactoryAware@Componentbean 上使用接口并自己拉取依赖关系(因为Spring不会注入它).
  • BeanFactoryPostProcessor在上下文配置中定义与s 连接的bean XML@Configuration(即不要@Component用于这些bean).


jon*_*ejj 3

由于对 spring 进行了一些认真的调试,我们发现参数DependantBeantoBeanFactoryPostProcessorConfiguration导致了其他(完全不相关的)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>
Run Code Online (Sandbox Code Playgroud)

新的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>
Run Code Online (Sandbox Code Playgroud)

新的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();
    }
}
Run Code Online (Sandbox Code Playgroud)

BeanFactoryPostProcessorConfigurationDependantBean参数:

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
            {

            }
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

让它发挥作用的最后一件事是移动DependantBeanDependencyBean放入bootstrap包中。@Value从数据库读取属性的目标已实现。同时重用 bean 的旧定义并且不重复 bean。