Autowire reference beans into list by type
Asked Answered
S

2

90

I have one class that has a list of objects of Daemon type.

class Xyz {    
    List<Daemon> daemons;
}

My spring configuration looks like this.

<bean id="xyz" class="package1.Xyz">
   <property name="daemons" ref="daemonsList">
</bean>

<bean id="daemon1" class="package1.DaemonImpl1"/>
<bean id="daemon2" class="package1.DaemonImpl2"/>

<bean id="daemonsList" class="java.util.ArrayList">
        <constructor-arg>
            <list>
                <ref bean="daemon1" />      
                <ref bean="daemon2" />
            </list>
        </constructor-arg>
</bean>

Now instead of explicitly wiring each daemon implementation in list, is it possible to autowire all beans of type Daemon automatically in list. Problem I am trying to solve is, If someone creates a bean of new implementation of Daemon class and forgets to wire it into list.

I have seen this question somewhere on stackoverflow but not able to find that again. Apologies for it.

Scurrilous answered 16/9, 2011 at 14:36 Comment(2)
See #1363810Hyphen
Thanks skaffmen. I would try to understand the concept involved here.Scurrilous
H
91

It should work like this (remove the ArrayList bean from your XML):

public Class Xyz {    

    private List<Daemon> daemons;

    @Autowired
    public void setDaemons(List<Daemon> daemons){
        this.daemons = daemons;
    }

}

I don't think there's a way to do this in XML.


See: 3.9.2. @Autowired and @Inject:

It is also possible to provide all beans of a particular type from the ApplicationContext by adding the annotation to a field or method that expects an array of that type:

public class MovieRecommender {

  @Autowired
  private MovieCatalog[] movieCatalogs;

  // ...
}

The same applies for typed collections:

public class MovieRecommender {

  private Set<MovieCatalog> movieCatalogs;

  @Autowired
  // or if you don't want a setter, annotate the field
  public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
      this.movieCatalogs = movieCatalogs;
  }

  // ...
}

BTW, as of Spring 4.x, these lists can be ordered automatically using the @Ordered mechanism.

Housecarl answered 16/9, 2011 at 14:40 Comment(4)
Thanks Sean. It worked. I thought, Adding @Autowired notation to List<Daemons> daemons would make spring search for beans which are of type java.util.List. It's amazing, how it finds out type of objects in list and wires them into list and finally wires list into main object.Scurrilous
@al. no prob. I use this feature a lot myselfHousecarl
When autowiring "List<Daemon> daemons", how does Spring know that it should look for beans of type Daemon, as this is generic type and will not be available at runtime?Nicky
@elyor that's not quite correct. Objects are subject to type erasure, fields and methods aren't. Reflection gives you the means through methods like Method.getGenericParameterTypes().Housecarl
S
5

Well, this can be achieved in two ways as stated in Spring Documentation.

Below is the excerpt from the documentation.

With byType or constructor autowiring mode, you can wire arrays and typed collections.

1. autowire="byType"

Autowiring using "byType" can be achieved if the type of the bean defined in the xml matches the type of list.

Example:

Motor.java

package com.chiranth;
public interface Motor 
{
   public void start();
}

ElectricMotor1.java

package com.chiranth;
public class ElectricMotor1 implements Motor
{
     public void start() 
     { 
         System.out.println("Motor 1 Started.");
     }
}

ElectricMotor2.java

package com.chiranth;
public class ElectricMotor2 implements Motor
{
    public void start() 
    {
        System.out.println("Motor 2 Started.");
    }
}

TeslaModelX.java

package com.chiranth;
import java.util.List;
public class TeslaModelX 
{
    private List<Motor> motor;

    public List<Motor> getMotor()
    {
        return motor;
    }

    public void setMotor(List<Motor> motor) 
    {
        this.motor = motor;
    }

    public void goForward()
    {
        for(Motor m :motor)
            m.start();
        System.out.println("Going Forward.");
    }
}

Spring.xml

<?xml version = "1.0" encoding = "UTF-8"?>

<beans xmlns = "http://www.springframework.org/schema/beans"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xmlns:p="http://www.springframework.org/schema/p"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="electricMotor1" class="com.chiranth.ElectricMotor1"/>
    <bean id="electricMotor2" class="com.chiranth.ElectricMotor2"/>

    <bean id="modelX" class="com.chiranth.TeslaModelX" autowire="byType"/>
</beans>

Test.java

package com.chiranth;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test 
{
    public static void main(String[] args) 
    {
        ApplicationContext context= new ClassPathXmlApplicationContext("Spring.xml");
        TeslaModelX modelx=(TeslaModelX)context.getBean("modelX");
        modelx.goForward();
    }
}

OUTPUT:

Motor 1 Started.
Motor 2 Started.
Going Forward.

2. autowire="constructor"

Autowiring using "constructor" can be achieved if the type of the bean defined in the xml matches the type of the argument in the constructor.

Example:

Considering the above Motor.java , ElectricMotor1.java and ElectricMotor2.java.

TeslaModelX.java

package com.chiranth;
import java.util.List;
public class TeslaModelX 
{
    private List<Motor> motor;

    public TeslaModelX(List<Motor> motor)
    {
        this.motor=motor;
    }

    public void goForward()
    {
        for(Motor m:motor)
            m.start();
        System.out.println("Going Forward.");
    }
}

Spring.xml

<beans xmlns = "http://www.springframework.org/schema/beans"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xmlns:p="http://www.springframework.org/schema/p"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="electricMotor1" class="com.chiranth.ElectricMotor1"/>
    <bean id="electricMotor2" class="com.chiranth.ElectricMotor2"/>

    <bean id="modelX" class="com.chiranth.TeslaModelX" autowire="constructor"/>
</beans>

Test.java

package com.chiranth;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test 
{
    public static void main(String[] args) 
    {
        ApplicationContext context= new ClassPathXmlApplicationContext("Spring.xml");
        TeslaModelX modelX=(TeslaModelX)context.getBean("modelX");
        modelX.goForward();
    }
}

OUTPUT:

Motor 1 Started.
Motor 2 Started.
Going Forward.
Simonides answered 18/7, 2019 at 16:36 Comment(1)
Thanks for the answer. Question for either solutions: would this work if "electricMotor1" and "electricMotor2" ... "electricMotorN" are defined not in the same spring.xml file but share the same ApplicationContext Container?Hatch

© 2022 - 2024 — McMap. All rights reserved.