Appendix F

The Spring Framework

The Spring Framework (or Spring for short) is an open source framework for developing enterprise applications. It is a light-weight solution composed of about twenty different modules. This appendix covers Spring’s Core and Beans modules as well as Spring MVC. Spring MVC is a subframework within Spring.

This appendix explains the Core and Beans modules and how they provide solutions for dependency injection. For the uninitiated, the concept of dependency injection is also discussed in detail. You will use the skills you acquire in this chapter to configure the Spring MVC applications developed in Chapter 23.

Dependency Injection

Dependency injection has been widely used in the past few years as a solution to, among others, code testability. In fact, dependency injection is behind great frameworks like Spring and Google Guice. So, what is dependency injection?

Many people use the terms dependency injection and Inversion of Control (IoC) interchangeably, even though Martin Fowler argues that they are not the same in his excellent article on the subject.

http://martinfowler.com/articles/injection.html

For those in a hurry, the short explanation of dependency injection is this.

If you have two components, A and B, and A depends on B, you can say A is dependent on B or B is a dependency of A. Suppose A is a class that has a method called importantMethod that uses B, as defined in the following code fragment.

public class A {
    public void importantMethod() {
        B b = ... // get an instance of B
        b.usefulMethod();
        ...
    }
    ...
}

A must obtain an instance of B before it can use B. While it is as straightforward as using the new keyword if B is a concrete class, it can be problematic if B is an interface with many implementations. You will have to choose an implementation of B and by doing so you reduce the reusability of A because you cannot use A with the implementations of B that you did not choose.

Dependency injection deals with this kind of situation by taking over object creation and injecting dependencies to an object that needs them. In this case, a dependency injection framework like Spring would create an instance of A and an instance of B and inject the latter to the former.

To make it possible for a framework to inject a dependency, you have to create a set method or a special constructor in the target class. For example, to make A injectable with an instance of B, you would modify A to this.

public class A {
    private B b;
    public void importantMethod() {
        // no need to worry about creating B anymore
        // B b = ... // get an instance of B
        b.usefulMethod();
        ...
    }
    public void setB(B b) {
        this.b = b;
    }
}

In the revised version of A, there is a setter method that can be called to inject an instance of B. Since dependency injection provides dependencies for an object, the importantMethod method in A no longer needs to create an instance of B before being allowed to call its usefulMethod method.

Alternatively, if you prefer a constructor, you could modify class A to this.

public class A {
    private B b;

    public A(B b) {
        this.b = b;
    }

    public void importantMethod() {
        // no need to worry about creating B anymore
        // B b = ... // get an instance of B
        b.usefulMethod();
        ...
    }
}

In this case, Spring would create an instance of B before it creates an instance of A and injects the former to the latter.

Note

Objects that Spring manages are called beans.

Spring gives you a way to intelligently manage dependencies of your Java objects by providing an Inversion of Control (IoC) container (or a dependency injection container, if you wish). The beauty of Spring is your classes do not need to know anything about Spring, nor do they need to import any Spring types.

Spring supports both setter-based and constructor-based dependency injection since version 1. Starting from Spring 2.5, field-based dependency injection is also made possible via the use of the Autowired annotation type. The drawback of using @Autowired is you have to import the org.springframework.beans.factory.annotation.Autowired annotation type in your class, which makes it dependent on Spring. In such scenarios, porting the application to another dependency injection container would not be straightforward.

With Spring, you practically hand over the creation of all important objects to it. You configure Spring to tell it how it should inject dependencies. There are two types of configuration in Spring, by using an XML file and by employing annotations. You would then create an ApplicationContext, which essentially represents a Spring IoC container. The org.springframework.context.ApplicationContext interface comes with several implementations, including ClassPathXmlApplicationContext and FileSystemXmlApplicationContext. Both expect an XML document or multiple XML documents that contain information on the beans that it will manage. A ClassPathXmlApplicationContext will try to find the configuration files in the class path whereas a FileSystemXmlApplicationContext will try to find them in the file system.

For example, here is code to create an ApplicationContext that searches for config1.xml and config2.xml files in the class path.

ApplicationContext context = new ClassPathXmlApplicationContext(
        new String[] {"config1.xml", "config2.xml"});

To obtain a bean from the ApplicationContext, call its getBean method.

