Spring dependency injection methods: set injection and constructor injection

Spring

  • Spring6 enables Log4j2 logging framework
  • dependency injection
    • set injection
      • set injects simple types
        • Classic case: injecting values into attributes of the data source:
      • Inject into array
      • Inject into List collection
      • Inject Set collection
      • Inject into Map collection
    • constructor injection

Spring6 enables Log4j2 logging framework

Starting from Spring 5, the integrated logging framework supported by the Spring framework is Log4j2. How to enable the logging framework:
Step 1: Introduce Log4j2 dependencies

<!--log4j2 dependency-->
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-core</artifactId>
  <version>2.19.0</version>
</dependency>
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-slf4j2-impl</artifactId>
  <version>2.19.0</version>
</dependency>

Step 2: Provide the log4j2.xml configuration file under the root path of the class (the file name is fixed to: log4j2.xml, and the file must be placed under the class root path.)

<?xml version="1.0" encoding="UTF-8"?>

<configuration>

    <loggers>
        <!--
            level specifies the log level, from low to high priority:
                ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF
        -->
        <root level="DEBUG">
            <appender-ref ref="spring6log"/>
        </root>
    </loggers>

    <appenders>
        <!--Output log information to the console-->
        <console name="spring6log" target="SYSTEM_OUT">
            <!--Control the format of log output-->
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/>
        </console>
    </appenders>

</configuration>

Step 3: Use the logging framework

Logger logger = LoggerFactory.getLogger(FirstSpringTest.class);
logger.info("I am a log message");

Dependency injection

● Dependency refers to the relationship between objects and objects.
● Injection refers to a data transfer behavior, which creates a relationship between objects and objects through injection behavior.
There are two common implementation methods of dependency injection:
● The first type: set injection
● The second type: constructor injection

New module: spring6-003-dependency-injection

set injection

Set injection is implemented based on the set method. The bottom layer will call the set method corresponding to the attribute through the reflection mechanism and then assign a value to the attribute. This method requires that the attribute must provide a set method to the outside world.

pom.xml

<?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>com.powernode</groupId>
    <artifactId>spring6-002-dependency-injection</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <repositories>
        <repository>
            <id>repository.spring.milestone</id>
            <name>Spring Milestone Repository</name>
            <url>https://repo.spring.io/milestone</url>
        </repository>
    </repositories>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.0.0-M2</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

</project>

UserDao.java

package com.w.spring6.dao;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class UserDao {<!-- -->

    private static final Logger logger= LoggerFactory.getLogger(UserDao.class);
    public void insert(){<!-- -->
        //System.out.println("Database is saving information");
        logger.info("Database is saving information");
    }

}

UserService.java

package com.w.spring6.service;

import com.w.spring6.dao.UserDao;
import com.w.spring6.dao.VipDao;

public class UserService {<!-- -->

    private UserDao userDao;
    private VipDao vipDao;

    public void setAbc(VipDao vipDao){<!-- -->
        this.vipDao=vipDao;
    }


    //For set injection, a set method must be provided
    //spring container will call this set method to assign value to userDao
/* //What I wrote does not comply with the javabean specification
    public void setMySQLUserDao(UserDao xyz){
        this.userDao=xyz;
    }*/

    //idea automatically generates compliance with javabean specifications
    public void setUserDao(UserDao userDao) {<!-- -->
        this.userDao = userDao;
    }

    public void saveUser(){<!-- -->
        userDao.insert();
        vipDao.insert();

    }
}

spring.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>

    <bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
        <property name="userDao" ref="userDaoBean"/>
    </bean>

</beans>

SpringDITest.java

package com.w.spring6.text;

import com.w.spring6.bean.QianDaYe;
import com.w.spring6.bean.User;
import com.w.spring6.jdbc.MyDataSource;
import com.w.spring6.service.CustomerService;
import com.w.spring6.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringDITest {<!-- -->

    @Test
    public void testSetDI(){<!-- -->
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        UserService userService = applicationContext.getBean("userServiceBean", UserService.class);
        userService.save();
    }
}

operation result:

Implementation principle:
Obtain the attribute name through the property tag: userDao
The set method name is inferred from the attribute name: setUserDao
Call the setUserDao() method through the reflection mechanism to assign values to properties.
The name of the property tag is the attribute name.
The ref of the property tag is the id of the bean object to be injected.

Summary: The core implementation principle of set injection: calling the set method through the reflection mechanism to assign values to properties and create a relationship between two objects.

