Spring

Dependency Injection using Annotations in Spring Framework

It’s one of the ways to implement dependency injection in which we use Java-Based Annotations to define our dependencies or beans.Instead of defining beans in an XML configuration file, here we define beans in a java class.Let ‘s understand it in a bit more detail with the help of below example and then we will differentiate it with our Dependency Injection using XML example from my previous post.

The example of Dependency Injection without Spring, which we have seen in this post, let’s re-write it using spring framework by our 2nd method that is “Explicit configuration in Java”.

Phone.java

package com.geekslab.device;

public interface Phone {

public void openApp(int number);

}

BasicPhone.java

package com.codegeekslab.type;

import com.geekslab.os.Phone;

public class BasicPhone implements Phone {

@Override
public void openApp(int number) {
System.out.println("calling via simcard... " + number);
}

}

SmartPhone.java

package com.codegeekslab.type;

import com.geekslab.device.Phone;

public class SmartPhone implements Phone {

@Override
public void openApp(int number) {
System.out.println("calling via whatsapp..." + number);
}

}

CallingApp.java

package com.codegeekslab.app;

import com.geekslab.device.Phone;

public class CallingApp {

private Phone phone;

public CallingApp(Phone phone) {
this.phone = phone;
}

public void makeCall(int number) {
phone.openApp(number);
}

}

CellPhoneConfiguration.java


package com.codegeekslab.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import com.codegeekslab.app.CallingApp;
import com.codegeekslab.type.BasicPhone;
import com.codegeekslab.type.SmartPhone;
import com.geekslab.device.Phone;

@Configuration
public class CellPhoneConfiguration {

@Bean
public BasicPhone smartPhone() {
return new BasicPhone();
}

@Bean
public CallingApp callingApp(Phone phone) {

return new CallingApp(phone);
}

}

Test.java


package com.codegeekslab.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

import com.codegeekslab.app.CallingApp;
import com.codegeekslab.configuration.CellPhoneConfiguration;

public class Test {

public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan("com.codegeekslab.configuration");
context.refresh();
CallingApp callingApp = context.getBean("callingApp", CallingApp.class);
callingApp.makeCall(99999);

}
}

Output


Apr 02, 2017 12:52:43 PM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@378fd1ac: startup date [Sun Apr 02 12:52:43 IST 2017]; root of context hierarchy
calling via simcard...99999

If you see there are just 2 changes, one is new CellPhoneConfiguration.java and another is modified Test.java, apart from this everything is exactly same.

To understand these 2 changes, that are CellPhoneConfiguration.java and Test.java, we first need to understand two important concepts of spring framework.


1.What is Bean in Spring Framework?

Beans are the java objects that are created and managed by spring container.

2.What is Spring container in Spring Framework?

Spring container is the one who creates and manages the beans.Apart from creating and managing the beans, it is also the place where all spring beans live.


Every Spring application contains beans and container.

Beans are defined in a configuration file which can be an annotation based defined in a java class or can be an XML (if you are using the 1st method to implement dependency injection).The above spring application is an annotation based, so we are going to define beans in a java configuration class.

Let’s understand how we have defined beans and spring container in above application example.

We have converted this example of Dependency Injection without spring from our previous post into a spring application by re-writing Test.java and adding one extra file CellPhoneConfiguration.java.

In this example, we have defined beans in CellPhoneConfiguration.java and spring container named as ApplicationContext in Test.java.

Let’s first understand CellPhoneConfiguration.java in detail and then we will move on to Test.java to understand spring container in detail.

In our example of Dependency Injection without spring from our previous post, we had defined our Test.java as below to achieve dependency injection.

CallingApp smartPhone = new CallingApp(); —-1st object
smartPhone.setPhone(new BasicPhone()); ——2nd object and constructor injection
smartPhone.makeCall(99999);

In this class of main we are creating two objects one is of CallingApp and another of BasicPhone and we are performing
Dependency Injection by passing object BasicPhone into the setPhone method of CallingApp class.

The number of objects requires to make a call is two, one is of  CallingApp and another BasicPhone, so we would require to create two beans of these two classes.So now in CellPhoneConfiguration.java, we will define these two objects of CallingApp and BasicPhone and we will wire(another term of injecting) them.

CellPhoneConfiguration.java


