Необязательные ссылки на Spring Bean - PullRequest
20 голосов
/ 18 октября 2010

В моем приложении я использую ContextLoaderListener для загрузки контекстных файлов из многих jar-файлов, используя:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:META-INF/contextBeans.xml</param-value>
</context-param>

Это означает, что я могу ссылаться на bean-компоненты из других jar-файлов без выполнения импорта.Есть несколько вариантов развертывания, и в некоторых развертываниях банки могут быть исключены.Чтобы поддержать это, я хотел бы, чтобы некоторые ссылки на bean-компоненты были необязательными.Например:

<bean id="mainAppBean" class="com.someapp.MyApplication">
    <constructor-arg index="0" ref="localBean"/>
     <constructor-arg index="1" ref="optionalBeanReference1"/>
    <constructor-arg index="2" ref="optionalBeanReference2"/>
 </bean>

В приведенном выше примере я хотел бы, чтобы optionBeanReference1 был равен нулю, если ссылка не была найдена (пометить ее как необязательную каким-либо образом)?или какой метод вы рекомендуете для обработки динамических ссылок?

Ответы [ 5 ]

25 голосов
/ 18 октября 2010

Мое лучшее предположение - использовать autowire -ing с обязательным значением false.Не знаю, как вы можете выразить это в XML, но с помощью конфигурации аннотации это будет выглядеть так:

@Autowired(required=false)
16 голосов
/ 03 ноября 2015

В последних версиях Spring (протестированных с spring 4.1) и Java Configuration и Java 8 вы можете использовать Optional в параметрах, и только если они доступны, они автоматически подключаются.

@Autowired
public MyApplication(Optional<YourOptionalObject> maybeObject) {
    // do something with the optional autowired
}
10 голосов
/ 18 октября 2010

Какой метод вы рекомендуете для обработки динамических ссылок?

Я думаю, что @ cristian's @Autowired - хороший ответ. Это вызовет методы установки, если бины этого типа доступны. Однако, если у вас есть несколько бинов одного типа, я думаю, что Spring выбрасывает исключение. Если вы не можете использовать @Autowired по той или иной причине, я вижу пару решений:

  1. Вы могли бы сделать свой класс ApplicationContextAware и сами искать компоненты в контексте:

    public void setApplicationContext(ApplicationContext applicationContext) {
        if (applicationContext.containsBean("optionalBeanReference1")) {
            setOptionalBeanReference1(
                (OptionalBeanReference1)applicationContext.bean(
                    "optionalBeanReference1");
        }
        ...
    }
    
  2. Вы можете инвертировать зависимость. Каждый из необязательных классов может установить сами в mainAppBean. Я использую это в определенных ситуациях, когда прямая зависимость может вызвать циклы или другие проблемы.

    <bean id="optionalBeanReference1" class="com.someapp.SomeClass">
        <constructor-arg index="0" ref="mainAppBean"/>
    </bean>
    

    Тогда в SomeClass:

    public SomeClass(com.someapp.MyApplication mainAppBean) {
        mainAppBean.setOptionalBeanReference1(this);
    }
    
  3. Вы можете остаться с вашей прямой зависимостью, а затем либо импортировать файл с определенными bean-компонентами, либо импортировать другой файл, в котором вы определяете bean-компоненты с нулевыми значениями с помощью фабричного bean-компонента. См. заводской код .

Удачи.

5 голосов
/ 19 июня 2014

Учитывая, что ссылки на bean-компоненты в вашей конфигурации XML определены с помощью языка выражений (EL), вы можете сделать следующее:

<property name="cache" value="#{getObject('optionalCache')}" />

, который использует метод BeanExpressionContext.getObject(). Подробнее см. здесь .

5 голосов
/ 18 октября 2010

Для этого нет встроенного механизма. Однако вы можете написать довольно простую FactoryBean реализацию, чтобы сделать это для вас, что-то вроде этого:

public class OptionalFactoryBean extends AbstractFactoryBean<Object> implements BeanNameAware {

    private String beanName;

    @Override
    public void setBeanName(String beanName) {
        this.beanName = BeanFactoryUtils.originalBeanName(beanName);

    }

    @Override
    protected Object createInstance() throws Exception {
        if (getBeanFactory().containsBean(beanName)) {
            return getBeanFactory().getBean(beanName);
        } else {
            return null;
        }
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }
}

Затем вы можете использовать его так:

<bean id="mainAppBean" class="com.someapp.MyApplication">
    <constructor-arg index="0" ref="localBean"/>    
    <constructor-arg index="1">
       <bean name="optionalBeanReference1" class="com.someapp.OptionalBeanFactory"/>
    </constructor-arg>
    <constructor-arg index="2">
       <bean name="optionalBeanReference2" class="com.someapp.OptionalBeanFactory"/>
    </constructor-arg>
</bean>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...