Skip to content

15. Spring IoC

15.1. Introduction

We aim to explore the configuration and integration capabilities of the Spring framework (http://www.springframework.org) as well as to define and use the concept of IoC (Inversion of Control), also known as Dependency Injection

Consider the 3-tier application we just built:

To respond to user requests, the [Application] controller must communicate with the [service] layer. In our example, this was an instance of type [DaoImpl]. The [Application] controller obtained a reference to the [service] layer in its [init] method (Section 14.8.3):

@SuppressWarnings("serial")
public class Application extends HttpServlet {
...

    // service
    ServiceImpl service=null;
...
    // init
    @SuppressWarnings("unchecked")
    public void init() throws ServletException {
...
        // instantiation of the [dao] layer
        DaoImpl dao = new DaoImpl();
        dao.init();
        // instantiate the [service] layer
        service = new ServiceImpl();
        service.setDao(dao);
    }
  • line 6: the [dao] layer was instantiated by explicitly creating a [DaoImpl] instance
  • line 9: the [service] layer has been instantiated by explicitly creating an instance of [ServiceImpl]

Recall that the [DaoImpl] and [ServiceImpl] classes implement interfaces, specifically the [IDao] and [IService] interfaces, respectively. In future versions, the [IDao] interface will be implemented by a class managing a list of people stored in a database. Let’s call this class [DaoBD] for the sake of this example. Replacing the [DaoImpl] implementation of the [dao] layer with the [DaoBD] implementation will require recompiling the [web] layer. Indeed, line 6 above, which instantiates the [dao] layer with a [DaoImpl] type, must now instantiate it with a [DaoBD] type. Our [web] layer is therefore dependent on the [dao] layer. Line 9 above shows that it is also dependent on the [service] layer.

Spring IoC will allow us to create a 3-tier application where the layers are independent of one another, i.e., changing one does not require changing the others. This provides great flexibility in the evolution of the application.

The previous architecture will evolve as follows:

With [Spring IoC], the [Application] controller will obtain the reference it needs from the [service] layer as follows:

  1. in its [init] method, it will ask the [Spring IoC] layer to provide a reference to the [service] layer
  2. [Spring IoC] will then use a configuration XML file that tells it which class should be instantiated and how it should be initialized.
  3. [Spring IoC] returns the reference to the created [service] layer to the [Application] controller.

The advantage of this solution is that the names of the classes instantiating the various layers are no longer hard-coded in the controller’s [init] method but are simply listed in a configuration file. Changing the implementation of a layer will require a change in this configuration file but not in the controller.

Let’s now explore the capabilities of [Spring IoC] using examples.

15.2. Spring IoC in Practice

15.2.1. Spring

[Spring IoC] is part of a larger project available at [http://www.springframework.org/] (May 2006):

  • [1]: Spring uses various third-party technologies referred to here as dependencies. You must download the version with dependencies to avoid having to download the third-party tool libraries later.
  • [2]: The directory structure of the downloaded zip file
  • [3]: The [Spring] distribution, i.e., the .jar archives of the Spring project itself without its dependencies. The [IoC] aspect of Spring is provided by the [spring-core.jar, spring-beans.jar] archives.
  • [4,5]: the third-party tool archives

15.2.2. Eclipse Projects for the Examples

We will build three examples illustrating the use of Spring IoC. They will all be in the following Eclipse project:

Image

The [springioc-examples] project is configured so that the source files and compiled classes are located at the root of the project folder:

  • [1]: The [Eclipse] project folder structure
  • [2]: Spring configuration files are located at the root of the project, and thus in the application’s classpath
  • [3]: the classes from Example 1
  • [4]: the classes from Example 2
  • [5]: the classes from Example 3
  • [6]: The project libraries [spring-core.jar, spring-beans.jar] can be found in the [dist] folder of the Spring distribution, and [commons-logging.jar] in the [lib/jakarta-commons] folder. These three archives have been included in the application's classpath.

15.2.3. Example 1

The elements of Example 1 have been placed in the [springioc01] package of the project:

Image

The [Person] class is as follows:

package istia.st.springioc01;

public class Person {

    // characteristics
    private String name;
    private int age;

    // display Person
    public String toString() {
        return "name=[" + this.name + "], age=[" + this.age + "]";
    }

    // init-close
    public void init() {
        System.out.println("initializing person [" + this.toString() + "]");
    }

    public void close() {
        System.out.println("destroy person [" + this.toString() + "]");
    }

    // getters and setters
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

The class contains:

  • lines 6–7: two private fields, name and age
  • lines 23–38: the get and set methods for these two fields
  • lines 10-12: a toString method to retrieve the value of the [Person] object as a string
  • lines 15-21: an init method that will be called by Spring when the object is created, and a close method that will be called when the object is destroyed

To instantiate objects of type [Person] using Spring, we will use the following [spring-config-01.xml] file:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <bean id="person1" class="istia.st.springioc01.Person" init-method="init" destroy-method="close">
        <property name="name" value="Simon" />
        <property name="age" value="40" />
    </bean>
    <bean id="person2" class="istia.st.springioc01.Person" init-method="init" destroy-method="close">
        <property name="name" value="Brigitte" />
        <property name="age" value="20" />
    </bean>
</beans>
  • Lines 3, 12: The <beans> tag is the root tag of Spring configuration files. Inside this tag, the <bean> tag is used to define the various objects to be created.
  • lines 4–7: definition of a bean
  • line 4: the bean is named [person1] (id attribute) and is an instance of the class [istia.st.springioc01.Person] (class attribute). The instance’s [init] method will be called once it is created (init-method attribute), and the instance’s [close] method will be called before it is destroyed (destroy-method attribute).
  • line 5: defines the value to be assigned to the [name] property (name attribute) of the created [Person] instance. To perform this initialization, Spring will use the [setName] method. This method must therefore exist. This is the case here.
  • Line 6: Same as above for the [age] property.
  • Lines 8–11: Similar definition of a bean named [person2]

The [Main] test class is as follows:

package istia.st.springioc01;

import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

public class Main {

    public static void main(String[] args) {
        // Load the Spring configuration file
        final XmlBeanFactory bf = new XmlBeanFactory(new ClassPathResource("spring-config-01.xml"));
        // Retrieve the [person1] bean
        Person person1 = (Person) bf.getBean("person1");
        System.out.println("person1=" + person1.toString());
        // Retrieve the [person2] bean
        Person person2 = (Person) bf.getBean("person2");
        System.out.println("person2=" + person2.toString());
        // Retrieve the [person2] bean again
        person2 = (Person) bf.getBean("person2");
        System.out.println("person2=" + person2.toString());
        // delete all beans
        bf.destroySingletons();
    }
}

Comments:

  • line 10: to retrieve the beans defined in the [spring-config-01.xml] file, we use an [XmlBeanFactory] object, which allows us to instantiate the beans defined in an XML file. The [spring-config-01.xml] file will be placed in the application’s [ClassPath], i.e., in one of the directories searched by the Java Virtual Machine when it looks for a class referenced by the application. The [ClassPathResource] object is used to search for a resource in an application’s [ClassPath], in this case the [spring-config-01.xml] file. The resulting [bf] object (Bean Factory) allows us to obtain a reference to a bean named "XX" using the statement bf.getBean("XX").
  • Line 12: A reference is requested for the bean named [person1] in the [spring-config-01.xml] file.
  • Line 13: The value of the corresponding [Person] object is displayed.
  • Lines 15–16: We do the same for the bean named [person2].
  • Lines 18–19: We request the bean named [person2] again.
  • Line 21: All beans in [bf] are removed, i.e., those created from the [spring-config-01.xml] file.

Executing the [Main] class yields the following results:

1
2
3
4
5
6
7
init person [name=[Simon], age=[40]]
person1=name=[Simon], age=[40]
init person [name=[Brigitte], age=[20]]
person2=name=[Brigitte], age=[20]
person2=name=[Brigitte], age=[20]
destroy person [name=[Simon], age=[40]]
destroy person [name=[Brigitte], age=[20]]

Comments:

  • Line 1 was obtained by executing line 12 of [Main]. The operation
Person person1 = (Person) bf.getBean("person1");

forced the creation of the bean [person1]. Because [init-method="init"] was written in the definition of the bean [person1], the [init] method of the created [Person] object was executed. The corresponding message is displayed.

  • Line 2: Line 13 of [Main] displayed the value of the created [Person] object.
  • Lines 3–4: The same process occurs for the bean named [person2].
  • Line 5: The operation in lines 18–19 of [Main]
    person2 = (Person) bf.getBean("person2");
    System.out.println("person2=" + person2.toString());

did not cause the creation of a new object of type [Person]. If that had been the case, the [init] method would have been displayed. This is the principle of the singleton. By default, Spring creates only a single instance of the beans in its configuration file. It is an object reference service. If asked for the reference to an object that has not yet been created, it creates it and returns a reference. If the object has already been created, Spring simply returns a reference to it. Here, since [person2] has already been created, Spring simply returns a reference to it.

  • The output on lines 6–7 was triggered by line 21 of [Main], which requests the destruction of all beans referenced by the [XmlBeanFactory bf] object, namely the [person1] and [person2] beans. Because these two beans have the attribute [destroy-method="close"], the [close] method of both beans is executed, causing lines 6–7 to be displayed.

Now that we have covered the basics of a Spring configuration, we will be able to move through our explanations a bit more quickly.

15.2.4. Example 2

The elements of Example 2 are placed in the [springioc02] package of the project:

Image

The [springioc02] package is first created by copying and pasting the [springioc01] package, then the [Car] class is added to it, and the [Main] class is adapted to the new example.

The [Car] class is as follows:

package istia.st.springioc02;

public class Car {
    // characteristics
    private String make;
    private String type;
    private Person owner;

    // constructors
    public Car() {
    }

    public Car(String brand, String model, Person owner) {
        setBrand(brand);
        setType(type);
        setOwner(owner);
    }

    // toString
    public String toString() {
        return "Car: make=[" + this.make + "] model=[" + this.model
                + "] owner=[" + this.owner + "]";
    }

    // getters-setters
    public String getMake() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public Person getOwner() {
        return owner;
    }

    public void setOwner(Person owner) {
        this.owner = owner;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    // init-close
    public void init() {
        System.out.println("init car [" + this.toString() + "]");
    }

    public void close() {
        System.out.println("destroy car [" + this.toString() + "]");
    }
}

The class contains:

  • lines 5–7: three private fields: type, make, and owner. These fields can be initialized and read using the public get and set methods in lines 26–48. They can also be initialized using the Car(String, String, Person) constructor defined in lines 13–17. The class also has a no-argument constructor to comply with the JavaBean standard.
  • lines 20–23: a toString method to retrieve the value of the [Car] object as a string
  • Lines 51–57: an `init` method that will be called by Spring immediately after the object is created, and a `close` method that will be called when the object is destroyed

To create objects of type [Car], we will use the following Spring file [spring-config-02.xml]:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <bean id="person1" class="istia.st.springioc02.Person" init-method="init" destroy-method="close">
        <property name="name" value="Simon" />
        <property name="age" value="40" />
    </bean>
    <bean id="person2" class="istia.st.springioc02.Person" init-method="init" destroy-method="close">
        <property name="name" value="Brigitte" />
        <property name="age" value="20" />
    </bean>
    <bean id="car1" class="istia.st.springioc02.Car" init-method="init" destroy-method="close">
        <constructor-arg index="0" value="Peugeot" />
        <constructor-arg index="1" value="307" />
        <constructor-arg index="2">
            <ref local="person2" />
        </constructor-arg>
    </bean>
</beans>

This file adds a bean with the key "car1" of type [Car] to the beans defined in [spring-config-01.xml] (lines 12–17). To initialize this bean, we could have written:

1
2
3
4
5
6
7
    <bean id="car1" class="istia.st.springioc.domain.Car" init-method="init" destroy-method="close">
        <property name="make" value="Peugeot"/>
        <property name="type" value="307"/>
        <property name="owner">
            <ref local="person2"/>
        </property>
</bean>

Rather than choosing the method already presented in Example 1, we have chosen to use the class's Car(String, String, Person) constructor here.

  • line 12: definition of the bean’s name, its class, the method to execute after its instantiation, and the method to execute after its destruction.
  • line 13: value of the first parameter of the constructor [Car(String, String, Person)].
  • Line 14: Value of the second parameter of the constructor [Car(String, String, Person)].
  • Lines 15–17: Value of the third parameter of the constructor [Car(String, String, Person)]. This parameter is of type [Person]. We provide it with the reference (ref tag) of the bean [person2] defined in the same file (local attribute).

For our tests, we will use the following [Main] class:

package istia.st.springioc02;

import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

public class Main {

    public static void main(String[] args) {
        // Load the Spring configuration file
        final XmlBeanFactory bf = new XmlBeanFactory(new ClassPathResource("spring-config-02.xml"));
        // Retrieve the [car1] bean
        Car Car1 = (Car) bf.getBean("car1");
        System.out.println("Car1=" + Car1.toString());
        // delete the beans
        bf.destroySingletons();
    }
}

The [main] method retrieves the reference to the [car1] bean (line 12) and displays it (line 13). The results are as follows:

1
2
3
4
5
init person [name=[Brigitte], age=[20]]
init car [Car: make=[Peugeot] model=[307] owner=[name=[Brigitte], age=[20]]]
Car1=Car: make=[Peugeot] model=[307] owner=[name=[Brigitte], age=[20]]
destroy car [Car: make=[Peugeot] model=[307] owner=[name=[Brigitte], age=[20]]]
destroy person [name=[Brigitte], age=[20]]

Comments:

  1. The [main] method requests a reference to the [car1] bean (line 12). Spring begins creating the [car1] bean because this bean has not yet been created (singleton). Because the [car1] bean references the [person2] bean, the latter bean is in turn constructed. The [person2] bean has been created. Its [init] method is then executed (line 1) of the results. The [car1] bean is then instantiated. Its [init] method is then executed (line 2) of the results.
  2. Line 3 of the results comes from line 13 of [main]: the value of the bean [car1] is displayed.
  3. Line 15 of [main] requests the destruction of all existing beans, which causes lines 4 and 5 of the results to be displayed.

15.2.5. Example 3

The elements of Example 3 are placed in the [springioc03] package of the project:

Image

The [springioc03] package is first created by copying and pasting the [springioc01] package, then the [GroupePersonnes] class is added, the [Voiture] class is removed, and the [Main] class is adapted to the new example.

The [GroupePersonnes] class is as follows:

package istia.st.springioc03;

import java.util.Map;

public class PeopleGroup {

    // characteristics
    private Person[] members;
    private Map workingGroups;

    // getters - setters
    public Person[] getMembers() {
        return members;
    }

    public void setMembers(Person[] members) {
        this.members = members;
    }

    public Map getWorkGroups() {
        return workGroups;
    }

    public void setWorkGroups(Map workGroups) {
        this.workGroups = workGroups;
    }

    // display
    public String toString() {
        String list = "members: ";
        for (int i = 0; i < this.members.length; i++) {
            list += "[" + this.members[i].toString() + "]";
        }
        return list + ", workgroups = "
                + this.workGroups.toString();
    }

    // init-close
    public void init() {
        System.out.println("init PeopleGroup [" + this.toString() + "]");
    }

    public void close() {
        System.out.println("destroy PeopleGroup [" + this.toString() + "]");
    }
}

Its two private members are:

line 8: members: an array of people who are members of the group

line 9: workingGroups: a dictionary mapping a person to a working group

Note here that the [PersonGroup] class does not define a no-argument constructor. Recall that in the absence of any constructor, there is a "default" constructor, which is the no-argument constructor that does nothing.

The goal here is to demonstrate how Spring allows you to initialize complex objects, such as those with array or dictionary fields. The beans file [spring-config-03.xml] for Example 3 is as follows:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <bean id="person1" class="istia.st.springioc03.Person" init-method="init" destroy-method="close">
        <property name="name" value="Simon" />
        <property name="age" value="40" />
    </bean>
    <bean id="person2" class="istia.st.springioc03.Person" init-method="init" destroy-method="close">
        <property name="name" value="Brigitte" />
        <property name="age" value="20" />
    </bean>
    <bean id="group1" class="istia.st.springioc03.PersonGroup" init-method="init" destroy-method="close">
        <property name="members">
            <list>
                <ref local="person1" />
                <ref local="person2" />
            </list>
        </property>
        <property name="workgroups">
            <map>
                <entry key="Brigitte" value="Marketing" />
                <entry key="Simon" value="Human Resources" />
            </map>
        </property>
    </bean>
</beans>
  • lines 14–17: The <list> tag allows you to initialize an array-type field or one that implements the List interface with different values.
  • lines 20-23: the <map> tag allows you to do the same thing with a field that implements the Map interface.

For our tests, we will use the following [Main] class:

package istia.st.springioc03;

import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

public class Main {

    public static void main(String[] args) {
        // Load the Spring configuration file
        final XmlBeanFactory bf = new XmlBeanFactory(new ClassPathResource("spring-config-03.xml"));
        // Retrieve the [group1] bean
        PeopleGroup group1 = (PeopleGroup) bf.getBean("group1");
        System.out.println("group1=" + group1.toString());
        // delete the beans
        bf.destroySingletons();
    }
}
  • Lines 12-13: We ask Spring for a reference to the [group1] bean and display its value.

The results are as follows:

1
2
3
4
5
6
7
init person [name=[Simon], age=[40]]
init person [name=[Brigitte], age=[20]]
init PeopleGroup [members: [name=[Simon], age=[40]][name=[Brigitte], age=[20]], workgroups = {Brigitte=Marketing, Simon=Human Resources}]
group1 = members: [name=[Simon], age=[40]][name=[Brigitte], age=[20]], workgroups = {Brigitte=Marketing, Simon=Human Resources}
destroy PeopleGroup [members: [name=[Simon], age=[40]][name=[Brigitte], age=[20]], workgroups = {Brigitte=Marketing, Simon=Human Resources}]
destroy person [name=[Simon], age=[40]]
destroy person [name=[Brigitte], age=[20]]

Comments:

  • line 12 of [Main], a reference to the bean [group1] is requested. Spring begins creating this bean. Because the bean [group1] references the beans [person1] and [person2], these two beans are created (lines 1 and 2 of the results). The [group1] bean is then instantiated and its [init] method executed (line 3 of the results).
  • Line 13 of [Main] displays line 4 of the results.
  • Line 15 of [Main] displays lines 5–7 of the results.

15.3. Configuring an n-tier application with Spring

Consider a 3-tier application with the following structure:

UserDataBusinessLayer [business]DataAccessLayer [dao]UserInterfaceLayer [ui]

Here, we aim to demonstrate the benefits of using Spring to build such an architecture.

  • The three layers will be made independent through the use of Java interfaces
  • The integration of the three layers will be handled by Spring

The application structure in Eclipse could be as follows:

  • [1]: the [DAO] layer:
    • [IDao]: the layer interface
    • [Dao1, Dao2]: two implementations of this interface
  • [2]: the [business] layer:
    • [IMetier]: the layer's interface
    • [Business1, Business2]: two implementations of this interface
  • [3]: the [ui] layer:
    • [IUi]: the interface of the layer
    • [Ui1, Ui2]: two implementations of this interface
  • [4]: the application's Spring configuration files. We will configure the application in two ways.
  • [5]: the libraries required by the application. These are the ones used in the previous examples.
  • [6]: the test package. [Main1] will use the [spring-config-01.xml] configuration and [Main2] the [spring-config-02.xml] configuration.

The purpose of this example is to show that we can change the implementation of one or more layers of the application with zero impact on the other layers. Everything happens in the Spring configuration file.


The [dao] layer


The [dao] layer implements the following [IDao] interface:

1
2
3
4
5
package istia.st.springioc.troistier.dao;

public interface IDao {
    public int doSomethingInDaoLayer(int a, int b);
}

The implementation [Dao1] will be as follows:

1
2
3
4
5
6
7
8
package istia.st.springioc.troistier.dao;

public class Dao1 implements IDao {

    public int doSomethingInDaoLayer(int a, int b) {
        return a+b;
    }
}

The implementation [Dao2] will be as follows:

1
2
3
4
5
6
7
8
package istia.st.springioc.troistier.dao;

public class Dao2 implements IDao {

    public int doSomethingInDaoLayer(int a, int b) {
        return a - b;
    }
}

The [business] layer


The [business] layer implements the following [IMetier] interface:

1
2
3
4
5
package istia.st.springioc.troistier.metier;

public interface IBusiness {
      public int doSomethingInBusinessLayer(int a, int b);
}

The [Business1] implementation will be as follows:

package istia.st.springioc.troistier.business;

import istia.st.springioc.troistier.dao.IDao;

public class BusinessLayer1 implements IBusinessLayer {

    // [DAO] layer
    private IDao dao = null;

    public IDao getDao() {
        return dao;
    }

    public void setDao(IDao dao) {
        this.dao = dao;
    }

    public int doSomethingInBusinessLayer(int a, int b) {
        a++;
        b++;
        return dao.doSomethingInDaoLayer(a, b);
    }

}

The [Metier2] implementation will be as follows:

package istia.st.springioc.troistier.metier;

import istia.st.springioc.troistier.dao.IDao;

public class Metier2 implements IMetier {

    // [DAO] layer
    private IDao dao = null;

    public IDao getDao() {
        return dao;
    }

    public void setDao(IDao dao) {
        this.dao = dao;
    }

    public int doSomethingInBusinessLayer(int a, int b) {
        a--;
        b--;
        return dao.doSomethingInDaoLayer(a, b);
    }

}

The [ui] layer


The [ui] layer implements the following [IUi] interface:

1
2
3
4
5
package istia.st.springioc.troistier.ui;

public interface IUi {
    public int doSomethingInUiLayer(int a, int b);
}

The implementation [Ui1] will be as follows:

package istia.st.springioc.troistier.ui;

import istia.st.springioc.troistier.metier.IMetier;

public class Ui1 implements IUi {

    // [business] layer
    private IMetier business = null;

    public IMetier getMetier() {
        return business;
    }

    public void setJob(IMetier business) {
        this.job = business;
    }

    public int doSomethingInUiLayer(int a, int b) {
        a++;
        b++;
        return business.doSomethingInBusinessLayer(a, b);
    }

}

The implementation [Ui2] will be as follows:

package istia.st.springioc.troistier.ui;

import istia.st.springioc.troistier.metier.IMetier;

public class Ui2 implements IUi {

    // [business] layer
    private IMetier business = null;

    public IMetier getMetier() {
        return business;
    }

    public void setJob(IMetier business) {
        this.job = business;
    }

    public int doSomethingInUiLayer(int a, int b) {
        a--;
        b--;
        return business.doSomethingInBusinessLayer(a, b);
    }

}

Spring configuration files


The first [spring-config-01.xml]:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <!-- the DAO class -->
    <bean id="dao" class="istia.st.springioc.troistier.dao.Dao1"/>
    <!-- the business class -->
    <bean id="business"     class="istia.st.springioc.troistier.metier.Metier1">
        <property name="dao">
            <ref local="dao" />
        </property>
    </bean>
    <!-- the UI class -->
    <bean id="ui" class="istia.st.springioc.troistier.ui.Ui1">
        <property name="business">
            <ref local="business" />
        </property>
    </bean>
</beans>

The second [spring-config-02.xml]:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <!-- the DAO class -->
    <bean id="dao" class="istia.st.springioc.troistier.dao.Dao2"/>
    <!-- the business class -->
    <bean id="business"
        class="istia.st.springioc.troistier.metier.Metier2">
        <property name="dao">
            <ref local="dao" />
        </property>
    </bean>
    <!-- the UI class -->
    <bean id="ui" class="istia.st.springioc.troistier.ui.Ui2">
        <property name="business">
            <ref local="metier" />
        </property>
    </bean>
</beans>

Test programs


The [Main1] program is as follows:

package istia.st.springioc.troistier.main;

import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

import istia.st.springioc.troistier.ui.IUi;

public class Main1 {

    public static void main(String[] args) {
        // retrieve an implementation of the IUi interface
        IUi ui = (IUi) (new XmlBeanFactory(new ClassPathResource("spring-config-01.xml"))).getBean("ui");
        // use the class
        int a = 10, b = 20;
        int res = ui.doSomethingInUiLayer(a, b);
        // display the result
        System.out.println("ui(" + a + "," + b + ")=" + res);
    }

}

The [Main1] program uses the [spring-config-01.xml] configuration file and therefore the [Ui1, Metier1, Dao1] implementations of the layers. The results displayed in the Eclipse console:

ui(10,20)=34

The [Main2] program is as follows:

package istia.st.springioc.troistier.main;

import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

import istia.st.springioc.troistier.ui.IUi;

public class Main2 {

    public static void main(String[] args) {
        // retrieve an implementation of the IUi interface
        IUi ui = (IUi) (new XmlBeanFactory(new ClassPathResource("spring-config-02.xml"))).getBean("ui");
        // use the class
        int a = 10, b = 20;
        int res = ui.doSomethingInUiLayer(a, b);
        // display the result
        System.out.println("ui(" + a + "," + b + ")=" + res);
    }

}

The [Main2] program uses the [spring-config-02.xml] configuration file and therefore the [Ui2, Metier2, Dao2] implementations of the layers. The results displayed in the Eclipse console:

ui(10,20)=-10

15.4. Conclusion

The application we have built is highly scalable. We can change the implementation of a layer simply by configuring it. The code for the other layers remains unchanged. This is achieved through the IoC concept, which is one of the two pillars of Spring. The other pillar is AOP (Aspect-Oriented Programming), which we have not covered. It allows you to add "behavior" to a class method—also via configuration—without modifying the method’s code.