@Configuration
public class CellPhoneConfiguration {

@Bean
public CallingApp basicPhone() {

Phone phone = new BasicPhone();

return new CallingApp(phone);
}

@Bean
public CallingApp smartPhone() {

Phone phone = new SmartPhone();

return new CallingApp(phone);
}

}

Let’s understand the above class and how we have defined the beans and wired them.

To make define any class as java configuration file we have to annotate that class by @Configuration which informs spring container that this a java configuration class and you can look for beans in this class.

To define any object as bean we need to define that object in java configuration file like below


@Bean
public BasicPhone basicPhone() {
return new BasicPhone();
}

@Bean
public CallingApp callingApp(Phone phone) {

return new CallingApp(phone);
}

@Bean annotation is used to define a bean.it should be placed on top of a method of which you want to create a bean and that method must return an object.

The name of the bean will be the name of the method and if you want a custom name then you can use @Bean(name=”name of the bean”) annotation.So here we have defined a bean named as “basicPhone” and “callingApp“.

Now the important point, When a spring container(explained below) loads this class, first it registers basicPhone bean into its container and then when it tries to register the second bean callingApp it finds that callingApp method requires a Phone object, then it searches any bean in its container which can be passed into callingApp method,and as we have registered basicPhone bean before which can be passed into callingApp method as it implements the Phone interface, spring container passes the basicPhone bean into callingApp method and creates the second bean callingApp.

Note: In this class, we have just defined the beans.Bean creation will happen when spring container(explained below) will load this class. So here spring container will create two beans one is basicPhone and another one callingApp by wiring basicPhone into it.

 

So,


@Bean
public BasicPhone basicPhone() {
return new BasicPhone();
}

@Bean
public CallingApp callingApp(Phone phone) {

return new CallingApp(phone);
}

[/xml]

Is Equivalent of


CallingApp smartPhone = new CallingApp();
smartPhone.setPhone(new BasicPhone());

Comparison with XML


@Bean
public BasicPhone basicPhone() {
return new BasicPhone();
}

@Bean
public CallingApp callingApp(Phone phone) {

return new CallingApp(phone);
}

This annoation based java configuration is equivalent to following XML based configuration.


<bean id="BasicPhone" class="com.codegeekslab.type.BasicPhone"/>
<bean id="CallingApp" class="com.codegeekslab.app.CallingApp">
<constructor-arg ref="BasicPhone"/>
</bean>

Now we have defined all our beans, it’s time to create these defined beans by loading this CellPhoneConfiguration class into spring container “ApplicationContext”  which I have defined in Test.java.

Before we jump into Test.java let’s understand more about Spring Containers and their types.

Types of Spring Container:
In Spring there are two types of container Application context and Bean Factory.

Bean Factory – it is a basic and low-level container thus providing basic support for dependency injection.We won’t discuss much on Bean Factory and mostly
advised not to use this container.

Application context – it is a high level, advanced and more powerful than bean factory, apart from supporting Dependency injection supports reading messages from a property file.

Types of Application Context: There are several flavors of application context we will discuss now the popular ones and the ones
which we are more likely to use in our projects.

1.ClassPathXmlApplicationContext:it loads the context definitions like beans from one or more XML files located in the classpath.Like in our example we are using beans.xml as our source of the XML file and loading few beans such as CallingApp.

2.GenericXmlApplicationContext:it’s kind of similar to ClassPathXmlApplicationContext and a better alternative of all application context.

3.FileSystemXmlApplicationContext:if you want to load your XML file from filesystem instead of classpath you can use this FileSystemXmlApplicationContext in which you have to give complete path of XML file like while loading the XML file like below.

ApplicationContext context = new FileSystemXmlApplicationContext(“C:/codegeekslab/beans.xml”);

4.AnnotationConfigApplicationContext:it is used when we are using java configuration classes instead of xml files to define our beans.that we wills ee in a moment.

5.AnnotationConfigWebApplicationContext:it’s used in Spring MVC based web application for loading beans from java configuration classes by using 2nd and 3rd methods.

6.XmlWebApplicationContext: it’s used in Spring MVC based web application for loading beans from xml file .

Now let’s understand our main class Test which contains ApplicationContext.

Test.java


package com.codegeekslab.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

import com.codegeekslab.app.CallingApp;
import com.codegeekslab.configuration.CellPhoneConfiguration;

