Vikdor
Vikdor

Reputation: 24134

Spring injects a one-element Set instead of an empty Set

We have an application where we are trying to inject an empty java.util.HashSet into a member of type java.util.Set, in a class which itself is a @Component. Spring seems to inject a HashSet with one element of the containing type. Any idea why Spring doesn't just inject an empty set?

Set element class:

@Component
public class SetElement
{
    private String value;

    public String getValue()
    {
        return value;
    }
}

Class that contains a Set as a member:

@Component
public class MyClassWithSet
{
    @Autowired
    private Set<SetElement> setOfElements;

    protected void setStringSet(Set<SetElement> stringSet)
    {
        this.setOfElements = stringSet;
    }

    public Set<SetElement> getStringSet()
    {
        return Collections.unmodifiableSet(setOfElements);
    }
}

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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
    ">

    <bean id="setOfElements" class="java.util.HashSet" />
    <context:component-scan base-package="com.vikdor.db " />
</beans>

Sample test case to confirm the behavior

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations =
{ "classpath:META-INF/spring.xml" })
public class SpringSetTest
{
    @Autowired
    private MyClassWithSet myClassWithSet;

    @Test
    public void test()
    {
        assertNotNull(myClassWithSet);
        assertNotNull(myClassWithSet.getStringSet());
        assertTrue(myClassWithSet.getStringSet().isEmpty());
    }

}

Upvotes: 2

Views: 1238

Answers (1)

nd.
nd.

Reputation: 8932

If you use @Autowired on a typed collection instance, then all beans in the application context that satisfy the type are injected:

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 [...] The same applies for typed collections:

public class MovieRecommender {

  private Set<MovieCatalog> movieCatalogs;

  @Autowired
  public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
      this.movieCatalogs = movieCatalogs;
  }

  // ...
}

http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/beans.html#beans-autowired-annotation

Thus, your single instance of SetElement is injected into the @Autowired Set<SetElement>. A possible solution would be to use a setter for the field. Alternatively, you could use the @Qualifier annotation or the @Resource annotation to refer to the bean by name.

Upvotes: 4

Related Questions