Необязательные Spring bean ссылки
В моем приложении я использую ContextLoaderListener для загрузки файлов контекста из многих банок, используя:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:META-INF/contextBeans.xml</param-value>
</context-param>
Это означает, что я могу ссылаться на beans на другие банки без импорта.
В приложении есть несколько вариантов развертывания, и в некоторых вариантах развертывания банки могут быть исключены. Чтобы поддержать это, я хотел бы, чтобы некоторые ссылки 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>
В приведенном выше примере я хотел бы иметь опциональное значениеBeanReference1 равным null, если ссылка не найдена (пометьте ее необязательным в некотором роде)
Можно ли это сделать в Spring? или какой метод вы рекомендуете для обработки динамических ссылок?
Ответы
Ответ 1
какой метод вы рекомендуете для обработки динамических ссылок?
Я думаю, что @cristian @Ответ на вопрос - хороший. Это вызовет методы настройки, если доступно beans этого типа. Однако, если у вас есть несколько beans одного типа, я считаю, что Spring генерирует исключение. Если вы не можете использовать @Autowired для этой или какой-либо другой причины, я вижу пару решений:
-
Вы можете создать свой класс ApplicationContextAware
и самостоятельно найти beans в контексте:
public void setApplicationContext(ApplicationContext applicationContext) {
if (applicationContext.containsBean("optionalBeanReference1")) {
setOptionalBeanReference1(
(OptionalBeanReference1)applicationContext.bean(
"optionalBeanReference1");
}
...
}
-
Вы можете инвертировать зависимость. Каждый из необязательных классов мог установить себя на 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);
}
-
Вы можете остаться с вашей прямой зависимостью, а затем либо импортировать файл с beans, либо указать другой файл, в котором вы определяете beans как имеющие нулевые значения, используя factory bean. См. Этот factory код.
Удачи.
Ответ 2
Мое лучшее предположение - использовать autowire -инг с требуемым значением false. Не знаете, как вы можете выразить это в XML, но используя конфигурацию аннотаций, это будет выглядеть так:
@Autowired(required=false)
Ответ 3
В последних версиях Spring (протестированных с помощью Spring 4.1) и конфигурации Java и Java 8 вы можете использовать опцию в параметрах и только в том случае, если они доступны.
@Autowired
public MyApplication(Optional<YourOptionalObject> maybeObject) {
// do something with the optional autowired
}
Ответ 4
Для этого нет встроенного механизма. Однако вы можете написать довольно тривиальную реализацию 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>
Ответ 5
Учитывая, что ссылки bean в вашей конфигурации XML определяются с помощью языка выражений (EL), вы можете сделать следующее:
<property name="cache" value="#{getObject('optionalCache')}" />
который использует метод BeanExpressionContext.getObject()
. Подробнее см. здесь.