What is Dependency Injection (DI) and Inversion of Control ... · Sat 2/16/2019 1 What is...

30
Sat 2/16/2019 1 What is Dependency Injection (DI) and Inversion of Control (IoC)? Definitions What is DI? Part of IoC object Injectors (Constructor, Setter or Interface) --- Dependencies object Pass object reference, receive instances of objects, not constructing them ie no new.. What is the purpose of DI? With dependency injection, objects don't define their dependencies themselves, the dependencies are injected to them as needed. How does it benefit ? The objects don't need to know where and how to get their dependencies, which results in loose coupling between objects, which makes them a lot easier to test. objects given dependency at runtime not compile time (ie new) How is it implemented ? Usually a container manages the lifecycle of objects and their dependencies based on a configuration file or annotations. http://www.jamesshore.com/Blog/Dependency-Injection-Demystified.html DI Frameworks Spring Framework 3 Design Patterns to create DI 1. Constructor Injection mandatory dependencies 2. Setter injection optional dependencies 3. Interface injection

Transcript of What is Dependency Injection (DI) and Inversion of Control ... · Sat 2/16/2019 1 What is...

Sat 2/16/2019

1

What is Dependency Injection (DI) and

Inversion of Control (IoC)?

Definitions

What is DI?

Part of IoC

object Injectors (Constructor, Setter or Interface) --- Dependencies

object Pass object reference, receive instances of objects, not constructing them

ie no new..

What is the purpose of DI?

With dependency injection, objects don't define their dependencies themselves, the dependencies are injected to them as needed.

How does it benefit ?

The objects don't need to know where and how to get their dependencies, which results in loose coupling between objects, which makes them a lot easier to test.

objects given dependency at runtime not compile time (ie new)

How is it implemented ?

Usually a container manages the lifecycle of objects and their dependencies based on a configuration file or annotations.

http://www.jamesshore.com/Blog/Dependency-Injection-Demystified.html

DI Frameworks

Spring Framework

3 Design Patterns to create DI

1. Constructor Injection mandatory dependencies

2. Setter injection optional dependencies

3. Interface injection

Sat 2/16/2019

2

Ex1. Without Dependency Injection

In the following Java example, the Client class contains a Service member variable that is initialized by the Client constructor. The client controls which implementation of service is used and controls its construction. In this situation, the client is said to have a hard-coded dependency on ExampleService.

// An example without dependency injection public class Client { // Internal reference to the service used by this client private ExampleService service; // Constructor Client() { // Specify a specific implementation in the constructor instead of using dependency injection service = new ExampleService(); } // Method within this client that uses the services public String greet() { return "Hello " + service.getName(); } }

Ex1. 3 Design Patterns With Dependency Injection

1. Constructor Injection

This method requires the client to provide a parameter in a constructor for the dependency.

// Constructor Client(Service service) { // Save the reference to the passed-in service inside this client this.service = service; }

2. Setter injection This method requires the client to provide a setter method for the dependency.

// Setter method public void setService(Service service) { // Save the reference to the passed-in service inside this client this.service = service; }

3. Interface injection

This is simply the client publishing a role interface to the setter methods of the client's dependencies. It can be used to establish how the injector should talk to the client when injecting dependencies.

// Service setter interface. public interface ServiceSetter { public void setService(Service service); } // Client class public class Client implements ServiceSetter { // Internal reference to the service used by this client. private Service service; // Set the service that this client is to use.

Sat 2/16/2019

3

@Override public void setService(Service service) { this.service = service; } }

Sat 2/16/2019

4

Ex2. DI

The basic idea of the Dependency Injection is to have a

separate object, an

assembler, that populates a field in the lister class with an

appropriate implementation for the finder interface,

resulting in a dependency diagram along the lines of Figure 2

Figure 2: The dependencies for a Dependency Injector

3 Design Patterns to create DI

1. Constructor Injection

2. Setter injection

3. Interface injection

1 – Constructor DI with TestContainer

How to create injection constructor using lightweight container called TestContainer.

TestContainer uses a constructor to decide how to inject a finder implementation into the lister class. For this to work, the movie lister class needs to declare a constructor that includes everything it needs injected.

class MovieLister... public MovieLister(MovieFinder finder) { this.finder = finder; }

