Jim Collins
Jim Collins

Reputation: 280

Spring configuration pass property to referenced bean

I have the following setup:

<bean id="b1" class="SomeClass">
    <property name="foo" ref="foo1"/>
</bean>

<bean id="b2" class="SomeClass">
    <property name="foo" ref="foo2"/>
</bean>

<bean id="foo1" class="FooClass">
    <constructor-arg index="0"><ref bean="dataSource1"/></constructor-arg>
    ...other constructor args
</bean>

<bean id="foo2" class="FooClass">
    <constructor-arg index="0"><ref bean="dataSource2"/></constructor-arg>
    ...other constructor args
</bean>

Is there a way I can avoid duplicating the FooClass? What I want to do in bean b1 and b2 is add the reference to FooClass but specify the data source all other constructor arguments are the same.

Thanks

Jim

Upvotes: 2

Views: 4623

Answers (4)

pappu_kutty
pappu_kutty

Reputation: 2488

what you are doing here is , autowiring class with constructure, as aviad said , you can use setter and getter method for your datasource injections

<bean id="foo" class="FooClass">
    <constructor-arg index="0">datasource</constructor-arg>
    ...other constructor args
</bean>

   <bean>your datasource bean1</bean>
   <bean>your datasource bean2</bean>

and in your implementation you can set your data source as below

@Autowire
private FooClass foo;

foo.setDataSource(datasourcebean1);

you fooClass

public void FooClass(Datasource datasource){

      private Datasource datasource;

      public void setDatSource(Datasource datasource);

      public Datasource  getDataSource();
}

EDIT- as per spring documentation, you can pass constructor argument if that doesnt change in terms of its value. But in your case for FooClass you want to pass different datasource at different occasion (hope i get it correctly), so in this case you need to just pass datasouce instance either datasource 1 or datasource 2 during spring initialization, as spring will expect constructor argument while initialing FooClass. later during runtime pass different datasource and set your datasource using setter method.

bean spring config

  <bean id="foo" class="FooClass">
        <constructor-arg index="0" ref="datasource1"></constructor-arg>
        ...other constructor args
    </bean>

public class FooClass(){
      // on spring initialization, it will inject datasource1 
      public void FooClass(DataSource dataSource){
      }
      have your setter and getter method for datasource
}

where in your calling service

public class dataBaseInvoke(){

public Datasource datasource2

public FooClass fooClass;

inside method{
   fooClass.setDatasource(datasource2);
   fooClass.addFoo();
}
}

Upvotes: 1

Francisco Spaeth
Francisco Spaeth

Reputation: 23893

Considering your implementation, you may want to go for the Bean Definition Inheritance.

From Spring documentation:

A bean definition can contain a lot of configuration information, including constructor arguments, property values, and container-specific information such as initialization method, static factory method name, and so on. A child bean definition inherits configuration data from a parent definition. The child definition can override some values, or add others, as needed. Using parent and child bean definitions can save a lot of typing. Effectively, this is a form of templating.

Basically what it says is that you could have a kind of "Template for your Bean Definition" mark it as abstract and use it in other compatible beans as parent to inherit those configurations.

This sample was taken from spring documentation:

<bean id="inheritedTestBean" abstract="true"
      class="org.springframework.beans.TestBean">
  <property name="name" value="parent"/>
  <property name="age" value="1"/>
</bean>

So at this point this bean isn't instantiated and will be used for bean definition inheritance purpose only.

<bean id="inheritsWithDifferentClass"
      class="org.springframework.beans.DerivedTestBean"
      parent="inheritedTestBean" init-method="initialize">

  <property name="name" value="override"/>
  <!-- the age property value of 1 will be inherited from  parent -->

</bean>

Here you could see that org.springframework.beans.DerivedTestBean is used to instantiate the bean but it will use all definition of the parent, plus, it will override the property name. The class doesn't needs to be specified on the parent, but if specified on the parent (inheritedTestBean) and not on the child (inheritsWithDifferentClass) the parent class will be used to instantiate the child. Not everything is inherited as we can see here:

A child bean definition inherits constructor argument values, property values, and method overrides from the parent, with the option to add new values. Any initialization method, destroy method, and/or static factory method settings that you specify will override the corresponding parent settings.

The remaining settings are always taken from the child definition: depends on, autowire mode, dependency check, singleton, scope, lazy init.

Here is a sample using your classes:

<!-- the way you are already using it -->
<bean id="b1" class="SomeClass">
  <property name="foo" ref="foo1"/>
</bean>

<!-- if you use it just once, you could declare it inside the bean that uses it -->    
<bean id="b2" class="SomeClass">
  <property name="foo">
    <bean id="foo1" class="FooClass" parent="foo">
      <constructor-arg index="0"><ref bean="dataSource1"/></constructor-arg>
    </bean>
  </property>
</bean>

<!-- here no class definition, only the default configuration -->
<bean id="foo" abstract="true">
  <!-- constructor arg 0 is defined only on child beans -->
  <constructor-arg index="1" value="whatever1" />
  <constructor-arg index="2" value="whatever2" />
  <constructor-arg index="3" value="whatever3" />
  <constructor-arg index="4" value="whatever4" />
</bean>

<bean id="foo2" class="FooClass">
  <constructor-arg index="0"><ref bean="dataSource2"/></constructor-arg>
</bean>

Upvotes: 0

Luca Basso Ricci
Luca Basso Ricci

Reputation: 18383

Use abstract bean

<bean id="foo" class="FooClass">
  // Set all properties except datasoure
  <property name="..." />
</bean>

<bean id="foo1" parent="foo">
  <property name="datasource" ref="ds1" />
</bean>
<bean id="foo2" parent="foo">
  <property name="datasource" ref="ds2" />
</bean>

Of course you have to use empty contructor instantiation and expose FooClass properties with accessors. If you don't need foo1 and foo2 in other places go for inner beans.

Upvotes: 0

aviad
aviad

Reputation: 8278

If you want some member of your class to be dynamically initialized\populated on every call to the corresponding getter, you can try the Lookup Method Injection. Read pp. 3.3.4.1 here.

So even if the class that contains the dynamic member was created in scope=singletone (the default for spring bean container) every time you will acces the field that has a lookup method assigned, you will get an appropriate object according to the business logic implemented inside the lookup method.

Also, I found a good example in Spring documentation - I think it is very clear. Take a look at "3.4.6.1 Lookup method injection"

Upvotes: 1

Related Questions