Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Autowiring does not happen if there is another bean of the same class with property set manually

I want to create two beans of the same class through xml config file. The class has an annotated setter to be filled by spring. In one of the bean definitions I also provide value manually to override one from annotation. But when I do this, spring no longer handles annotation wiring.

Here is a minimal code to demonstrate this effect, used @Value for simplicity but it's the same with @Autowired:

import org.springframework.beans.factory.annotation.Value;

import javax.annotation.PostConstruct;

public class AutowireTest {
  public String testField;

  @PostConstruct
  public void init() {
    if (testField == null)
      throw new RuntimeException("FAIL");
  }

  @Value("default")
  public void setTestField(String testField) {
    this.testField = testField;
  }
}

and spring config:

<bean id="au_test1" class="AutowireTest">
  <property name="testField" value="manual"/>
</bean>

<bean id="au_test2" class="AutowireTest"/>

If I remove <property name="testField" value="manual"/>, both beans receive "default". If it's there the second bean throws an exception. I had a look at spring code and AutowiredAnnotationBeanPostProcessor is using injectionMetadataCache where classes are keys for injection metadata meaning setting particular property for one bean disables autowiring for the other instances.

Any idea why it's this way? How can I achieve a similar effect working not necessarily for string values but also for object beans?

EDIT: I want to have a property where there are multiple matching candidates. One is marked as primary. Unless I specify a candidate manually through xml, I would like to have the primary one wired. My original approach was to use @Autowired for this property but since this does not work I'm looking for an alternative. For some reason I don't want to use bean inheritance.

EDIT2: If I swap these two bean definitions, the problem does not occur. The property is autowired fine until manual override is detected for the first time. This means this is not an intented feature since it can lead to weird and hard to detect bugs in some projects with dependencies not being wired as expected.

like image 860
mrembisz Avatar asked Feb 28 '12 16:02

mrembisz


1 Answers

If you take a look at the javaDocs for the AutowiredAnnotationBeanPostProcessor, there is a note stating:

"Annotation injection will be performed before XML injection; thus the latter configuration will override the former for properties wired through both approaches."

This means that setting your beans property in xml with and with out a property value for "testField" makes a two new beans one where testField is set and one where it is null.

Its a one or the other thing. you can either use autowiring for a class or set it up manually in xml but cant do both. Im guessing that when your removing the property spring is just setting the same autowired bean to both names.

Once the beans of the same class are set up manually you can inject them into other beans using the @Qualifier("au_test1") annotation

like image 78
Sebastien Avatar answered Oct 13 '22 20:10

Sebastien