1. 程式人生 > 實用技巧 >Spring5入門-02-基於構造器和setter的DI(依賴注入)

Spring5入門-02-基於構造器和setter的DI(依賴注入)

一、前言

本文目的:瞭解Spring建立Bean、構造器依賴注入的一些方法。



二、依賴

注意:這裡用到的應該是spring-context,但是spring-webmvc由於繼承的關係會有一張依賴網:

算是省心省力吧。

<!--Spring-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${org.springframework.version}</version>
</dependency>

<!--JUnit-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>


三、建立實體類

路徑

程式碼

為了後面看得清楚Spring用的構造方法,在構造方法中加了列印:

User.java:

package com.huangdekai.pojo;

/**
 * @Autord: HuangDekai
 * @Date: 2020/9/23 10:28
 * @Version: 1.0
 * @since: jdk11
 */
public class User {
    private int id;
    private String name;

    public User() {
        System.out.println("User無參構造");
    }

    public User(int id, String name) {
        this.id = id;
        this.name = name;
        System.out.println("User有參構造");
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}


四、配置文件

路徑

程式碼

<?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="user" class="com.huangdekai.pojo.User">
        <property name="id" value="1" />
        <property name="name" value="Duzhuan" />
    </bean>

</beans>


五、測試樣例

路徑

程式碼

import com.huangdekai.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @Autord: HuangDekai
 * @Date: 2020/9/23 10:41
 * @Version: 1.0
 * @since: jdk11
 */
public class UserTest {
    @Test
    public void UserTest(){
        ApplicationContext context = new ClassPathXmlApplicationContext("pojo.xml");
        User user = (User) context.getBean("user");
        System.out.println(user);
    }
}

結果:



六、有參構造

官方文件為此介紹了三種方法(其實是4種,但有一種是注入實體類的,如下所示,先按下不講):

package x.y;

public class ThingOne {

   public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
       // ...
   }
}
<beans>
   <bean id="beanOne" class="x.y.ThingOne">
       <constructor-arg ref="beanTwo"/>
       <constructor-arg ref="beanThree"/>
   </bean>

   <bean id="beanTwo" class="x.y.ThingTwo"/>

   <bean id="beanThree" class="x.y.ThingThree"/>
</beans>

6.1 構造引數型別匹配(Constructor argument type matching)

配置檔案改為:

<?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="user" class="com.huangdekai.pojo.User">-->
<!--        <property name="id" value="1" />-->
<!--        <property name="name" value="Duzhuan" />-->
<!--    </bean>-->

    <bean id="user" class="com.huangdekai.pojo.User">
        <constructor-arg type="int" value="1"/>
        <constructor-arg type="java.lang.String" value="Duzhuan"/>
    </bean>

</beans>

很顯然,除了基本型別,比較複雜一點的類都需要全限定名

再次執行測試樣例:

但是很顯然,這是一種十分不推薦的注入方式,就比如,一個類裡有多個String,那就不能使用,即使是簡單的場景,這種可讀性很低的方式也是不推薦的。


6.2 構造引數(Constructor argument index)

再次註釋掉前面的內容,寫入新的:

<bean id="user" class="com.huangdekai.pojo.User">
     <constructor-arg index="0" value="1"/>
     <constructor-arg index="1" value="Duzhuan"/>
</bean>

很顯然,index表示的就是有參構造中,構造引數的順序序號。從上面也可以看出,index是0開始的。

這種方法比起第一種構造方法的好處就是,當有多個屬性都是同一資料型別時候,不衝突。


6.3 構造引數名(Constructor argument name)

再次註釋掉上面的內容,寫入新程式碼:

<bean id="user" class="com.huangdekai.pojo.User">
    <constructor-arg name="id" value="1"/>
    <constructor-arg name="name" value="Duzhuan"/>
</bean>

比較推薦使用這種方法。

name的值顯然就是User的屬性名稱。



七、基於setter的依賴注入

官方文件特別提出這一類注入:

Setter-based DI is accomplished by the container calling setter methods on your beans after invoking a no-argument constructor or a no-argument static factory method to instantiate your bean.

