tags:

views:

58

answers:

3

In my application I am using ContextLoaderListener to load context files from many jars using:

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

This means I can reference beans from other jars without doing import.

In the application there are multiple deployment options and in some deployments jars can be excluded. To support that I would like some bean references to be optional. For example:

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

In the example above I would like to have optionalBeanReference1 equal null if the reference was not found (mark it optional in some way)

Can this be done in Spring? or what method do you recommend for handling dynamic references?

A: 

My best guess is to use autowire-ing with required false. Don't know how you can express this in XML but using annotation configuration this would look like:

@Autowired(required=false)
Cristian Vrabie
Oh, yea, and setter injection is probably better for this type of situations.
Cristian Vrabie
This sounds good however I am unable to use Spring annotations in the project for now.
mb
A: 

I think @cristian's @Autowired answer is the right one. That will call the setter methods if the beans of that type are available. If you have multiple beans of the same type however, I believe Spring throws an exception. If you cannot use @Autowired for this or some other reason, I see a couple of solutions:

  1. You could make your class ApplicationContextAware and lookup the beans in the context yourself:

    public void setApplicationContext(ApplicationContext applicationContext) {
        if (applicationContext.containsBean("optionalBeanReference1")) {
            setOptionalBeanReference1(
                (OptionalBeanReference1)applicationContext.bean(
                    "optionalBeanReference1");
        }
        ...
    }
    
  2. You could invert the dependency. Each of the optional classes could set themselves on the mainAppBean. I use this in certain situations when a direct dependency would cause loops or other problems.

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

    Then in the SomeClass:

    public SomeClass(com.someapp.MyApplication mainAppBean) {
        mainAppBean.setOptionalBeanReference1(this);
    }
    
  3. You could stay with your direct dependency and then either import a file with the beans defined or import another file where you define the beans as having null values by using a factory bean. See this factory code.

Good luck.

Gray
I used something similar to the first option, where after the bean is loaded I check if other beans exist as well. This seems like the best way without annotations
mb
A: 

There's no built-in mechanism for this. However, you could write a pretty trivial FactoryBean implementation to do this for you, something like this:

public class OptionalFactoryBean extends AbstractFactoryBean<Object> implements BeanNameAware {

    private String beanName;

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

    }

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

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

You can then use it like this:

<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>
skaffman