I am currently improving my Spring knowledge. I wonder what really happens when I use Spring annotation @Autowire on a field.
Here is a piece of code :
OutputHelper file
@Component
public class OutputHelper {
    @Autowired
    @Qualifier("csvOutputGenerator")
    private IOutputGenerator outputGenerator;
    public void setOutputGenerator(IOutputGenerator outputGenerator) {
        this.outputGenerator = outputGenerator;
    }
    // I can focus only on what my code do because my objects are injected
    public void generateOutput(){
        outputGenerator.generateOutput();
    }
}
CsvOutputGenerator file
@Component 
public class CsvOutputGenerator implements IOutputGenerator {
    public void generateOutput(){
        System.out.println("Csv Output Generator");
    } 
}
Application file
public static void main(String[] args) {
    // Create the spring context
    ApplicationContext context = new ClassPathXmlApplicationContext("META-INF/spring/spring-module.xml");
    // Get the configured OutpuHelper from the spring-module.xml
    OutputHelper output = (OutputHelper) context.getBean("outputHelper");
    // Display output from the output configured
    output.generateOutput(); 
}
My configuration file just contain <context:component-scan base-package="com.xxx.xxx.output"/>
When I execute this code all work fine. But what makes me surprised is when I delete the setOutputGenerator in OutPutHelper file, my piece of code keeps working. I tought that with this configuration, the OutputHelper was first created with default constructor and initialized with setter.
I expected an error because the variable outputGenerator was not be able to be initialized.
Is anyone can help me to understand ?
The idea to have fields @Autowired is questionable. It works, but it will difficult other aspects of your implementation (i.e. testing).
There are 3 types of injections:
fields - basically configured applying reflection (Field.set(Object, Object)) directly to the field:
@Autowired
private MyInterface field;
setters - with this approach the configuration of each dependency goes through a property (spring goes through all methods and execute each one annotated with @Autowired using Method.invoke(Object, Object...), thus its value is configured using its setter as follows:
@Autowired
public void setField(MyInterface value) { 
    this.field = value;
}
constructors - the last, and my preferable approach, the constructor injection. That one basically annotates an constructor with @Autowired and instead of using methods or fields, you can configure your bean directly on your constructor. For that spring will elect the a constructor to be used to instantiate your @Component, and it will use an @Autowired if existent or a empty params constructor, invoking it using Constructor.newInstance(Object...). Example:
@Component
public class Implementation {
    private MyInterface field;
    @Autowired
    public Implementation(MyInterface value) {
        Assert.notNull(value, "value should not be null");
        this.field = value;
    }
}
One of the ideas behind Inversion of Control (or Dependence Injection) is to be able to isolate a piece of code in order to provide decent test implementation support.
In order to go deeper, it is necessary to comment that during a unit test you want the class in its isolated form, all you will use with that class are basically mocks for its dependencies (injections).
So, what are the results:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With