public class Test {

public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan("com.codegeekslab.configuration");
context.refresh();
CallingApp callingApp = context.getBean("basicPhone", CallingApp.class);
callingApp.makeCall(99999);

}
}

Let’s understand what happens in background which when we instantiate an application container and load beans.xml

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

A particular type of spring container gets instantiated which in this case is AnnotationConfigApplicationContext which is
going to manage the beans defined in CellPhoneConfiguration.java.

Now the next step is to scan for beans from our application. To do that we have to call scan method of  AnnotationConfigApplicationContext class and pass the package name where we have defined configuration class which contains all the bean definitions and then we have to call refresh method which loads the beans into spring container.

As we have written our java configuration class and other beans in com.codegeekslab.configuration package we will pass the same.


context.scan("com.codegeekslab.configuration");
context.refresh();

 

Now the final step is to get the bean of CallingApp to make the call using makeCall() method, So to get the bean we
need to call the getBean(“id of the bean”) method and pass the id of bean which we want to get which in the case
is basicPhone.

The return type of getBean method is java.lang.Object so we need to cast to its actual type before using it.

CallingApp callingApp = (CallingApp) context.getBean("CallingApp");

There is also an another variation where to don’t need to cast but just pass the class name.

CallingApp callingApp = context.getBean("CallingApp",CallingApp.class);

Now we have got the bean or object of CallingApp class we can now call the makeCall method.

callingApp.makeCall(77777777);

Resolving Ambiguities using Qualifier

In our above example i had defined only one bean that is basicPhone in the configuration class,What if I have more than one bean of type Phone which can be passed into the callingApp method or we can say can be wired into the callingApp bean in the CellPhoneConfiguration class like below?

CellPhoneConfiguration.java


public class CellPhoneConfigurationNew {

@Bean
public SmartPhone smartPhone() {
return new SmartPhone();
}

@Bean
public BasicPhone basicPhone() {
return new BasicPhone();
}

@Bean
public CallingApp callingApp(Phone phone) {

return new CallingApp(phone);
}

}

As you can see from the above class, both the beans smartPhone and basicPhone are eligible for wiring with callingApp bean as both BasicPhone and CallingApp class implements Phone Interface, So in this situation, the spring container gets confused and throw an org.springframework.beans.factory.UnsatisfiedDependencyException exception.

Output


WARNING: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'callingApp' defined in com.codegeekslab.configuration.CellPhoneConfigurationNew: Unsatisfied dependency expressed through method 'callingApp' parameter 0: No qualifying bean of type [com.geekslab.device.Phone] is defined: expected single matching bean but found 2: smartPhone,basicPhone; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.geekslab.device.Phone] is defined: expected single matching bean but found 2: smartPhone,basicPhone
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'callingApp' defined in com.codegeekslab.configuration.CellPhoneConfigurationNew: Unsatisfied dependency expressed through method 'callingApp' parameter 0: No qualifying bean of type [com.geekslab.device.Phone] is defined: expected single matching bean but found 2: smartPhone,basicPhone; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.geekslab.device.Phone] is defined: expected single matching bean but found 2: smartPhone,basicPhone
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:749)

As you can see from the exception stacktrace  No qualifying bean of type [com.geekslab.device.Phone] is defined: expected single matching bean but found 2: smartPhone,basicPhone. it says excepted single bean but found two smartPhone and basicPhone.

To resolve this issue spring provides @Qualifier(“name of the bean”).Let’s add this annotation into our configuration class and resolve this ambiguity.

CellPhoneConfiguration.java


public class CellPhoneConfigurationNew {

@Bean

public SmartPhone smartPhone() {
return new SmartPhone();
}

@Bean
public BasicPhone basicPhone() {
return new BasicPhone();
}

@Bean
public CallingApp callingApp(@Qualifier("smartPhone") Phone phone) {

return new CallingApp(phone);
}

}

Output


Apr 15, 2017 11:11:06 AM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@378fd1ac: startup date [Sat Apr 15 11:11:06 IST 2017]; root of context hierarchy
calling via whatsapp...99999

With @Qualifier(“smartPhone”)  spring container won’t get confused as it knows now i have to wire smartPhone bean with callingApp bean.

 

Leave a Reply

Your email address will not be published. Required fields are marked *