The finder itself will also be managed by the TestContainer, and as such will have the filename of the text file injected into it by the container.

class ColonMovieFinder... public ColonMovieFinder(String filename) {

Sat 2/16/2019

5

this.filename = filename; }

The Test container then needs to be told which implementation class to associate with each interface, and which string to inject into the finder.

private MutableTestContainer configureContainer() { MutableTestContainer test = new DefaultTestContainer(); parameter[] finderParams = { new ConstantParameter("movies1.txt") }; test.registerComponentImplementation(MovieFinder.class, ColonMovieFinder.class, finderParams); test.registerComponentImplementation(MovieLister.class); return test; }

This configuration code is typically set up in a different class. For our example, each friend who uses my lister might write the appropriate configuration code in some setup class of their own. Of course it's common to hold this kind of configuration information in separate config files. You can write a class to read a config file and set up the container appropriately. Although TestContainer doesn't contain this functionality itself, there is a closely related project called NanoContainer that provides the appropriate wrappers to allow you to have XML configuration files. Such a nano container will parse the XML and then configure an underlying test container. The philosophy of the project is to separate the config file format from the underlying mechanism.

To use the container you write code something like this.

public void testWithTest() { MutableTestContainer test = configureContainer(); MovieLister lister = (MovieLister) test.getComponentInstance(MovieLister.class); Movie[] movies = lister.moviesDirectedBy("Sergio Leone"); assertEquals("Once Upon a Time in the West", movies[0].getTitle()); }

Sat 2/16/2019

6

2 - Setter Injection with Spring

To get my movie lister to accept the injection I

4. define a setting method for that service class MovieLister... private MovieFinder finder; public void setFinder(MovieFinder finder) { this.finder = finder; }

5. Similarly I define a setter for the filename. class ColonMovieFinder... public void setFilename(String filename) { this.filename = filename; }

6. The third step is to set up the configuration for the files. Spring supports configuration through XML

files and also through code, but XML is the expected way to do it. <beans> <bean id="MovieLister" class="spring.MovieLister"> <property name="finder"> <ref local="MovieFinder"/> </property> </bean> <bean id="MovieFinder" class="spring.ColonMovieFinder"> <property name="filename"> <value>movies1.txt</value> </property> </bean> </beans>

The test then looks like this.

public void testWithSpring() throws Exception { ApplicationContext ctx = new FileSystemXmlApplicationContext("spring.xml"); MovieLister lister = (MovieLister) ctx.getBean("MovieLister"); Movie[] movies = lister.moviesDirectedBy("Sergio Leone"); assertEquals("Once Upon a Time in the West", movies[0].getTitle()); }

Sat 2/16/2019

7

3 - Interface Injection

The third injection technique is to define and use interfaces for the injection. Avalon is an example of a framework that uses this technique in places.

With this technique I begin by defining an interface that I'll use to perform the injection through. Here's the interface for injecting a movie finder into an object.

public interface InjectFinder { void injectFinder(MovieFinder finder); }

This interface would be defined by whoever provides the MovieFinder interface. It needs to be implemented by any class that wants to use a finder, such as the lister.

class MovieLister implements InjectFinder public void injectFinder(MovieFinder finder) { this.finder = finder; }

I use a similar approach to inject the filename into the finder implementation.

public interface InjectFinderFilename { void injectFilename (String filename); }

class ColonMovieFinder implements MovieFinder, InjectFinderFilename... public void injectFilename(String filename) { this.filename = filename; }

Then, as usual, I need some configuration code to wire up the implementations. For simplicity's sake I'll do it in code.

class Tester... private Container container; private void configureContainer() { container = new Container(); registerComponents(); registerInjectors(); container.start(); }

This configuration has two stages, registering components through lookup keys is pretty similar to the other examples.

class Tester...

private void registerComponents() { container.registerComponent("MovieLister", MovieLister.class); container.registerComponent("MovieFinder", ColonMovieFinder.class); }

A new step is to register the injectors that will inject the dependent components. Each injection interface needs some code to inject the dependent object. Here I do this by registering injector objects with the container. Each injector object implements the injector interface.

class Tester...

private void registerInjectors() { container.registerInjector(InjectFinder.class, container.lookup("MovieFinder")); container.registerInjector(InjectFinderFilename.class, new FinderFilenameInjector()); }

Sat 2/16/2019