Product product = context.getBean("product", Product.class);

The getBean method above looks for a bean with the id product that is of type Product.

Note

Ideally, you only need to create an ApplicationContext in a test class, and your application should not know that it is being managed by Spring. With Spring MVC you do not deal with ApplicationContext directly. Instead, you use a Spring servlet to handle the ApplicationContext.

XML-Based Spring Configuration

Spring supports XML-based configuration since version 1.0 as well as annotation-based configuration starting version 2.5. The following section discusses how an XML configuration file would look like. The root element of a Spring configuration file is always 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-4.0.xsd">
       
   ... 
</beans>

You can add more schemas to the schemaLocation attribute if you need more Spring functionality in your application. In addition, you can split your XML configuration file into multiple files to make it more modular. Implementations of ApplicationContext are designed to read multiple configuration files. Alternatively, you can have a main XML configuration file that imports other configuration files.

Here is an example of importing three other XML configuration files from a main configuration file.

<?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-4.0.xsd">
       
    <import resource="config1.xml"/>
    <import resource="module2/config2.xml"/>
    <import resource="/resources/config3.xml"/>
   ... 
</beans>

You learn the other subelements of <beans> in the next section.

Using the Spring IoC Container

In this section you learn how to use Spring to manage your objects and dependencies.

Creating A Bean Instance with A Constructor

To get an instance of a bean, call the getBean method on the ApplicationContext. For example, the XML configuration file in Listing F.1 contains the definition of a single bean named product.

Listing F.1: A simple configuration file

<?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-4.0.xsd">
       
    <bean name="product" class="com.example.bean.Product"/>
    
</beans>

The bean declaration will tell Spring to instantiate the Product class using its no-argument (default) constructor. If such a constructor does not exist (because the author of the class defined another constructor and did not explicitly define the default constructor), Spring will throw an exception. The no-argument constructor does not have to be public for Spring to create an instance of the class.

Note that you use the name or id attribute to identify a bean. To use Spring to create an instance of Product, call the ApplicationContext’s getBean method, passing the bean name or id and its class.

ApplicationContext context =
        new ClassPathXmlApplicationContext(
        new String[] {"spring-config.xml"});
Product product1 = context.getBean("product", Product.class);
product1.setName("Excellent snake oil");
System.out.println("product1: " + product1.getName()); 

Creating A Bean Instance with A Factory Method

Most classes will be instantiated using one of their constructors. However, Spring is equally happy if it has to call a factory method to instantiate a class.

The following bean definition specifies a factory method for instantiating java.util.Calendar.

<bean id="calendar" class="java.util.Calendar"
    factory-method="getInstance"/>

Note that instead of the name attribute, I used the id attribute to identify the bean. You can then use getBean to get an instance of Calendar.

ApplicationContext context =
        new ClassPathXmlApplicationContext(
        new String[] {"spring-config.xml"});
Calendar calendar = context.getBean("calendar", Calendar.class);

Using A Destroy Method

Some classes come with methods that should be called before instances of the classes are put for garbage collection. Spring supports this notion too. In your bean declaration, you can use the destroy-method attribute to name a method that should be invoked before the object is decommissioned.

For example, the following bean element instructs Spring to create an instance of java.util.concurrent.ExecutorService by calling the static method newCachedThreadPool on the java.util.concurrent.Executors class. The bean definition defines the shutdown method as the value of its destroy-method attribute. As a result, Spring will call shutdown before destroying the ExecutorService instance.

<bean id="executorService" class="java.util.concurrent.Executors"
    factory-method="newCachedThreadPool" 
    destroy-method="shutdown"/>

Passing Arguments to a Constructor

Spring can pass arguments to a class constructor if using the constructor is how it is intended to instantiate the class. Consider the Product class in Listing F.2.

Listing F.2: The Product class

package com.example.bean;
import java.io.Serializable;

public class Product implements Serializable {
    private static final long serialVersionUID = 748392348L;
    private String name;
    private String description;
    private float price;

    public Product() {
    }

