Dependency Injection Configuration — Annotation Based

Dharan Prajwal
5 min readJan 19, 2019

--

Java Spring

Had explained about XML based configuration for Dependency Injection in my previous blog. So, why do we require Annotation approach when we could use XML configuration for the same?

Here’s the answer:

When we are building a large application, the ApplicationContext.xml would be filled with a lot of Bean declarations and injections, making it complex to read for other developers in the team.

So, what’s the best approach? ( XML or Annotations )

Both have its own Pros and Cons. A mix of the two is a better approach where you could use the XML based configuration to pass parameters which could be changed in future like absolute file paths, DB auth parameters, etc. and Annotation based configuration for other purposes. Thus, we would have a flexibility to edit the variables just by editing the XML file without need to recompile the program.

Types of Injection through Annotation -

  1. Constructor Injection
  2. Setter Injection
  3. Field Injection

We could use any method above to achieve our goal! But, it’s the best practice to use one particular approach throughout an application to make it easily readable.

When to use What and Why?

  1. Constructor Injection: To be used for Mandatory Dependencies. Without the necessary dependencies to a class, the functionality of the class would be broken. This would mandate necessary dependency passing to the class whenever the class is being instantiated ( Even if we are using the new keyword while Unit testing ).
  2. Setter Injection: To be used for Optional Dependencies. It’s better to use setter injection if all the dependencies are optional.
  3. Field Injection: It’s better to avoid Field Injection. Reason — Java Spring is used to modularise the classes in a proper way, i.e. If we pass the correct dependencies and invoke the class using the “new” keyword, the module has to work as expected. However, if we are opting for Field injection then the parent calling the respective module won’t be aware of all the dependencies to the class and there’s a high probability that we may miss out to add a particular dependency while invoking the class using the new keyword. This is a major flaw in the design. This could have been easily avoided if we would have asked the parent to inject all the mandate dependencies through a Constructor Injection.

Okay, let’s get to work -

Here’s the lightweight ApplicationContext.xml, compared to XML approach:

<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd">
<!-- Points to the package where it recursively checks for all the annotations -->
<context:component-scan base-package="spring_app_annotation"></context:component-scan>
</beans>

And the Components ( Spring managed component )

/* Bank-1 */package spring_app_annotation;import org.springframework.stereotype.Component;@Component("Bank1")
public class Bank1 implements Bank{
private String b_name="Bank1"; @Override
public String getBankName() {
return b_name;
}
}/* Bank-2 */package spring_app_annotation;import org.springframework.stereotype.Component;@Component("Bank2")
public class Bank2 implements Bank{
private String b_name="Bank2"; @Override
public String getBankName() {
return b_name;
}
}

And here comes the Consortium, which requires the dependencies to be injected -

package spring_app_annotation;import java.util.List;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Component("consortiumBean")
public class Consortium
{
private List<Bank> banks;
private String as_of = "2019-01-01";

@Autowired
public Consortium(List<Bank> banks)
{
this.banks = banks;
}

public void getConsortiumDetails()
{
System.out.println("Consortium Details as of "+ as_of);
for(Bank bank:banks)
{
System.out.println("Name:"+bank.getBankName());
}
}
}

In the Above code, we have Autowired the Constructor, i.e. we have used Constructor Injection approach. Spring internally, would look out for the Bank components and pass them as a List to the constructor.

It seems that as_of can change frequently and we need to make it more configurable than hard coding it into the component.

Okay, lets modify the above code a bit ( changes marked in Italic bold )

package spring_app_annotation;import java.util.List;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Component("consortiumBean")
@PropertySource("classpath:/spring_app_annotation/my.properties")
public class Consortium
{

private List<Bank> banks;
private String as_of = "";

@Autowired
public Consortium(List<Bank> banks,@Value("${my.property.name}")
String as_of)
{
this.banks = banks;
this.as_of = as_of;
}

public void getConsortiumDetails()
{
System.out.println("Consortium Details as of "+ as_of);
for(Bank bank:banks)
{
System.out.println("Name:"+bank.getBankName());
}
}
}

And the corresponding property file goes as ( /spring_app_annotation/my.properties )

my.property.name=2019-01-01

And finally the Parent Class of the Program ( MyApp.java )

package spring_app_annotation;import org.springframework.context.support.ClassPathXmlApplicationContext;public class MyApp
{
public static void main(String args[])
{
ClassPathXmlApplicationContext context = new
ClassPathXmlApplicationContext("applicationContext.xml");

Consortium bank =
context.getBean("consortiumBean",Consortium.class);
bank.getConsortiumDetails();
context.close();
}}

And we get the Expected Output

Consortium Details as of 2019–01–01

Name:Bank1

Name:Bank2

In the above Example as we have used Constructor Injection, we are making sure that all the mandatory dependencies to the module ( component ) is passed even if the parent invokes the class using the “new” keyword without the spring framework.

Bean Scopes, Init-method and Destroy-method

By Default the scope of the Bean is Singleton [ In object-oriented programming, a singleton class is a class that can have only one object at a time.]

We can define the scope of the Bean through Scope(“<scope_name>”) annotation

/*Consortium.java*/package spring_app_annotation;import java.util.List;import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component("consortiumBean")
@PropertySource("classpath:/spring_app_annotation/my.properties")
@Scope("prototype")
public class Consortium
{
...

The possible scopes to the Bean:

Singleton

Prototype

Request

Session

Global Session

Init-method ( PostConstruct annotation ) and Destroy-method ( PreDestroy annotation )

/*Consortium.java*/package spring_app_annotation;import java.util.List;import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component("consortiumBean")
@PropertySource("classpath:/spring_app_annotation/my.properties")
public class Consortium
{
private List<Bank> banks;
private String as_of = "";
@Autowired
public Consortium(List<Bank> banks,@Value("${my.property.name}")
String as_of)
{
this.banks = banks;
this.as_of = as_of;
}
@PostConstruct
public void callMeBefore()
{
System.out.println("I'm called before");
}
@PreDestroy
public void callMeAfter()
{
System.out.println("I'm called after");
}
public void getConsortiumDetails()
{
System.out.println("Consortium Details as of "+ as_of);
for(Bank bank:banks)
{
System.out.println("Name:"+bank.getBankName());
}
}
}

And the Output

I’m called before

Consortium Details as of 2019–01–01

Name:Bank1

Name:Bank2

I’m called after

Note: Prototype Bean doesn’t call the Destroy method. The Init and Destroy methods are supposed to be No-Arg methods.

Now, there might arise a question to you on how do we inject a specific Bank to the Consortium instead of both the Banks… Hmm! Good question :)

Qualifier Annotation is used in this scenario -

@Autowired
public Consortium(@Qualifier(value="Bank1") List<Bank> banks,@Value("${my.property.name}") String as_of)
{
this.banks = banks;
this.as_of = as_of;
}

Output after this change would be

I’m called before

Consortium Details as of 2019–01–01

Name:Bank1

I’m called after

Happy Coding!!

If you would like to read about creating Custom Validations in Spring MVC, my next blog is the one you are looking for!

Please feel free to correct or leave your suggestions in comments below

--

--

Dharan Prajwal

Senior Software Engineer at Luxoft | Ex- Envestnet Yodlee | Freelancer | Knowledge Seeker