8

public interface Injector { public void inject(Object target); }

When the dependent is a class written for this container, it makes sense for the component to implement the injector interface itself, as I do here with the movie finder. For generic classes, such as the string, I use an inner

class within the configuration code.

class ColonMovieFinder implements Injector... public void inject(Object target) { ((InjectFinder) target).injectFinder(this); }

class Tester... public static class FinderFilenameInjector implements Injector { public void inject(Object target) { ((InjectFinderFilename)target).injectFilename("movies1.txt"); } }

4 - The tests then use the container.

class Tester… public void testIface() { configureContainer(); MovieLister lister = (MovieLister)container.lookup("MovieLister"); Movie[] movies = lister.moviesDirectedBy("Sergio Leone"); assertEquals("Once Upon a Time in the West", movies[0].getTitle()); }

The container uses the declared injection interfaces to figure out the dependencies and the injectors to inject the correct dependents. (The specific container implementation I did here isn't important to the technique, and I won't show it because you'd only laugh.)

Sat 2/16/2019

9

Ex 3 DI

3 Design Patterns to create DI

1. Constructor Injection

2. Setter injection

3. Interface injection

1 - Constructor DI

2 - Setter Injection with Spring

3 - Interface Injection

1 - Constructor-Based Dependency Injection

Constructor-based DI is when the container invokes a constructor with a number of arguments, each of which represents a dependency or other class.

Calling a static factory method with particular arguments to construct the bean is approximately equivalent, treating arguments to a constructor and to a static factory method. The following example shows a class that can only be dependency-injected with constructor injection. It is a POJO that has no dependencies on container specific interfaces, base classes, or annotations.

public class SimpleStudentList { // the SimpleStudentList has a dependency on StudentFind private StudentFind studentFind; // a constructor that Spring container can 'inject' a StudentFind public SimpleStudentList(StudentFind studentFind ) { this.studentFind = studentFind ; } // business logic code }

Book.java

package com.spring.example; public class Book { private int id; private String bookName; public Book() {System.out.println("Java");} public Book(int id) {this.id = id;} public Book(String bookName) { this.bookName = bookName;} public Book(int id, String bookName) { this.id = id; this.bookName = bookName; } void display(){ System.out.println(id+" "+bookName); } }

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"

Sat 2/16/2019

10

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="book" class="com.spring.example.Book"> <constructor-arg value="1" type="int"></constructor-arg> </bean> </beans>

Main.java

package com.spring.example; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.*; public class Main { public static void main(String[] args) { Resource r=new ClassPathResource("applicationContext.xml"); BeanFactory factory=new XmlBeanFactory(r); Book b=(Book)factory.getBean("book"); b.display(); } }

Output:

1 null

Sat 2/16/2019

11

2. Setter-Based Dependency Injection

Setter-based DI is the when the container calls setter methods on your beans after it has invoked a no-argument constructor or no-argument static factory method to instantiate that bean.

The following example shows a class that can only have pure setter injection.

public class SimpleStudentList { // the SimpleStudentList has a dependency on StudentFind private StudentFind studentFind; // a setter method that Spring container can 'inject' a StudentFind public void setStudentFind(StudentFind studentFind ) { this.studentFind = studentFind ; } // business logic }

Book.java

package com.spring.example; public class Book { private int id; private String bookName; private String author; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getBookName() { return bookName; } public void setBookName(String bookName) { this.bookName = bookName; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } void display(){ System.out.println(id+" "+bookName+" "+author); } }

applicationContext.xml

<?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="book" class="com.spring.example.Book"> <property name="id"> <value>1</value> </property> <property name="bookName"> <value>The Complete Reference J2EE</value>

Sat 2/16/2019

12

</property> <property name="author"> <value>Herbert Schildt</value> </property> </bean> </beans>

Main.java

package com.spring.example; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.*; public class Main { public static void main(String[] args) { Resource r=new ClassPathResource("applicationContext.xml"); BeanFactory factory=new XmlBeanFactory(r); Book b=(Book)factory.getBean("book"); b.display(); } }

Output :

The Complete Reference J2EE Herbert Schildt

Sat 2/16/2019

13

Ex 3

No DI

Let’s say we have an application where we consume EmailService to send emails. Normally

we would implement this like below.

package com.journaldev.java.legacy; public class EmailService { public void sendEmail(String message, String receiver){ //logic to send email System.out.println("Email sent to "+receiver+ " with Message="+message); } }

EmailService class holds the logic to send email message to the recipient email address. Our

application code will be like below.

package com.journaldev.java.legacy; public class MyApplication { private EmailService email = new EmailService(); public void processMessages(String msg, String rec){ //do some msg validation, manipulation logic etc this.email.sendEmail(msg, rec); } }

Our client code that will use MyApplication class to send email messages will be like below.

package com.journaldev.java.legacy; public class MyLegacyTest { public static void main(String[] args) { MyApplication app = new MyApplication(); app.processMessages("Hi Pankaj", "[email protected]"); } }

At first look, there seems nothing wrong with above implementation. But above code logic has certain limitations.

MyApplication class is responsible to initialize the email service and then use it. This leads to hard-coded

dependency. If we want to switch to some other advanced email service in future, it will require code changes in MyApplication class. This makes our application hard to extend and if email service is used in multiple classes then that would be even more harder.

Sat 2/16/2019

14

If we want to extend our application to provide additional messaging feature, such as SMS or Facebook message then we would need to write another application for that. This will involve code changes in application classes and in client classes too.

Testing the application will be very difficult since our application is directly creating the email service instance. There is no way we can mock these objects in our test classes.

One can argue that we can remove the email service instance creation

from MyApplication class by having a constructor that requires email service as argument.

package com.journaldev.java.legacy; public class MyApplication { private EmailService email = null; public MyApplication(EmailService svc){ this.email=svc; } public void processMessages(String msg, String rec){ //do some msg validation, manipulation logic etc this.email.sendEmail(msg, rec); } }

But in this case, we are asking client applications or test classes to initializing the email service that is not a good design decision.

Now let’s see how we can apply java dependency injection pattern to solve all the problems with above implementation. Dependency Injection in java requires at least following:

1. Service components should be designed with base class or interface. It’s better to prefer interfaces or

abstract classes that would define contract for the services.

2. Consumer classes should be written in terms of service interface.

3. Injector classes that will initialize the services and then the consumer classes.

Sat 2/16/2019

15

Java Dependency Injection – Service Components

For our case, we can have MessageService that will declare the contract for service

implementations.

package com.journaldev.java.dependencyinjection.service; public interface MessageService { void sendMessage(String msg, String rec); }

Now let’s say we have Email and SMS services that implement above interfaces.

package com.journaldev.java.dependencyinjection.service; public class EmailServiceImpl implements MessageService { @Override public void sendMessage(String msg, String rec) { //logic to send email System.out.println("Email sent to "+rec+ " with Message="+msg); } } package com.journaldev.java.dependencyinjection.service; public class SMSServiceImpl implements MessageService { @Override public void sendMessage(String msg, String rec) { //logic to send SMS System.out.println("SMS sent to "+rec+ " with Message="+msg); } }

Our dependency injection java services are ready and now we can write our consumer class.

Sat 2/16/2019

16

Java Dependency Injection – Service Consumer

We are not required to have base interfaces for consumer classes but I will have

a Consumer interface declaring contract for consumer classes.

package com.journaldev.java.dependencyinjection.consumer; public interface Consumer { void processMessages(String msg, String rec); }

My consumer class implementation is like below.

package com.journaldev.java.dependencyinjection.consumer; import com.journaldev.java.dependencyinjection.service.MessageService; public class MyDIApplication implements Consumer{ private MessageService service; public MyDIApplication(MessageService svc){ this.service=svc; } @Override public void processMessages(String msg, String rec){ //do some msg validation, manipulation logic etc this.service.sendMessage(msg, rec); } }

Notice that our application class is just using the service. It does not initialize the service that leads to better “separation of concerns“. Also use of service interface allows us to easily test the application by mocking the MessageService and bind the services at runtime rather than compile time.

Now we are ready to write java dependency injector classes that will initialize the service and

also consumer classes.

Sat 2/16/2019

17

Java Dependency Injection – Injectors Classes

Let’s have an interface MessageServiceInjector with method declaration that returns

the Consumer class.

package com.journaldev.java.dependencyinjection.injector; import com.journaldev.java.dependencyinjection.consumer.Consumer; public interface MessageServiceInjector { public Consumer getConsumer(); }

Now for every service, we will have to create injector classes like below.

package com.journaldev.java.dependencyinjection.injector; import com.journaldev.java.dependencyinjection.consumer.Consumer; import com.journaldev.java.dependencyinjection.consumer.MyDIApplication; import com.journaldev.java.dependencyinjection.service.EmailServiceImpl; public class EmailServiceInjector implements MessageServiceInjector { @Override public Consumer getConsumer() { return new MyDIApplication(new EmailServiceImpl()); } } package com.journaldev.java.dependencyinjection.injector; import com.journaldev.java.dependencyinjection.consumer.Consumer; import com.journaldev.java.dependencyinjection.consumer.MyDIApplication; import com.journaldev.java.dependencyinjection.service.SMSServiceImpl; public class SMSServiceInjector implements MessageServiceInjector { @Override public Consumer getConsumer() { return new MyDIApplication(new SMSServiceImpl()); } }

Now let’s see how our client applications will use the application with a simple program.

package com.journaldev.java.dependencyinjection.test; import com.journaldev.java.dependencyinjection.consumer.Consumer; import com.journaldev.java.dependencyinjection.injector.EmailServiceInjector; import com.journaldev.java.dependencyinjection.injector.MessageServiceInjector; import com.journaldev.java.dependencyinjection.injector.SMSServiceInjector; public class MyMessageDITest { public static void main(String[] args) { String msg = "Hi Pankaj"; String email = "[email protected]"; String phone = "4088888888"; MessageServiceInjector injector = null;

Sat 2/16/2019

18

Consumer app = null; //Send email injector = new EmailServiceInjector(); app = injector.getConsumer(); app.processMessages(msg, email); //Send SMS injector = new SMSServiceInjector(); app = injector.getConsumer(); app.processMessages(msg, phone); } }

As you can see that our application classes are responsible only for using the service. Service classes are created in injectors. Also if we have to further extend our application to allow facebook messaging, we will have to write Service classes and injector classes only.

So dependency injection implementation solved the problem with hard-coded dependency and helped us in making our application flexible and easy to extend. Now let’s see how easily we can test our application class by mocking the injector and service classes.

Sat 2/16/2019

19

Java Dependency Injection – JUnit Test Case with Mock Injector and

Service

package com.journaldev.java.dependencyinjection.test; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.journaldev.java.dependencyinjection.consumer.Consumer; import com.journaldev.java.dependencyinjection.consumer.MyDIApplication; import com.journaldev.java.dependencyinjection.injector.MessageServiceInjector; import com.journaldev.java.dependencyinjection.service.MessageService; public class MyDIApplicationJUnitTest { private MessageServiceInjector injector; @Before public void setUp(){ //mock the injector with anonymous class injector = new MessageServiceInjector() { @Override public Consumer getConsumer() { //mock the message service return new MyDIApplication(new MessageService() { @Override public void sendMessage(String msg, String rec) { System.out.println("Mock Message Service implementation"); } }); } }; } @Test public void test() { Consumer consumer = injector.getConsumer(); consumer.processMessages("Hi Pankaj", "[email protected]"); } @After public void tear(){ injector = null; } }

As you can see that I am using anonymous classes to mock the injector and service classes and I can easily test my application methods. I am using JUnit 4 for above test class, so make sure it’s in your project build path if you are running above test class.

We have used constructors to inject the dependencies in the application classes, another way is to use setter method to inject dependencies in application classes. For setter method

dependency injection, our application class will be implemented like below.

package com.journaldev.java.dependencyinjection.consumer; import com.journaldev.java.dependencyinjection.service.MessageService;

Sat 2/16/2019

20

public class MyDIApplication implements Consumer{ private MessageService service; public MyDIApplication(){} //setter dependency injection public void setService(MessageService service) { this.service = service; } @Override public void processMessages(String msg, String rec){ //do some msg validation, manipulation logic etc this.service.sendMessage(msg, rec); } } package com.journaldev.java.dependencyinjection.injector; import com.journaldev.java.dependencyinjection.consumer.Consumer; import com.journaldev.java.dependencyinjection.consumer.MyDIApplication; import com.journaldev.java.dependencyinjection.service.EmailServiceImpl; public class EmailServiceInjector implements MessageServiceInjector { @Override public Consumer getConsumer() { MyDIApplication app = new MyDIApplication(); app.setService(new EmailServiceImpl()); return app; } }

One of the best example of setter dependency injection is Struts2 Servlet API Aware interfaces.

Whether to use Constructor based dependency injection or setter based is a design decision and depends on your requirements. For example, if my application can’t work at all without the service class then I would prefer constructor based DI or else I would go for setter method based DI to use it only when it’s really needed.

Dependency Injection in Java is a way to achieve Inversion of control (IoC) in our application by moving objects binding from compile time to runtime. We can achieve IoC through Factory Pattern, Template Method Design Pattern, Strategy Pattern and Service Locator pattern too.

Sat 2/16/2019

21

Spring - Dependency Injection

Every 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.

Sat 2/16/2019

22

Thus, DI exists in two major variants and the following two sub-chapters will cover both of them with examples −

Sr.No. Dependency Injection Type & Description

1 Constructor-based dependency injection

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.

2 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.

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.

https://www.tutorialspoint.com/spring/spring_injecting_inner_beans.htm

Sat 2/16/2019

23

Spring - Injecting Inner Beans

As you know Java inner classes are defined within the scope of other classes, similarly, inner beans are beans that are defined within the scope of another bean. Thus, a <bean/> element inside the <property/> or <constructor-arg/> elements is called inner bean and it is shown below.

<?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 = "outerBean" class = "..."> <property name = "target"> <bean id = "innerBean" class = "..."/> </property> </bean> </beans>

Example Let us have working Eclipse IDE in place and follow the following steps to create a Spring application −

Steps Description

1 Create a project with a name SpringExample and create a package com.tutorialspoint under the src folder in the created project.

2 Add required Spring libraries using Add External JARs option as explained in the Spring Hello World Example chapter.

3 Create Java classes TextEditor, SpellChecker and MainApp under

the com.tutorialspoint package.

4 Create Beans configuration file Beans.xml under the src folder.

5 The final step is to create the content of all the Java files and Bean Configuration file and run the application as explained below.

Here is the content of TextEditor.java file −

package com.tutorialspoint; 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; }

Sat 2/16/2019

24

// a getter method to return spellChecker public SpellChecker getSpellChecker() { return spellChecker; } public void spellCheck() { spellChecker.checkSpelling(); } } Following is the content of another dependent class file SpellChecker.java − package com.tutorialspoint; 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.tutorialspoint; 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 setter-based injection but using inner beans −

<?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 using inner bean --> <bean id = "textEditor" class = "com.tutorialspoint.TextEditor"> <property name = "spellChecker"> <bean id = "spellChecker" class = "com.tutorialspoint.SpellChecker"/> </property> </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 setSpellChecker. Inside checkSpelling.

Sat 2/16/2019

25

Spring - Injecting Collection

You have seen how to configure primitive data type using value attribute and object references using ref attribute of the <property> tag in your Bean configuration file. Both the cases deal with passing singular value to a bean.

Now what if you want to pass plural values like Java Collection types such as List, Set, Map, and Properties. To handle the situation, Spring offers four types of collection configuration elements which are as follows −

Sr.No Element & Description

1 <list>

This helps in wiring ie injecting a list of values, allowing duplicates.

2 <set>

This helps in wiring a set of values but without any duplicates.

3 <map>

This can be used to inject a collection of name-value pairs where name

and value can be of any type.

4 <props>

This can be used to inject a collection of name-value pairs where the

name and value are both Strings.

You can use either <list> or <set> to wire any implementation of java.util.Collection or an array.

You will come across two situations (a) Passing direct values of the collection and (b) Passing a reference of a bean as one of the collection elements.

Example

Let us have a working Eclipse IDE in place and take the following steps to create a Spring application −

Steps Description

1 Create a project with a name SpringExample and create a package com.tutorialspoint under the src folder in the created project.

2 Add required Spring libraries using Add External JARs option as explained in the Spring Hello World Example chapter.

3 Create Java classes JavaCollection, and MainApp under

the com.tutorialspoint package.

Sat 2/16/2019

26

4 Create Beans configuration file Beans.xml under the src folder.

5 The final step is to create the content of all the Java files and Bean

Configuration file and run the application as explained below.

Here is the content of JavaCollection.java file −

package com.tutorialspoint; import java.util.*; public class JavaCollection { List addressList; Set addressSet; Map addressMap; Properties addressProp; // a setter method to set List public void setAddressList(List addressList) { this.addressList = addressList; } // prints and returns all the elements of the list. public List getAddressList() { System.out.println("List Elements :" + addressList); return addressList; } // a setter method to set Set public void setAddressSet(Set addressSet) { this.addressSet = addressSet; } // prints and returns all the elements of the Set. public Set getAddressSet() { System.out.println("Set Elements :" + addressSet); return addressSet; } // a setter method to set Map public void setAddressMap(Map addressMap) { this.addressMap = addressMap; } // prints and returns all the elements of the Map. public Map getAddressMap() { System.out.println("Map Elements :" + addressMap); return addressMap; } // a setter method to set Property public void setAddressProp(Properties addressProp) { this.addressProp = addressProp; } // prints and returns all the elements of the Property. public Properties getAddressProp() { System.out.println("Property Elements :" + addressProp); return addressProp; } }

Sat 2/16/2019

27

Following is the content of the MainApp.java file −

package com.tutorialspoint; 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"); JavaCollection jc=(JavaCollection)context.getBean("javaCollection"); jc.getAddressList(); jc.getAddressSet(); jc.getAddressMap(); jc.getAddressProp(); } }

Following is the configuration file Beans.xml which has configuration for all the type of collections −

<?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 javaCollection --> <bean id = "javaCollection" class = "com.tutorialspoint.JavaCollection"> <!-- results in a setAddressList(java.util.List) call --> <property name = "addressList"> <list> <value>INDIA</value> <value>Pakistan</value> <value>USA</value> <value>USA</value> </list> </property> <!-- results in a setAddressSet(java.util.Set) call --> <property name = "addressSet"> <set> <value>INDIA</value> <value>Pakistan</value> <value>USA</value> <value>USA</value> </set> </property> <!-- results in a setAddressMap(java.util.Map) call --> <property name = "addressMap"> <map> <entry key = "1" value = "INDIA"/> <entry key = "2" value = "Pakistan"/> <entry key = "3" value = "USA"/> <entry key = "4" value = "USA"/> </map> </property> <!-- results in a setAddressProp(java.util.Properties) call --> <property name = "addressProp"> <props>

Sat 2/16/2019

28

<prop key = "one">INDIA</prop> <prop key = "one">INDIA</prop> <prop key = "two">Pakistan</prop> <prop key = "three">USA</prop> <prop key = "four">USA</prop> </props> </property> </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 −

List Elements :[INDIA, Pakistan, USA, USA] Set Elements :[INDIA, Pakistan, USA] ap Elements :{1 = INDIA, 2 = Pakistan, 3 = USA, 4 = USA} Property Elements :{two = Pakistan, one = INDIA, three = USA, four = USA}

Sat 2/16/2019

29

Injecting Bean References

The following Bean definition will help you understand how to inject bean references as one of the collection's element. Even you can mix references and values all together as shown in the following code snippet −

<?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 Definition to handle references and values --> <bean id = "..." class = "..."> <!-- Passing bean reference for java.util.List --> <property name = "addressList"> <list> <ref bean = "address1"/> <ref bean = "address2"/> <value>Pakistan</value> </list> </property> <!-- Passing bean reference for java.util.Set --> <property name = "addressSet"> <set> <ref bean = "address1"/> <ref bean = "address2"/> <value>Pakistan</value> </set> </property> <!-- Passing bean reference for java.util.Map --> <property name = "addressMap"> <map> <entry key = "one" value = "INDIA"/> <entry key = "two" value-ref = "address1"/> <entry key = "three" value-ref = "address2"/> </map> </property> </bean> </beans>

To use the above bean definition, you need to define your setter methods in such a way that they should be able to handle references as well.

Injecting null and empty string values If you need to pass an empty string as a value, then you can pass it as follows −

<bean id = "..." class = "exampleBean"> <property name = "email" value = ""/> </bean>

The preceding example is equivalent to the Java code: exampleBean.setEmail("")

If you need to pass a NULL value, then you can pass it as follows −

<bean id = "..." class = "exampleBean"> <property name = "email"><null/></property>

Sat 2/16/2019

30

</bean>

The preceding example is equivalent to the Java code: exampleBean.setEmail(null)