Spring – Dependency InjectionEvery Java-based application has a few objects that work together to present what the end-user sees as a working application. When writing a complex Java application, application classes should be as independent as possible of other Java classes to increase the possibility to reuse these classes and to test them independently of other classes while unit testing. Dependency Injection (or sometime called wiring) helps in gluing these classes together and at the same time keeping them independent. Consider you have an application which has a text editor component and you want to provide a spell check. Your standard code would look something like this:
public class TextEditor {
private SpellChecker spellChecker; public TextEditor() { spellChecker = new SpellChecker(); } } What we've done here is, create a dependency between the TextEditor and the SpellChecker. In an inversion of control scenario, we would instead do something like this:
public class TextEditor {
private SpellChecker spellChecker; public TextEditor(SpellChecker spellChecker) { this.spellChecker = spellChecker; } } Here, the TextEditor should not worry about SpellChecker implementation. The SpellChecker will be implemented independently and will be provided to the TextEditor at the time of TextEditor instantiation. This entire procedure is controlled by the Spring Framework. Here, we have removed total control from the TextEditor and kept it somewhere else (i.e. XML configuration file) and the dependency (i.e. class SpellChecker) is being injected into the class TextEditor through a Class Constructor. Thus the flow of control has been "inverted" by Dependency Injection (DI) because you have effectively delegated dependances to some external system. The second method of injecting dependency is through Setter Methods of the TextEditor class where we will create a SpellChecker instance. This instance will be used to call setter methods to initialize TextEditor's properties. Thus, DI exists in two major variants and the following two sub-chapters will cover both of them with examples:
Constructor-based DI is accomplished when the container invokes a class constructor with a number of arguments, each representing a dependency on the other class. Example The following example shows a class TextEditor that can only be dependency-injected with the constructor injection. Let us have a working Eclipse IDE in place and take the following steps to create a Spring application:
package com.jtc;
public class TextEditor { private SpellChecker spellChecker; public TextEditor(SpellChecker spellChecker) { System.out.println("Inside TextEditor constructor." ); this.spellChecker = spellChecker; } public void spellCheck() { spellChecker.checkSpelling(); } } Following is the content of another dependent class file SpellChecker.java
package com.jtc;
public class SpellChecker { public SpellChecker(){ System.out.println("Inside SpellChecker constructor." ); } public void checkSpelling() { System.out.println("Inside checkSpelling." ); } } Following is the content of the MainApp.java file
package com.jtc;
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MainApp { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml"); TextEditor te = (TextEditor) context.getBean("textEditor"); te.spellCheck(); Following is the configuration file Beans.xml which has configuration for the constructorbased injection:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <!-- Definition for textEditor bean --> <bean id="textEditor" class="com.jtc.TextEditor"> <constructor-arg ref="spellChecker"/> </bean> <!-- Definition for spellChecker bean --> <bean id="spellChecker" class="com.jtc.SpellChecker"> </bean> </beans> Once you are done creating the source and bean configuration files, let us run the application. If everything is fine with your application, it will print the following message:
Inside SpellChecker constructor.
Inside TextEditor constructor. Inside checkSpelling. Constructor Arguments Resolution There may be an ambiguity while passing arguments to the constructor, in case there are more than one parameters. To resolve this ambiguity, the order in which the constructor arguments are defined in a bean definition is the order in which those arguments are supplied to the appropriate constructor. Consider the following class:
package x.y;
public class Foo { public Foo(Bar bar, Baz baz) { // ... } } The following configuration works fine:
<beans>
<bean id="foo" class="x.y.Foo"> <constructor-arg ref="bar"/> <constructor-arg ref="baz"/> </bean> <bean id="bar" class="x.y.Bar"/> <bean id="baz" class="x.y.Baz"/> </beans> Let us check one more case where we pass different types to the constructor. Consider the following class:
package x.y;
public class Foo { public Foo(int year, String name) { // ... } } The container can also use type matching with simple types, if you explicitly specify the type of the constructor argument using the type attribute. For example:
<beans>
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg type="int" value="2001"/> <constructor-arg type="java.lang.String" value="Zara"/> </bean> </beans> Finally, the best way to pass constructor arguments, use the index attribute to specify explicitly the index of constructor arguments. Here, the index is 0 based. For example:
<beans>
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg index="0" value="2001"/> <constructor-arg index="1" value="Zara"/> </bean> </beans> A final note, in case you are passing a reference to an object, you need to use ref attribute of <constructor-arg> tag and if you are passing a value directly then you should use the value attribute as shown above. Setter-based Dependency Injection Setter-based DI is accomplished by the container calling setter methods on your beans after invoking a no-argument constructor or no-argument static factory method to instantiate your bean. Example The following example shows a class TextEditor that can only be dependency-injected using pure setter-based injection. Let us have a working Eclipse IDE in place and take the following steps to create a Spring application:
package com.jtc;
public class TextEditor { private SpellChecker spellChecker; // a setter method to inject the dependency. public void setSpellChecker(SpellChecker spellChecker) { System.out.println("Inside setSpellChecker." ); this.spellChecker = spellChecker; } // a getter method to return spellChecker public SpellChecker getSpellChecker() { return spellChecker; } public void spellCheck() { spellChecker.checkSpelling(); } } Here you need to check the naming convention of the setter methods. To set a variable spellChecker we are using setSpellChecker() method, which is very similar to Java POJO classes. Let us create the content of another dependent class file SpellChecker.java:
package com.jtc;
public class SpellChecker { public SpellChecker(){ System.out.println("Inside SpellChecker constructor." ); } public void checkSpelling() { System.out.println("Inside checkSpelling." ); } } Following is the content of the MainApp.java file:
package com.jtc;
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MainApp { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml"); TextEditor te = (TextEditor) context.getBean("textEditor"); te.spellCheck(); } } Following is the configuration file Beans.xml which has the configuration for the setterbased injection:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <!-- Definition for textEditor bean --> <bean id="textEditor" class="com.jtc.TextEditor"> <property name="spellChecker" ref="spellChecker"/> </bean> <!-- Definition for spellChecker bean --> <bean id="spellChecker" class="com.jtc.SpellChecker"> </bean> </beans> You should note the difference in Beans.xml file defined in the constructor-based injection and the setter-based injection. The only difference is inside the <bean> element where we have used <constructor-arg> tags for constructor-based injection and <property> tags for setter-based injection. The second important point to note is that in case you are passing a reference to an object, you need to use ref attribute of <property> tag and if you are passing a value directly then you should use value attribute. Once you are done creating the source and bean configuration files, let us run the application. If everything is fine with your application, this will print the following message:
Inside SpellChecker constructor.
Inside setSpellChecker. Inside checkSpelling. XML Configuration Using p-namespace If you have many setter methods, then it is convenient to use p-namespace in the XML configuration file. Let us check the difference: Let us consider the example of a standard XML configuration file with <property> tags:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="john-classic" class="com.example.Person"> <property name="name" value="John Doe"/> <property name="spouse" ref="jane"/> </bean> <bean name="jane" class="com.example.Person"> <property name="name" value="John Doe"/> </bean> </beans> The above XML configuration can be re-written in a cleaner way using p-namespace as follows:
<?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:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="john-classic" class="com.example.Person" p:name="John Doe" p:spouse-ref="jane"/> </bean> <bean name="jane" class="com.example.Person" p:name="John Doe"/> </bean> </beans> Here, you should note the difference in specifying primitive values and object references with p-namespace. The -ref part indicates that this is not a straight value but rather a reference to another bean. You can mix both, Constructor-based and Setter-based DI but it is a good rule of thumb to use constructor arguments for mandatory dependencies and setters for optional dependencies. The code is cleaner with the DI principle and decoupling is more effective when objects are provided with their dependencies. The object does not look up its dependencies and does not know the location or class of the dependencies, rather everything is taken care by the Spring Framework. |