Spring02_XML-based IOC

Keywords: Java Spring xml Maven encoding

For the source code of this tutorial, please visit: tutorial_demo

In the previous tutorial, we learned how to use factory mode decoupling to give the creation of objects to a custom factory class by a programmer. In this tutorial, we will learn how to use Spring's IOC to solve coupling problems.

1. What is IOC

IOC: Inversion of Control, control reversal, delegate the power to create objects to the framework.In the past, objects were created by developers in the new way. With IOC, developers don't need new. They just need to get them from the Spring container, which we can think of as a container to hold objects. Control of creating objects has been transferred, from the developer to the Spring container, or to the Spring framework.This transfer of control is what we call inversion of control.

Purpose: To reduce computer program coupling and decouple code dependencies.

2. Use IOC (the first Spring program)

2.1. Creating projects

  1. Create a new Maven project in Idea;

  2. Add coordinates when the project is created.

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>org.example</groupId>
        <artifactId>ioc</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>jar</packaging>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.2.6.RELEASE</version>
            </dependency>
        </dependencies>
    </project>
    

2.2. Add Related Classes

2.2.1, Creating persistent layer interfaces

package org.codeaction.dao;

public interface IAccountDao {
    void saveAccount();
}

2.2.2, Create a persistent layer interface implementation class

package org.codeaction.dao.impl;

import org.codeaction.dao.IAccountDao;

public class AccountDaoImpl implements IAccountDao {
    @Override
    public void saveAccount() {
        System.out.println("Account saved successfully");
    }
}

2.2.3, Create Business Tier Interface

package org.codeaction.service;

public interface IAccountService {
    void saveAccount();
}

2.2.4, Create Business Tier Interface Implementation Class

package org.codeaction.service.impl;

import org.codeaction.dao.IAccountDao;
import org.codeaction.dao.impl.AccountDaoImpl;
import org.codeaction.service.IAccountService;

public class AccountServiceImpl implements IAccountService {
    private IAccountDao accountDao = new AccountDaoImpl();
    @Override
    public void saveAccount() {
        accountDao.saveAccount();
    }
}

Notice the implementation of this class, which will be explained at the end of this tutorial.

2.3, Add XML Configuration File

The XML file is in the resource directory.

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--Give object creation to spring To manage-->
    <bean id="accountDao" class="org.codeaction.dao.impl.AccountDaoImpl"></bean>
    <bean id="accountService" class="org.codeaction.service.impl.AccountServiceImpl"></bean>
</beans>

bean Tag Role: Configuration lets Spring create objects.By default, parameterless constructors are called and cannot be created successfully without them.

bean tag properties:

  • id: Provides a unique identifier in the container for the object to be retrieved;
  • Class: Specifies the fully qualified class name of the class used to reflect the creation of the object, calling the parameterless constructor by default;
  • Scope: Specify the scope of the object, singleton by default, as mentioned in 3.1;
  • init-method: Specifies the name of the initialization method in the class;
  • destroy-method: Specifies the destroy method name in the class.

2.4, Add Test Class

Create a class with the main method for testing

package org.codeaction.ui;

import org.codeaction.dao.IAccountDao;
import org.codeaction.service.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AccountUI {
    public static void main(String[] args) {
        //1. Get Core Container Objects
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //2. Get the Bean object based on the id, which is the ID configured in the bean tag
        IAccountService accountService = (IAccountService) context.getBean("accountService");
        IAccountDao accountDao = context.getBean("accountDao", IAccountDao.class);

        System.out.println(accountService);
        System.out.println(accountDao);
        accountService.saveAccount();
    }
}

Run the main method with the console output as follows:

org.codeaction.service.impl.AccountServiceImpl@754ba872
org.codeaction.dao.impl.AccountDaoImpl@146ba0ac
//Account saved successfully