    public Product(String name, String description, float price) {
        this.name = name;
        this.description = description;
        this.price = price;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public float getPrice() {
        return price;
    }
    public void setPrice(float price) {
        this.price = price;
    }
}

The following bean definition passes arguments to the Product class by name.

<bean name="featuredProduct" class="com.example.bean.Product">
    <constructor-arg name="name" value="Ultimate Olive Oil"/>
    <constructor-arg name="description" 
        value="The purest olive oil on the market"/>
    <constructor-arg name="price" value="9.95"/>
</bean>

In this case, Spring will use the following constructor of the Product class.

public Product(String name, String description, float price) {
    this.name = name;
    this.description = description;
    this.price = price;
}

Passing arguments by name is not the only way to do business in Spring. Spring allows you to pass argument by index. Here is how the featuredProduct bean can be rewritten.

<bean name="featuredProduct2" class="com.example.bean.Product">
    <constructor-arg index="0" value="Ultimate Olive Oil"/>
    <constructor-arg index="1" 
        value="The purest olive oil on the market"/>
    <constructor-arg index="2" value="9.95"/>
</bean>

If you choose to pass arguments to a constructor, you must pass all the arguments required by the constructor. An incomplete list of arguments will not be accepted.

Setter-based Dependency Injection

Consider the Employee class in Listing F.3 and the Address class in Listing F.4.

Listing F.3: The Employee class

package com.example.bean;
public class Employee {
    private String firstName;
    private String lastName;
    private Address homeAddress;
    
    public Employee() {
    }
    
    public Employee(String firstName, String lastName, Address homeAddress) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.homeAddress = homeAddress;
    }
    
    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public Address getHomeAddress() {
        return homeAddress;
    }

    public void setHomeAddress(Address homeAddress) {
        this.homeAddress = homeAddress;
    }

    @Override
    public String toString() {
        return firstName + " " + lastName
                + "
" + homeAddress;
    }

}

Listing F.4: The Address class

package com.example.bean;
public class Address {
  private String line1;
    private String line2;
    private String city;
    private String state;
    private String zipCode;
    private String country;
    
    public Address(String line1, String line2, String city,
            String state, String zipCode, String country) {
        this.line1 = line1;
        this.line2 = line2;
        this.city = city;
        this.state = state;
        this.zipCode = zipCode;
        this.country = country;
    }

    // getters and setters omitted
    
    @Override
    public String toString() {
        return line1 + "
"
                + line2 + "
"
                + city + "
"
                + state + " " + zipCode + "
"
                + country;
    }
}

Employee depends on Address. To make sure that every Employee instance contains an instance of Address, you can configure Spring with these two bean elements.

<bean name="simpleAddress" class="com.example.bean.Address">
    <constructor-arg name="line1" value="151 Corner Street"/>
    <constructor-arg name="line2" value=""/>
    <constructor-arg name="city" value="Albany"/>
    <constructor-arg name="state" value="NY"/>
    <constructor-arg name="zipCode" value="99999"/>
    <constructor-arg name="country" value="US"/>
</bean>

<bean name="employee1" class="com.example.bean.Employee">
    <property name="homeAddress" ref="simpleAddress"/>
    <property name="firstName" value="Junior"/>
    <property name="lastName" value="Moore"/>
</bean>

The simpleAddress bean instantiates Address and passes values to its constructor. The employee1 bean uses property elements to inject values to its setter methods. Of special interest is the homeAddress property, which is given the reference of simpleAddress.

The bean declaration of a dependency does not have to appear before the declarations of the beans that use it. In this example, employee1 may appear before simpleAddress.

Constructor-based Dependency Injection

Since the Employee class in Listing F.3 provides a constructor that can take values, you can inject an Address to an instance of Employee through its constructor. For instance, these bean definitions create an instance of Employee and inject three values to its constructor.

<bean name="employee2" class="com.example.bean.Employee">
    <constructor-arg name="firstName" value="Senior"/>
    <constructor-arg name="lastName" value="Moore"/>
    <constructor-arg name="homeAddress" ref="simpleAddress"/>
</bean>
    
<bean name="simpleAddress" class="com.example.bean.Address">
    <constructor-arg name="line1" value="151 Corner Street"/>
    <constructor-arg name="line2" value=""/>
    <constructor-arg name="city" value="Albany"/>
    <constructor-arg name="state" value="NY"/>
    <constructor-arg name="zipCode" value="99999"/>
    <constructor-arg name="country" value="US"/>
</bean>
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.140.186.201