可选的Spring bean引用

时间:2010-10-18 08:05:55

标签: java spring

在我的应用程序中,我使用ContextLoaderListener从许多jar中加载上下文文件:

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

这意味着我可以在不进行导入的情况下从其他jar中引用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>

在上面的示例中,如果找不到引用,我希望optionalBeanReference1等于null(以某种方式将其标记为可选)

这可以在Spring完成吗?或者你推荐什么方法来处理动态参考?

5 个答案:

答案 0 :(得分:25)

我最好的猜测是使用autowire - 使用必需的false。不知道如何在XML中表达这一点,但使用注释配置,这将是:

@Autowired(required=false)

答案 1 :(得分:13)

使用最新版本的Spring(使用Spring 4.1测试)和Java Configuration and Java 8,您可以在参数中使用Optional,并且只在可用时自动连接。

@Autowired
public MyApplication(Optional<YourOptionalObject> maybeObject) {
    // do something with the optional autowired
}

答案 2 :(得分:10)

  

您建议使用哪种方法处理动态引用?

我认为@ cristian的@Autowired答案很好。如果该类型的bean可用,那将调用setter方法。但是,如果你有多个相同类型的bean,我相信Spring会抛出异常。如果由于这个或其他原因你不能使用@Autowired,我会看到几个解决方案:

  1. 您可以创建班级ApplicationContextAware并自己在上下文中查找bean:

    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定义为具有空值。请参阅此factory code

  4. 祝你好运。

答案 3 :(得分:5)

这没有内置机制。但是,您可以编写一个非常简单的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>

答案 4 :(得分:5)

鉴于XML配置中的bean引用是通过表达式语言(EL)定义的,您可以执行以下操作:

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

使用BeanExpressionContext.getObject()方法。有关详细信息,请参阅here