Как изменить beans, определенный в контейнере spring

У меня есть два xml файла, определяющих beans для springframework (версия 2.5.x):

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 bean 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?

EDIT 2009-10-06:

Целью этого является наличие стандартного контейнера containerBase, который используется множеством разных проектов. Каждый проект может переопределять/расширять некоторые свойства, которые являются специальными для этого проекта, в своем собственном containerSpecial. Если проект не переопределяется, он использует значения по умолчанию, определенные в containerBase.

Ответ 1

Вы можете использовать BeanFactoryPostProcessor для изменения метаданных bean до того, как контейнер Spring создает экземпляр CodeBase 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;
    }

}

Затем в контекстеSpecial.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>

Ответ 2

Да. Определение bean может иметь атрибут "parent", который ссылается на родительское определение bean. Новое определение "child" наследует большинство свойств родителя, и любое из этих свойств может быть переопределено.

См. Bean Наследование наследования

Также вы можете использовать Слияние коллекций, чтобы объединить определение свойства списка из родительского и дочернего bean определений, Таким образом вы можете указать некоторые элементы списка в родительском определении bean и добавить в него дополнительные элементы в определении child bean.

Ответ 3

3 подхода:

  • Простой: есть два списка defaultSourceCodeLocations и дополнительныеSourceCodeLocations и ваши методы доступа проверяют оба из них (или объединяют их). Я видел это в некоторых фреймворках - заполняется список обработчиков по умолчанию, затем добавляются дополнительные созданные пользователем...

  • Сложнее, но сохраняет исходный класс в чистоте: тогда вы можете создать класс 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, который сделает это путем отражения. Будьте осторожны при заказе, если используете этот подход. Зависимый beans от CodeBase должен был убедиться, что этот класс был создан сначала (зависит от)

3 Вариант на 2... Вместо прямого создания класса CodeBase вместо этого создайте factory, который возвращает заполненный bean. Этот factory может быть сконфигурирован с помощью Spring аналогично 2. Имеют значения по умолчаниюSourceCodeLocations и дополнительныеSourceCodeLocations

Если вам не нужно много расширяемых свойств, я бы пошел с опцией 1.

Ответ 4

Есть ли способ определить список в свойствах или другой конфигурации перед рукой?

Похоже, что конфигурация приложения и проводка тесно связаны. По моему опыту, если трудно что-то сделать в Spring, возможно, существует другой способ сделать это.