set injects simple types

The previously injected object attribute was UserDao. How to write it when the object attribute is int type?

Example: Write a program to assign a value of 20 to the age attribute of a User object:

The first step: define the User class, provide the age attribute, and provide the setter method of the age attribute.

package com.w.spring6.bean;

public class User {<!-- -->

    private String username;
    private String password;
    private int age;

    @Override
    public String toString() {<!-- -->
        return "User{" +
                "username='" + username + ''' +
                ", password='" + password + ''' +
                ", age=" + age +
                '}';
    }
}

Step 2: Write the spring configuration file: beans.xml, add

<!--Inject simple types-->
    <bean id="userBean" class="com.w.spring6.bean.User">
            <property name="username" value="Zhang San"/>
            <property name="password" value="123"/>
            <property name="age" value="20"/>
    </bean>

Note: If assigning a value to a simple type, use the value attribute or value tag. instead of ref

Step 3: Write a test program

 @Test
    public void testSimpleTypeSet() {<!-- -->

        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("beans.xml");
        User user=applicationContext.getBean("userBean", User.class);
        System.out.println(user);
    }

operation result:

Classic case: Injecting values into attributes of the data source:

Write a data source by yourself. All data sources must implement the javax.sql.DataSource interface, and the data source should have information to connect to the database, such as: driver, url, username, password, etc.

Step 1: Create a new MyDataSource.java, provide 4 properties, and provide a setter method.

package com.w.spring6.jdbc;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;


/*
All data sources must implement the Java specification: java.sql.DataSource
What is a data source: Anything that can provide you with a Connection object is a data source.
 */
public class MyDataSource implements DataSource {<!-- --> //The data source can be managed by the spring container

    private String driver;
    private String url;
    private String username;
    private String password;

    @Override
    public String toString() {<!-- -->
        return "MyDataSource{" +
                "driver='" + driver + ''' +
                ", url='" + url + ''' +
                ", username='" + username + ''' +
                ", password='" + password + ''' +
                '}';
    }

    public void setDriver(String driver) {<!-- -->
        this.driver = driver;
    }

    public void setUrl(String url) {<!-- -->
        this.url = url;
    }

    public void setUsername(String username) {<!-- -->
        this.username = username;
    }

    public void setPassword(String password) {<!-- -->
        this.password = password;
    }


