Spring使用篇(二)—— Spring依赖注入的三种方式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_36378917/article/details/86489589

1、综述

  在实际环境中实现IoC容器的方式主要分为两大类,一类是依赖查找,依赖查找是通过资源定位,把赌赢的资源查找回来;另一种是依赖注入,而Spring主要使用的是依赖注入(DI)。一般而言,依赖注入可以分为3中方式:

  • 构造器注入
  • setter注入
  • 接口注入

  构造器注入和setter注入是主要的方式,而接口注入是从别的地方注入的方式,比如在Web工程中,配置的数据源往往是通过服务器(比如Tomcat)去配置的,这个时候可以用JNDI的形式通过接口将它注入Spring IoC容器中来。下面对它们进行详细讲解。

2、演示项目搭建

  该演示项目开发环境为IDEA,Project名为“Spring Demo”。

  首先,创建名为“SpringDIWays”的Spring Module,具体过程如下:
在这里插入图片描述
  通过该种方式即可在IDEA中快速构建Spring开发环境。然后在lib文件夹中补充导入测试及日志jar包如下:
在这里插入图片描述
  在com.ccff.spring.di.ways.pojo包下创建名为“Student”的POJO类,具体代码如下所示:

package com.ccff.spring.di.ways.pojo;

public class Student {
    private String name;
    private String sex;
    private String studentNo;
    private Integer age;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", studentNo='" + studentNo + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getStudentNo() {
        return studentNo;
    }

    public void setStudentNo(String studentNo) {
        this.studentNo = studentNo;
    }

    public Integer getAge() {
        return age;
    }

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

  在com.ccff.spring.di.ways.test包下创建名为“StudentTest”的测试类,具体代码如下:

package com.ccff.spring.di.ways.test;

import com.ccff.spring.di.ways.pojo.Student;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class StudentTest {
    private ApplicationContext context;

    @Before
    public void getContainer(){
        context = new ClassPathXmlApplicationContext("spring-config.xml");
    }
}

3、构造器注入

  构造器注入依赖于构造方法实现,而构造方法可以是有参数的或者是无参数的。在大部分的情况下,我们都是通过类的构造方法来创建类对象,Spring也可以采用反射的方式,通过构造方法来完成注入,这就是构造器注入的原理。

  为了让Spring完成对应的构造注入,我们有必要去描述具体的类、构造方法并设置对应的参数,这样Spring就会通过对应的信息用反射的形式来创建对象。

  根据常见的场景,将分为如下几种情况进行测试:

3.1 只有无参构造方法(显式无参构造方法或隐士构造方法)。

  初始的Student类中并没有显式地给出构造方法,因此该类具有默认的无参构造方法。在spring-config.xml进行如下配置,实现采用无参构造器注入Student 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="student_noneParam_construction" class="com.ccff.spring.di.ways.pojo.Student">
    </bean>
</beans>

