可选的Spring bean引用

mbd*_*dev 20 java spring

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

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:META-INF/contextBeans.xml</param-value>
</context-param>
Run Code Online (Sandbox Code Playgroud)

这意味着我可以在不进行导入的情况下从其他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>
Run Code Online (Sandbox Code Playgroud)

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

这可以在Spring完成吗?或者您推荐哪种方法来处理动态参考?

Cri*_*bie 25

我最好的猜测是使用自动装配所需的假.不知道如何用XML表达它,但使用注释配置,它看起来像:

@Autowired(required=false)
Run Code Online (Sandbox Code Playgroud)


ase*_*ell 16

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

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

  • 在此处查看Spring改进票:https://jira.spring.io/browse/SPR-11833,并在此处查看实际示例:https://stackoverflow.com/questions/19485878/can-inject-be-made-optional-在jsr-330之类的autowire中必须为false (2认同)

Gra*_*ray 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");
        }
        ...
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 你可以反转依赖.每个可选类都可以在mainAppBean上设置自己.我在某些情况下使用它,直接依赖会导致循环或其他问题.

    <bean id="optionalBeanReference1" class="com.someapp.SomeClass">
        <constructor-arg index="0" ref="mainAppBean"/>
    </bean>
    
    Run Code Online (Sandbox Code Playgroud)

    然后在SomeClass中:

    public SomeClass(com.someapp.MyApplication mainAppBean) {
        mainAppBean.setOptionalBeanReference1(this);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 您可以保留直接依赖关系,然后导入一个定义了bean的文件,或者导入另一个文件,您可以使用工厂bean将bean定义为具有空值.请参阅此工厂代码.

祝好运.


ska*_*man 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样使用它:

<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>
Run Code Online (Sandbox Code Playgroud)


Bri*_*new 5

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

<property name="cache" value="#{getObject('optionalCache')}" />
Run Code Online (Sandbox Code Playgroud)

它利用了这种BeanExpressionContext.getObject()方法.有关详细信息,请参见此处