即:

基於Setter的依賴注入是由容器呼叫無參建構函式一個無參靜態工程方法後呼叫bean上的setter方法完成的。

看下面的例項。


7.1 建立一個Role

路徑

程式碼

package com.huangdekai.pojo;

/**
 * @Autord: HuangDekai
 * @Date: 2020/9/23 12:18
 * @Version: 1.0
 * @since: jdk11
 */
public class Role {
    private int id;
    private String roleName;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    @Override
    public String toString() {
        return "Role{" +
                "id=" + id +
                ", roleName='" + roleName + '\'' +
                '}';
    }
}

7.2 修改pojo.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="user" class="com.huangdekai.pojo.User">-->
<!--        <property name="id" value="1" />-->
<!--        <property name="name" value="Duzhuan" />-->
<!--    </bean>-->

<!--    <bean id="user" class="com.huangdekai.pojo.User">-->
<!--        <constructor-arg type="int" value="1"/>-->
<!--        <constructor-arg type="java.lang.String" value="Duzhuan"/>-->
<!--    </bean>-->

<!--        <bean id="user" class="com.huangdekai.pojo.User">-->
<!--            <constructor-arg index="0" value="1"/>-->
<!--            <constructor-arg index="1" value="Duzhuan"/>-->
<!--        </bean>-->

<!--    <bean id="user" class="com.huangdekai.pojo.User">-->
<!--        <constructor-arg name="id" value="1"/>-->
<!--        <constructor-arg name="name" value="Duzhuan"/>-->
<!--    </bean>-->

    <bean id="role" class="com.huangdekai.pojo.Role">
        <property name="id" value="1"/>
        <property name="roleName" value="Duzhuan"/>
    </bean>

</beans>


7.3 測試樣例

路徑

程式碼

import com.huangdekai.pojo.Role;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @Autord: HuangDekai
 * @Date: 2020/9/23 12:30
 * @Version: 1.0
 * @since: jdk11
 */
public class RoleTest {
    @Test
    public void roleTest(){
        ApplicationContext context = new ClassPathXmlApplicationContext("pojo.xml");
        Role role = (Role) context.getBean("role");
        System.out.println(role);
    }
}

結果:

有沒有覺得很眼熟?

其實這正是一開始的時候的例子,只是這裡由於沒有有參建構函式,無需顯式地寫無參建構函式罷了。

如果用IDEA,現在去註釋掉Role的set方法,再去看看pojo.xml會有提示錯誤:



八、一些有趣的東西

有沒有一些由於粗心沒註釋掉某些程式碼而出現的有趣現象?

我們將pojo.xml中的一個關於user這個bean的方法的註釋去掉

<?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="user" class="com.huangdekai.pojo.User">-->
<!--        <property name="id" value="1" />-->
<!--        <property name="name" value="Duzhuan" />-->
<!--    </bean>-->

<!--    <bean id="user" class="com.huangdekai.pojo.User">-->
<!--        <constructor-arg type="int" value="1"/>-->
<!--        <constructor-arg type="java.lang.String" value="Duzhuan"/>-->
<!--    </bean>-->

<!--        <bean id="user" class="com.huangdekai.pojo.User">-->
<!--            <constructor-arg index="0" value="1"/>-->
<!--            <constructor-arg index="1" value="Duzhuan"/>-->
<!--        </bean>-->

    <bean id="user" class="com.huangdekai.pojo.User">
        <constructor-arg name="id" value="1"/>
        <constructor-arg name="name" value="Duzhuan"/>
    </bean>

    <bean id="role" class="com.huangdekai.pojo.Role">
        <property name="id" value="1"/>
        <property name="roleName" value="Duzhuan"/>
    </bean>

</beans>

然後執行RoleTest.java中的roleTest方法:

RoleTest.java中根本就沒用到User,但是這還是表示,有一個User物件被建立了。

顯然,當我們用ClassPathXmlApplication創建出一個容器的時候,整個配置裡設定好的Bean都被創建出來交給容器管理了。