问题 如何修改spring容器中定义的bean


我有两个xml文件定义springframework(版本2.5.x)的bean:

containerBase.xml:
<beans>
    <bean id="codebase" class="com.example.CodeBase">
        <property name="sourceCodeLocations">
            <list>
                <value>src/handmade/productive</value>
            </list>
        </property>
    </bean>
</beans>

......和

containerSpecial.xml:
<beans>
    <import resource="containerBase.xml" />
</beans>

现在我想调整属性 sourceCodeLocations 豆 codebase 中 containerSpecial.xml。我需要添加第二个值 src/generated/productive

一个简单的方法 是要覆盖的定义 codebase 在 containerSpecial.xml 并添加两个值,来自 containerBase.xml 和新的:

containerSpecial.xml:
<beans>
    <import resource="containerBase.xml" />

    <bean id="codebase" class="com.example.CodeBase">
        <property name="sourceCodeLocations">
            <list>
                <value>src/handmade/productive</value>
                <value>src/generated/productive</value>
            </list>
        </property>
    </bean>
</beans>

有没有办法扩展列表而不重新定义bean?

编辑2009-10-06:

这样做的目的是拥有一个共享的标准容器 containerBase 许多不同的项目使用它。每个项目都可以覆盖/扩展一些特定于该项目的属性 containerSpecial。如果项目没有覆盖,则使用中定义的默认值 containerBase


2204
2017-09-28 12:35


起源



答案:


你可以用一个 的BeanFactoryPostProcessor 在Spring容器实例化Code​​Base bean之前更改bean的元数据。例如:

public class CodebaseOverrider implements BeanFactoryPostProcessor {

    private List<String> sourceCodeLocations;

    public void postProcessBeanFactory(
            ConfigurableListableBeanFactory beanFactory) throws BeansException {        
        CodeBase codebase = (CodeBase)beanFactory.getBean("codebase");
        if (sourceCodeLocations != null)
        {
            codebase.setSourceCodeLocations(sourceCodeLocations);
        }
    }

    public void setSourceCodeLocations(List<String> sourceCodeLocations) {
        this.sourceCodeLocations = sourceCodeLocations;
    }

}

然后在contextSpecial.xml中:

<beans>
    <import resource="context1.xml" />

    <bean class="com.example.CodebaseOverrider">
        <property name="sourceCodeLocations">
            <list>
                <value>src/handmade/productive</value>
                <value>src/generated/productive</value>
            </list>
        </property>
    </bean>
</beans>

8
2017-10-10 13:01



CodebaseOverrider并不是我想要的,但通过这种方法,我可以轻松编写CodebaseListExtender。我会试试看。 - tangens


答案:


你可以用一个 的BeanFactoryPostProcessor 在Spring容器实例化Code​​Base bean之前更改bean的元数据。例如:

public class CodebaseOverrider implements BeanFactoryPostProcessor {

    private List<String> sourceCodeLocations;

    public void postProcessBeanFactory(
            ConfigurableListableBeanFactory beanFactory) throws BeansException {        
        CodeBase codebase = (CodeBase)beanFactory.getBean("codebase");
        if (sourceCodeLocations != null)
        {
            codebase.setSourceCodeLocations(sourceCodeLocations);
        }
    }

    public void setSourceCodeLocations(List<String> sourceCodeLocations) {
        this.sourceCodeLocations = sourceCodeLocations;
    }

}

然后在contextSpecial.xml中:

<beans>
    <import resource="context1.xml" />

    <bean class="com.example.CodebaseOverrider">
        <property name="sourceCodeLocations">
            <list>
                <value>src/handmade/productive</value>
                <value>src/generated/productive</value>
            </list>
        </property>
    </bean>
</beans>

8
2017-10-10 13:01



CodebaseOverrider并不是我想要的,但通过这种方法,我可以轻松编写CodebaseListExtender。我会试试看。 - tangens


是。 bean定义可以具有引用父bean定义的“父”属性。新的“子”定义继承了父级的大多数属性,并且可以覆盖任何这些属性。

看到 Bean定义继承

你也可以使用 合并合并 从父和子bean定义合并列表属性定义。这样,您可以在父bean定义中指定一些列表项,并在子bean定义中向其添加更多项。


3
2017-09-28 12:41



但后来有两个豆子。 “codebase”和“containerSpecial.xml”中的“codebase”。从应用程序我只想找到一个bean。而且我不想在“containerBase.xml”摘要中创建bean“codebase”,因为它需要来自其他地方。 - tangens
您不必将两个bean命名为“codebase”,它们可以具有唯一的名称。此外,您不需要创建不是必需的父bean摘要,只需要选项。 - kdubb
顺便说一下,你还需要查找“集合合并”。它解决了需要重新指定已在父bean定义中指定的集合项的问题。 - kdubb
但是后来我仍然有两个类“com.example.CodeBase”的bean。我的应用程序调用XmlBeanFactory.getBeansOfType(CodeBase.class)。所以我真的只想要一个豆子。 - tangens
在这种情况下,在参考示例中建议的父bean上使用abstract关键字; getBeansOfType不会返回标记为abstract的bean定义。你在第一个评论中说过,从其他地方需要父bean定义(现在是抽象的),这是好的,因为抽象不会禁用它只使用它的实例化。因此,无论在其他地方需要什么,您都可以创建一个新的子定义,这也不是抽象的。 - kdubb


有没有办法在事先定义属性或其他配置中的列表?

应用配置和布线似乎紧密耦合。根据我的经验,如果在Spring中很难做某事,可能会有一种更简单的方法。


1
2017-10-08 01:54



我是最好的自由方式。你有好主意吗? - tangens
属性文件是一个简单的系统配置,可以通过Properties对象注入spring bean。我已经将它们用于类似的默认/容器属性。要获得列表支持,请委托一个方法来解析Properties对象中的列表。 config.default.properties --------------------------- source.locations.1 = src / handmade / productive source.locations.2 = src / handmade / productive1 config.container.properties --------------------------- source.locations.1 = src / container / productive source.locations 0.2 = SRC /容器/ productive1 - jon077


3种方法:

  1. 简单:有两个列表defaultSourceCodeLocations和additionalSourceCodeLocations,并让您的访问者方法检查这两个(或组合它们)。我已经在一些框架中看到了这一点 - 填充了一个默认的处理程序列表,然后添加了其他用户创建的处理程序...

  2. 更复杂但保持原始类清洁:然后您可以创建CodeBaseModifier类。这将有一个init方法来改变注入的bean实例。

    <bean id="codebaseModifier" class="com.example.CodeBase" init-method="populateCodeBase">
        <property name="sourceCodeLocations" ref="codebase"/>
        <property name="additionalSourceCodeLocations">
        <list>
            <value>src/handmade/productive</value>
        </list>
        </property>
    </bean>
    

如果你想让它变得非常通用,你可以制作一个通过反射来做到这一点的bean修饰符。如果使用这种方法,请注意订购。 CodeBase的依赖bean必须确保首先实例化该类(取决于)

3 2的变体...而不是直接创建CodeBase类而是创建一个返回填充bean的工厂。然后可以以类似于2的方式使用Spring配置此工厂。具有defaultSourceCodeLocations和additionalSourceCodeLocations

除非你需要很多可扩展的属性,否则我会选择1。


1
2017-10-06 12:52



抱歉无法正确格式化... - Pablojim


在Spring 3.0中,您可以在'list'标记上指定merge =“true”。看到 http://forum.springsource.org/archive/index.php/t-97501.html 详情。


1
2017-08-12 02:59