Search code examples
springspring-el

injecting a spring bean property different values according to its context


I have a spring bean my_bean with a property my_map, and I want to inject it with the value "X" or with the value "Y". The bean:

<bean id="my_bean">
  <property name="my_map">
    <map>
      <entry key="p" value="X" />
    </map>
  </property>
</bean>

It's referenced in a very deep hierarchy by the bean root_a:

<bean id="root_a">
  <ref bean="root_a_a"/>
</bean>
<bean id="root_a_a">
  <ref bean="root_a_a_a"/>
</bean>
<bean id="root_a_a_a">
  <ref bean="my_bean"/>
</bean>

and this entire deep hierarchy is referenced again from the bean root_b. In the ref of my_bean from this hierarchy I would the property to be injected with the value "Y", but I would not like to duplicate the entire hierarchy twice.

<bean id="root_b">
  <ref bean="root_a_a"/>
</bean>

How do I do this in the spring XML? can you think of a clever spring EL solution? something else? I prefer all my configuration to be done in the XML and no Java code...


Solution

  • By default Spring beans are singletons, which means that once bean="my_bean" is created it is shared between other components e.g. shared between A => bean id="root_a_a_a" and B => bean id="root_b_b_b"

    The answer to your question depends on what exactly you are trying to achieve.

    Two Beans

    If bean="my_bean" does not need to be shared between A and B, then create two beans:

    inject this one to A

    <bean id="myBeanX" class="My">
      <property name="culprit" value="X"/>
    </bean>
    

    and this one to B

    <bean id="myBeanY" class="My">
      <property name="culprit" value="Y"/>
    </bean>
    

    notice they both are instances of the same class.

    You can also inline them into collaborators (A / B) if you don't need them for anything else:

    <bean id="root_a_a_a">
      <constructor-arg>
        <bean class="My">
          <property name="culprit" value="X"/>
        </bean>
      </constructor-arg>
    </bean>
    

    You can also have a factory bean that creates root_a_a_a given the property for a class My, but that would be an overkill.

    Single Bean

    In case A and B needs to share the exact same reference to bean="my_bean", the question is: are you ok with A and B changing my_bean's state after my_bean is created? Probably not.

    If you are, which would be 0.41172% chance, you can change my_bean's value to whatever you need in A's or B's constructors => would not recommend

    Hence you either would go with the Two Bean approach (which is most likely what you want), or you would need to refactor a property for "X" and "Y" into another e.g. myConfig component.


    EDIT after the question was edited

    If root_a and root_b will not be used together in the same instance of the context,you can use Spring Profiles (example), or SpEL / Property Based solutions (example)

    e.g.

    <bean id="my_bean">
      <property name="my_map">
        <map>
          <entry key="p" value="${ENV_SYSTEM:X}" />
        </map>
      </property>
    </bean>
    

    It will set it to X by default, unless a ENV_SYSTEM system variable is set (e.g. to Y).