Differences between 2.4.1, BeanFactory, and ApplicationContext

  1. BeanFactory is the top-level interface in the Spring container and ApplicationContext is its subinterface.
  2. ApplicationContext creates objects by default whenever a configuration file is read;
  3. When BeanFactory uses time to create objects;
  4. ApplicationContext is used to create a single object and BeanFactory is used to create multiple objects.

2.4.2, Implementation class of the ApplicationContext interface

  1. ClassPathXmlApplicationContext can load a configuration file under the class path, requiring that the configuration file must be under the class path, otherwise it cannot be loaded.
  2. FileSystemXmlApplicationContext can load a configuration file in any path to the disk (access is required)
  3. The AnnotationConfigApplicationContext is used to read annotations to create containers, which will be covered in later articles.

3. In-depth explanation

3.1. Scope and life cycle of bean s

The scope of a bean is set by the scope property in the bean tag, which can have the following values:

  • Singleton: default, singleton;
  • prototype: multiple cases;
  • Request: In a web project, Spring creates an object for a Bean that is stored in the request domain;
  • Session: In a web project, Spring creates a Bean object that is stored in the session domain;
  • global session: In a web project, applies to the Portlet environment.Without a Portlet environment, a globalSession would be equivalent to a session.

Let's focus on singleton and prototype.

3.1.1,singleton

An application has only one instance of an object, and its scope is the entire application.
Lifecycle:

  • Object birth: When an application loads and a container is created, the object is created;
  • The object is alive: as long as the container is there, the object is alive;
  • Object death: When an application unloads and destroys a container, the object is destroyed.

3.1.2,prototype

Every time an object is accessed, the object instance is recreated.
Lifecycle:

  • Object birth: When using an object, create a new object instance;
  • The object is alive: as long as the object is in use, it will always live;
  • Object death: When an object is not used for a long time, it is recycled by the Java garbage collector.

3.1.3, Case (code based on first Spring program)

3.1.3.1, Modify Business Tier Implementation Class

Add init and destroy methods

package org.codeaction.dao.impl;

import org.codeaction.dao.IAccountDao;

public class AccountDaoImpl implements IAccountDao {
    @Override
    public void saveAccount() {
        System.out.println("Account saved successfully");
    }

    public void init() {
        System.out.println("dao init");
    }

    public void destroy() {
        System.out.println("dao destroy");
    }
}
3.1.3.2, Modifying persistence layer implementation classes

Add init and destroy methods

package org.codeaction.dao.impl;

import org.codeaction.dao.IAccountDao;

public class AccountDaoImpl implements IAccountDao {
    @Override
    public void saveAccount() {
        System.out.println("Account saved successfully");
    }

    public void init() {
        System.out.println("dao init");
    }

    public void destroy() {
        System.out.println("dao destroy");
    }
}
3.1.3.3, Modify XML 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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--Give object creation to spring To manage-->
    <!--To configure bean Specify initialization and destruction methods and scopes-->
    <bean
            id="accountDao"
            class="org.codeaction.dao.impl.AccountDaoImpl"
            scope="singleton"
            init-method="init"
            destroy-method="destroy"></bean>
    <bean
            id="accountService"
            class="org.codeaction.service.impl.AccountServiceImpl"
            scope="prototype"
            init-method="init"
            destroy-method="destroy"></bean>
</beans>
3.1.3.4, Modify test classes
package org.codeaction.ui;

import org.codeaction.dao.IAccountDao;
import org.codeaction.service.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AccountUI {
    public static void main(String[] args) {
        //1. Get Core Container Objects
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //2. Get the Bean object based on the id, which is the ID configured in the bean tag
        IAccountService accountService1 = (IAccountService) context.getBean("accountService");
        IAccountDao accountDao1 = (IAccountDao) context.getBean("accountDao");
        IAccountService accountService2 = (IAccountService) context.getBean("accountService");
        IAccountDao accountDao2 = (IAccountDao) context.getBean("accountDao");

        System.out.println("accountDao1 == accountDao2 ? " + (accountDao1 == accountDao2));
        System.out.println("accountService1 == accountService2 ? " + (accountService1 == accountService2));
        //Container Destroy
        context.close();
    }
}