    @Override
    public Connection getConnection() throws SQLException {<!-- -->
        return null;
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {<!-- -->
        return null;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {<!-- -->
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {<!-- -->

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {<!-- -->

    }

    @Override
    public int getLoginTimeout() throws SQLException {<!-- -->
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {<!-- -->
        return null;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {<!-- -->
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {<!-- -->
        return false;
    }
}

Step 2: Write the spring configuration file: spring-properties.xml. We provide setter methods for the four properties of driver, url, username, and password. We can use spring’s dependency injection to complete the creation of data source objects and assignment of properties.

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--
Import external properties files
Step 1: Introduce the context namespace
Step 2: Use the tag location attribute to specify the path to the attribute configuration file

-->

    <context:property-placeholder location="jdbc.properties"/>

        <bean id="dataSource" class="com.w.spring6.jdbc.MyDataSource">
            <property name="driver" value="${jdbc.driverClass}"></property>
            <property name="url" value="${jdbc.url}"></property>
            <property name="username" value="${jdbc.username}"></property>
            <property name="password" value="${jdbc.password}"></property>
        </bean>

</beans>

Step 3: Write a test program

@Test
    public void testProperties() {<!-- -->
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring-properties.xml");
        MyDataSource da=applicationContext.getBean("dataSource", MyDataSource.class);
        System.out.println(da);
    }

operation result:

Inject array

When the elements in the array are simple types and not simple types

QianDaYe.java

package com.w.spring6.bean;

import java.util.Arrays;

public class QianDaYe {<!-- -->

    private String[] aiHaos;

    private Woman[] womens;

    @Override
    public String toString() {<!-- -->
        return "QianDaYe{" +
                "aiHaos=" + Arrays.toString(aiHaos) +
                ", womens=" + Arrays.toString(womens) +
                '}';
    }

    public void setWomens(Woman[] womens) {<!-- -->
        this.womens = womens;
    }

    public void setAiHaos(String[] aiHaos) {<!-- -->
        this.aiHaos = aiHaos;
    }
}

Woman.java

package com.w.spring6.bean;

public class Woman {<!-- -->

    private String name;

    @Override
    public String toString() {<!-- -->
        return "Woman{" +
                "name='" + name + ''' +
                '}';
    }

    public void setName(String name) {<!-- -->
        this.name = name;
    }
}

spring-array.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="w1" class="com.w.spring6.bean.Woman">
        <property name="name" value="小花"/>
    </bean>

    <bean id="w2" class="com.w.spring6.bean.Woman">
        <property name="name" value="Xiaohong"/>
    </bean>

    <bean id="w3" class="com.w.spring6.bean.Woman">
        <property name="name" value="Xiaoli"/>
    </bean>


    <bean id="yuQian" class="com.w.spring6.bean.QianDaYe">
        <property name="aiHaos">
            <array>
                <value>Smoking</value>
                <value>Drink</value>
                <value>Playing mahjong</value>
            </array>
        </property>

<!-- This array is not a simple type-->
        <property name="womens">
            <array>
                <ref bean="w1"/>
                <ref bean="w2"/>
                <ref bean="w3"/>
            </array>

        </property>


    </bean>

</beans>

SpringDITest.java

@Test
    public void testArray() {<!-- -->

        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring-array.xml");
        QianDaYe yuQian=applicationContext.getBean("yuQian", QianDaYe.class);
        System.out.println(yuQian);

    }

Test Results:

Inject List collection

List collection: ordered and repeatable

Note: Use the list tag when injecting a List collection. If the List collection is a simple type, use the value tag. Otherwise, use the ref tag.

People.java

package com.w.spring6.bean;
import java.util.List;


public class People {<!-- -->
    //A person has multiple names
    private List<String> names;

    public void setNames(List<String> names) {<!-- -->
        this.names = names;
    }

    @Override
    public String toString() {<!-- -->
        return "People{" +
                "names=" + names +
                '}';
    }
}

spring-collection.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="peopleBean" class="com.w.spring6.bean.People">
        <property name="names">
            <list>
                <value>Iron Hammer</value>
                <value>Zhang San</value>
                <value>Zhang San</value>
                <value>Zhang San</value>
                <value>Wolf</value>
            </list>
        </property>
    </bean>
</beans>

test program:

 @Test
    public void testCollection(){<!-- -->
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-collection.xml");
        People peopleBean = applicationContext.getBean("peopleBean", People.class);
        System.out.println(peopleBean);
    }

Test Results:

The subsequent Set collection and Map collection are the same and will not be demonstrated.

Inject Set collection

 <set>
                <!--You can use ref for non-simple types, and value for simple types-->
                <value>110</value>
                <value>120</value>
                <value>119</value>
            </set>

Inject Map collection

 <map>
                <!--If the key is not a simple type, use the key-ref attribute-->
                <!--If value is not a simple type, use the value-ref attribute-->
                <entry key="1" value="Beijing"/>
                <entry key="2" value="Shanghai"/>
                <entry key="3" value="Shenzhen"/>
            </map>

Construction injection

Core principle: assign values to properties by calling the constructor method.

OrderDao

package com.w.spring6.dao;

public class OrderDao {<!-- -->
    public void deleteById(){<!-- -->
        System.out.println("Deleting order...");
    }
}

OrderService

package com.w.spring6.service;

import com.w.spring6.dao.OrderDao;

public class OrderService {<!-- -->
    private OrderDao orderDao;

    // Call the constructor through reflection mechanism to assign values to properties
    public OrderService(OrderDao orderDao) {<!-- -->
        this.orderDao = orderDao;
    }

    public void delete(){<!-- -->
        orderDao.deleteById();
    }
}

spring.xml added

 <bean id="orderDaoBean" class="com.w.spring6.dao.OrderDao"/>
    <bean id="orderServiceBean" class="com.w.spring6.service.OrderService">
        <!--index="0" indicates the first parameter of the constructor, and the orderDaoBean object is passed to the first parameter of the constructor. -->
        <constructor-arg index="0" ref="orderDaoBean"/>
    </bean>

test program:
SpringDITest.java

 @Test
    public void testConstructorDI(){<!-- -->
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        OrderService orderServiceBean = applicationContext.getBean("orderServiceBean", OrderService.class);
        orderServiceBean.delete();
    }

When injected through the constructor method:
● You can use subscripts
● You can pass the parameter name
● You do not need to specify the subscript and parameter name, and the type can be automatically inferred.