  在StudentTest测试类中添加名为“TestDIByConstruction”的测试方法,用来测试通过无参构造方法是否注入bean成功,具体代码如下:

package com.ccff.spring.di.ways.test;

import com.ccff.spring.di.ways.pojo.Student;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class StudentTest {
    private ApplicationContext context;

    @Before
    public void getContainer(){
        context = new ClassPathXmlApplicationContext("spring-config.xml");
    }

    @Test
    public void TestDIByConstruction(){
        Student student_noneParam_construction = (Student) context.getBean("student_noneParam_construction");
        System.out.println("通过无参构造器创建的Student Bean对象:");
        System.out.println(student_noneParam_construction);
    }

  运行该测试是方法后,查看输出到控制台的日志信息如下,则说明Student类的bean对象构建成功。只不过此时的该bean对象的属性值均为null。
在这里插入图片描述

3.2 只有有参构造方法。

  修改Student类,增加一个有参构造方法,具体代码如下:

public Student(String name, String sex, String studentNo, Integer age) {
    this.name = name;
    this.sex = sex;
    this.studentNo = studentNo;
    this.age = age;
}

  这里值得注意的是:当类中显式地具有有参构造方法时,若无显式地具有无参构造方法,则此时没有办法利用无参构造方法去创建对象的。

  若想要创建对象,则修改spring-config.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="student_Param_construction" class="com.ccff.spring.di.ways.pojo.Student">
        <constructor-arg index="0" value="学生1" />
        <constructor-arg index="1" value="" />
        <constructor-arg index="2" value="S001" />
        <constructor-arg index="3" value="24" />
    </bean>
</beans>

  通过上面的配置可以看出,可以使用bean标签中的constructor-arg子标签来将有参构造方法中的形参配置到bean中。上面的配置中采用了index-value的形式,其中index中配置的为有参构造方法中形参的序号(从0开始),value中配置的是该形参的值。可以看出,无论形参是什么类型,在value中均以String的形式进行配置。

  在constructor-arg子标签中还可以使用name-value的形式进行配置,其中name中配置的是该形参的参数名,value仍为该形参的值(String形式)。该种配置方法如下所示:

<?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="student_Param_construction" class="com.ccff.spring.di.ways.pojo.Student">
        <constructor-arg name="name" value="学生1" />
        <constructor-arg name="sex" value="" />
        <constructor-arg name="studentNo" value="S001" />
        <constructor-arg name="age" value="24" />
    </bean>
</beans>

  最后修改StudentTest测试类中的TestDIByConstruction方法,具体代码如下:

package com.ccff.spring.di.ways.test;

import com.ccff.spring.di.ways.pojo.Student;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class StudentTest {
    private ApplicationContext context;

    @Before
    public void getContainer(){
        context = new ClassPathXmlApplicationContext("spring-config.xml");
    }

    @Test
    public void TestDIByConstruction(){      
        System.out.println(student_noneParam_construction);
		Student student_Param_construction = (Student) context.getBean("student_Param_construction");
        System.out.println("通过有参构造器创建的Student Bean对象:");
        System.out.println(student_Param_construction);
    }

  运行该测试是方法后,查看输出到控制台的日志信息如下,则说明Student类的bean对象构建成功。
在这里插入图片描述

3.3 只有有参构造方法,且有参构造方法有多个。

  在3.2小节的Student类基础上,再增加一个构造方法,此时Student类中具有两个有参构造方法,且参数个数不同。具体代码如下:

public Student(String name, String sex, String studentNo, Integer age) {
    this.name = name;
    this.sex = sex;
    this.studentNo = studentNo;
    this.age = age;
}

public Student(String name, String studentNo) {
    this.name = name;
    this.studentNo = studentNo;
}

  此时在spring-config配置文件中配置Student类的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="student_Param_construction" class="com.ccff.spring.di.ways.pojo.Student">
        <constructor-arg name="name" value="学生1" />
        <constructor-arg index="1" value="" />
        <constructor-arg index="2" value="S001" />
        <constructor-arg index="3" value="24" />
    </bean>
    
    <bean id="student_Param_construction2" class="com.ccff.spring.di.ways.pojo.Student">
        <constructor-arg name="name" value="学生1" />
        <constructor-arg name="studentNo" value="S001" />
    </bean>
</beans>

  然后修改测试类中的TestDIByConstruction方法如下:

package com.ccff.spring.di.ways.test;

import com.ccff.spring.di.ways.pojo.Student;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class StudentTest {
    private ApplicationContext context;

    @Before
    public void getContainer(){
        context = new ClassPathXmlApplicationContext("spring-config.xml");
    }

    @Test
    public void TestDIByConstruction(){
        Student student_Param_construction = (Student) context.getBean("student_Param_construction");
        System.out.println("通过第一个有参构造器创建的Student Bean对象:");
        System.out.println(student_Param_construction);

        Student student_Param_construction2 = (Student) context.getBean("student_Param_construction2");
        System.out.println("通过第二个有参构造器创建的Student Bean对象:");
        System.out.println(student_Param_construction2);
    }
}

  运行该测试是方法后,查看输出到控制台的日志信息如下,则说明Student类的bean对象构建成功。
在这里插入图片描述
  注意:如果有多个构造方法,每个构造方法只有参数的顺序不同,那通过构造方法注入多个参数会注入到第一个出现的构造方法中,即哪个构造方法在前就注入哪一个,这种情况下就与构造方法顺序有关。

3.4 无参构造方法与有参构造方法并存。

  在3.3小节的Student类基础上显式地增加Student类的无参构造方法,使得Student类的无参构造方法与有参构造方法显式并存。在此种情况下,即可通过无参构造方法创建对象,也可以通过有参构造方法创建对象,仅是根据在spring-config中对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="student_Param_construction" class="com.ccff.spring.di.ways.pojo.Student">
        <constructor-arg name="name" value="学生1" />
        <constructor-arg index="1" value="" />
        <constructor-arg index="2" value="S001" />
        <constructor-arg index="3" value="24" />
    </bean>
    <bean id="student_Param_construction2" class="com.ccff.spring.di.ways.pojo.Student">
        <constructor-arg name="name" value="学生1" />
        <constructor-arg name="studentNo" value="S001" />
    </bean>

    <!--采用无参构造器注入-->
    <bean id="student_noneParam_construction" class="com.ccff.spring.di.ways.pojo.Student">
    </bean>
</beans>

  修改StudentTest测试类中的TestDIByConstruction测试方法如下:

package com.ccff.spring.di.ways.test;

import com.ccff.spring.di.ways.pojo.Student;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class StudentTest {
    private ApplicationContext context;

    @Before
    public void getContainer(){
        context = new ClassPathXmlApplicationContext("spring-config.xml");
    }

    @Test
    public void TestDIByConstruction(){
        Student student_noneParam_construction = (Student) context.getBean("student_noneParam_construction");
        System.out.println("通过无参构造器创建的Student Bean对象:");
        System.out.println(student_noneParam_construction);

        Student student_Param_construction = (Student) context.getBean("student_Param_construction");
        System.out.println("通过第一个有参构造器创建的Student Bean对象:");
        System.out.println(student_Param_construction);

        Student student_Param_construction2 = (Student) context.getBean("student_Param_construction2");
        System.out.println("通过第二个有参构造器创建的Student Bean对象:");
        System.out.println(student_Param_construction2);
    }
}

  运行该测试是方法后,查看输出到控制台的日志信息如下,则说明Student类的bean对象构建成功。
在这里插入图片描述

4、使用setter注入

  setter注入是Spring中最主流的注入方式,它利用Java Bean规范定义的setter方法来完成注入,灵活且可读性高。它消除了使用构造方法注入时出现多个参数的可能性,首先可以把构造方法声明为无参的,然后使用setter注入为其设置对应的值,其实也是通过Java反射技术得以实现的。

  首先,将此前在Student类中出现的构造方法全部去掉,具体代码为:

package com.ccff.spring.di.ways.pojo;

public class Student {
    private String name;
    private String sex;
    private String studentNo;
    private Integer age;
    
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", studentNo='" + studentNo + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getStudentNo() {
        return studentNo;
    }

    public void setStudentNo(String studentNo) {
        this.studentNo = studentNo;
    }

    public Integer getAge() {
        return age;
    }

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

  然后,在spring-config配置文件中使用setter方法注入进行配置,具体配置如下:

<?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">

    <!--采用setter方法注入-->
    <bean id="student_setter" class="com.ccff.spring.di.ways.pojo.Student">
        <property name="name" value="学生2" />
        <property name="sex" value="" />
        <property name="studentNo" value="S002" />
        <property name="age" value="23" />
    </bean>

</beans>

  注意:在配置文件中name属性的值的首字母大写或小写,Spring均可以通过setter方法完成bean的注入。因为spring会将name值的每个单词首字母转换成大写,然后再在前面拼接上"set"构成一个方法名,然后去对应的类中查找该方法,通过反射调用,实现注入。

  接着,修改StudentTest测试类,在测试类中创建名为“TestDIBySetter”的测试方法,具体代码如下:

package com.ccff.spring.di.ways.test;

import com.ccff.spring.di.ways.pojo.Student;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class StudentTest {
    private ApplicationContext context;

    @Before
    public void getContainer(){
        context = new ClassPathXmlApplicationContext("spring-config.xml");
    } 

    @Test
    public void TestDIBySetter(){
        Student student = (Student) context.getBean("student_setter");
        System.out.println("通过setter方法创建的Student Bean对象:");
        System.out.println(student);
    }
}

  运行该测试是方法后,查看输出到控制台的日志信息如下,则说明Student类的bean对象构建成功。
在这里插入图片描述

5、接口注入

  有些时候资源并非来自于自生系统,而是来自于外界,比如数据库连接资源完全可以在Tomcat下配置,然后通过JNDI的形式去获取它,这样数据库连接资源是属于开发工程外的资源,这个时候我们可以采用接口注入的形式来获取它。

  由于该部分并不会在该篇文章中作为重点讲解,因此不再赘述,若日后再有涉及,再次介绍。

猜你喜欢

转载自blog.csdn.net/weixin_36378917/article/details/86489589