Step through the program with the following output:

dao init
service init
service init
accountDao1 == accountDao2 ? true
accountService1 == accountService2 ? false
dao destroy

The output verifies that:

  • A singleton's bean object is created only once in the container and is created when the container is created.
  • The bean object of a prototype can be created multiple times in a container, and when used, a new object is created.
  • The bean object of a singleton is also destroyed when the container is destroyed;
  • The bean object of the prototype is garbage collected (not visible from the console).

3.2. Three ways to instantiate bean s

3.2.1, three ways of explaining

  1. Use the default parameterless constructor;
  2. Create objects using the static factory method;
  3. Create objects using the instance factory method.

3.2.2, Case (code based on first Spring program)

3.2.2.1, Create Static Factory Class
package org.codeaction.factory;

import org.codeaction.service.IAccountService;
import org.codeaction.service.impl.AccountServiceImpl;

/**
 * Simulate a static factory to create a business-tier implementation class
 */
public class StaticFactory {
    public static IAccountService  createAccountService() {
        return new AccountServiceImpl();
    }
}
3.2.2.2, Create Instance Factory Class
package org.codeaction.factory;

import org.codeaction.service.IAccountService;
import org.codeaction.service.impl.AccountServiceImpl;

/**
 * Simulate an instance factory to create a business-tier implementation class
 * This factory creates objects and must have existing factory instance objects before calling methods
 */
public class InstanceFactory {
    public IAccountService createAccountService(){
        return new AccountServiceImpl();
    }
}
3.2.2.3, Modifying an XML 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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--Mode 1: Use the defau lt parameterless construction method-->
    <bean id="accountService1" class="org.codeaction.service.impl.AccountServiceImpl"></bean>

    <!--
        Mode 2: Create objects using the static factory method
        id property: Specifies the id of the bean, used to get it from the container
        Class attribute: specifies the fully qualified class name of the static factory
        factory-method property: specifies the static method of the production object
    -->
    <bean
	id="accountService2"
        class="org.codeaction.factory.StaticFactory"
        factory-method="createAccountService"></bean>
    <!--
        Mode 3: Create objects using the instance factory method
        Start by leaving the creation of the factory to spring.
        Then use the bean of the factory to call the method inside.
        factory-bean property: Used to specify the id of the instance factory bean.
        factory-method property: Specifies the method used to create objects in the instance factory.
    -->
    <bean id="factory" class="org.codeaction.factory.InstanceFactory"></bean>
    <bean id="accountService3" factory-bean="factory" factory-method="createAccountService"></bean>
</beans>
3.2.2.4, Modify test classes
package org.codeaction.ui;

import org.codeaction.dao.IAccountDao;
import org.codeaction.service.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AccountUI {
    public static void main(String[] args) {
        //1. Get Core Container Objects
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //2. Get the Bean object based on the id, which is the ID configured in the bean tag
        IAccountService accountService1 = (IAccountService) context.getBean("accountService1");
        IAccountService accountService2 = (IAccountService) context.getBean("accountService2");
        IAccountService accountService3 = (IAccountService) context.getBean("accountService3");

        System.out.println(accountService1);
        System.out.println(accountService2);
        System.out.println(accountService3);
    }
}

Run the test class with the console output as follows:

org.codeaction.service.impl.AccountServiceImpl@42dafa95
org.codeaction.service.impl.AccountServiceImpl@6500df86
org.codeaction.service.impl.AccountServiceImpl@402a079c

4. Current Problems

Through this tutorial, we have a preliminary understanding of IOC.The transfer of control is achieved by giving the right to create objects to the Spring container through IOC.In this 2.2.4 tutorial, you create an implementation class for a business-tier interface, and the properties inside it are still assigned as new objects, there is still no decoupling, and the objects of service and dao are still stirred together. How can you solve this problem?Next we will learn DI (Dependent Injection), which can solve the current problem.

Posted by TCovert on Sun, 24 May 2020 10:48:08 -0700