[109002] Spring IoC : Dependency Injection

本文是为学生讲课后整理的笔记,同道中人可以任意传播,唯独期望在 CxDN 上全文照抄时,注明原文出处

本节主要讲解在 Spring IoC 中的依赖注入(Dependency Injection)的两种实现方式。

由于英文水平有限,同时为保证原汁原味地表达Spring中关于依赖注入部分本来的含义,标题和部分内容采用 Spring Framework 官方文档中的英文原文。

另外,在测试代码中读取 Spring 配置文件时,尽量使用了完整的路径而未使用通配符。

0、Field and Proprey

设存在以下 Java Bean :

public class Person {

    private int id ;
    private String name ;

    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;
    }

}

在 Person 类中,以成员变量(Member Variable)形式存在的 id 和 name 属于 Person 类中的字段( Field )。字段(Field)本质上是一个变量,用于存储数值(可以是基本数据类型的数值、也可以是引用)。

在 Person 类中刻意声明了两种不同类型的字段(Field):

    private int id ;
    private String name ;

因为id是基本数据类型,因此该字段(Field)可以直接存储整数值。

name是 String 类型,属于引用类型,因此该字段(Field)用于存储字符串的引用。

注意:即使在为 String 类型的变量赋值时使用了字符串字面量,也不能改变String变量中存储的是引用的事实。

在 Java Bean 中,字段(Field)通常是私有的,因此需要提供setter和getter来访问相应的字段,比如在 Person 类中,就有以下方法用以访问 id 字段:

    public int getId() {
        return id;
    }

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

在这里,我们要通过getter或setter来强调属性(property)的概念。

对于 getter 来说,按照以下顺序进行变换即可得到属性(property):

  1. 将 getId 方法的方法名去除 get ,得到 Id (注意不改变任何字母的大小写)
  2. 将 第1步 中得到的字符串首字母变小写,得到 id (即得到属性名称)

同样对于 setter 来说,也可以按照以下顺序进行变换即可得到属性(property):

  1. 将 setId 方法的方法名去除 set ,得到 Id (注意不改变任何字母的大小写)
  2. 将 第1步 中得到的字符串首字母变小写,得到 id (即得到属性名称)

因为绝大多数的 Java Bean 中字段(field)名称 和 属性(property) 是相同的,因此很多人会误以为属性(property)就是字段(field)、字段(field)就是属性(property)。

为了进一步区分字段(field)和属性(property),我们将 Person 改成以下形式:

public class Person {

    private int personId ;
    private String personName ;

    public int getId() {
        return personId;
    }

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

    public String getName() {
        return personName;
    }

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

}

此时,成员变量 personId 和 personName 是 Person 类中的字段(field),用于存储数据。而 id 和 name 是 Person 类的属性(property),用于访问相应的字段。

因此在 JSP 中,可以通过以下形式的 EL 表达式中可以用以下形式来获取属性(property)值

    id : ${person.id}
    name : ${person.name}

本质上,是通过 getId 和 getName 来获取 personId 和 personName 字段的值。


接下来,我们提供一个 Java Bean 供后续使用:

package io.malajava.ioc.injection;

import java.util.Date;

public class Sinaean {

    private Integer id ;
    private String name ;
    private char gender ;
    private Date birthdate ;
    private boolean married ;

    public Sinaean() {
        super();
        System.out.println( "public Sinaean()" );
    }

    public Sinaean(Integer id, String name) {
        super();
        System.out.println( "public Sinaean(Integer,String) : " + id );
        this.id = id;
        this.name = name;
    }

    public Sinaean(Integer id, String name, char gender) {
        super();
        System.out.println( "public Sinaean(Integer,String,char) : " + id );
        this.id = id;
        this.name = name;
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "{" +
                "id : " + id +
                ", name : '" + name + '\'' +
                ", gender : " + gender +
                ", birthdate : [ " + birthdate +
                " ], married : " + married +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        System.out.println( "Sinaean # setId( Integer )" );
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        System.out.println( "Sinaean # setName( String )" );
        this.name = name;
    }

    public char getGender() {
        return gender;
    }

    public void setGender(char gender) {
        System.out.println( "Sinaean # setGender( char )" );
        this.gender = gender;
    }

    public Date getBirthdate() {
        return birthdate;
    }

    public void setBirthdate(Date birthdate) {
        System.out.println( "Sinaean # setBirthdate( java.util.Date )" );
        this.birthdate = birthdate;
    }

    public boolean isMarried() {
        return married;
    }

    public void setMarried(boolean married) {
        System.out.println( "Sinaean # setMarried( boolean )" );
        this.married = married;
    }

}

1、Setter-based dependency injection


1.1、使用 <property> 标签实现注入
  • Java Bean
    参见前面步骤中的 Sinaean 类
  • Configuration Metadata
    <!-- 使用 DateFactoryBean 来创建任意年月日对应的 Date 对象-->
    <bean id="birthdate" class="io.malajava.ioc.beancreation.DateFactoryBean">
        <property name="year" value="1996" />
        <property name="month" value="7" />
        <property name="date" value="4" />
    </bean>

    <bean id="xiaoyuer" class="io.malajava.ioc.injection.Sinaean" >
        <property name="id" value="1001" />
        <property name="name" value="小鱼儿" />
        <property name="gender" value="男" />
        <property name="married" value="false" />
        <property name="birthdate" ref="birthdate" />
    </bean>

    <bean id="huawuque" class="io.malajava.ioc.injection.Sinaean" >
        <property name="id" value="1002" />
        <property name="name" value="花无缺" />
        <property name="gender" value="男" />
        <property name="married" value="false" />
        <property name="birthdate">
            <bean class="io.malajava.ioc.beancreation.DateFactoryBean">
                <property name="year" value="1997" />
                <property name="month" value="7" />
                <property name="date" value="5" />
            </bean>
        </property>
    </bean>

</beans>

注: 设以上内容存在于类路径下的 io/malajava/ioc/injection/injection-setter-property.xml 文件中。

注意,在以上配置中使用 <property>标签可以为指定JavaBean的属性(property)注入属性值,其中:

  1. <property>标签的 name 属性用于指定 Java Bean 中属性的名称
  2. <property>标签的 value 属性用于指定 需要注入的属性值
  3. <property>标签的 ref 属性用于指定 引用其它的 bean

以下17种类型可以通过<property>标签的 value 属性直接指定字面值:

  1. Java语言中的 8 种基本数据类型
  2. Java语言中 8 种基本数据类型对应的包装类类型
  3. java.lang.String 类型

所有的引用类型(包括包装类类型和String类型)的属性,都可以使用 <property>标签的 ref 属性来应用已经存在于IoC容器中的其它的 bean (同种类型),比如:

    <!-- 使用 DateFactoryBean 来创建任意年月日对应的 Date 对象-->
    <bean id="birthdate" class="io.malajava.ioc.beancreation.DateFactoryBean">
        <property name="year" value="1996" />
        <property name="month" value="7" />
        <property name="date" value="4" />
    </bean>

    <bean id="xiaoyuer" class="io.malajava.ioc.injection.Sinaean" >
        <!-- 此处省略其它属性 -->
        <property name="birthdate" ref="birthdate" />
    </bean>

所有的引用类型(包括包装类类型和String类型)的属性,也都可以在<property>标签内部使用 <bean>标签来创建需要注入的bean,比如:

   <property name="birthdate">
       <bean class="io.malajava.ioc.beancreation.DateFactoryBean">
           <property name="year" value="1997" />
           <property name="month" value="7" />
           <property name="date" value="5" />
       </bean>
   </property>
  • Testing
public class SetterInjectionTest1 {

    public static void main(String[] args) {

        DateFormat df = new SimpleDateFormat( "yyyy-MM-dd" );

        // 指定 Configuration Metadata
        String configLocation = "classpath:io/malajava/ioc/injection/injection-setter-property.xml" ;

        // 创建 Spring IoC 容器
        ApplicationContext iocContainer = new ClassPathXmlApplicationContext( configLocation ) ;

        // 从容器中获取 bean
        Sinaean s = iocContainer.getBean( "xiaoyuer" , Sinaean.class );
        System.out.println( s ); // s.toString()
        System.out.println( df.format( s.getBirthdate() ) );

        Sinaean x = iocContainer.getBean( "huawuque" , Sinaean.class );
        System.out.println( x ); // x.toString()
        System.out.println( df.format( x.getBirthdate() ) );

    }

}
1.2、使用 p 命名空间 实现注入
  • Java Bean
    参见前面步骤中的 Sinaean 类
  • Configuration Metadata

从 Spring 3 开始,支持在XML配置文件中使用 p 命名空间。

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

    <!-- 使用 DateFactoryBean 来创建任意年月日对应的 Date 对象-->
    <bean id="birthdate"
              class="io.malajava.ioc.beancreation.DateFactoryBean"
              p:year="1996"
              p:month="7"
              p:date="4" />

    <bean id="huaan"
              class="io.malajava.ioc.injection.Sinaean"
              p:id="9527"
              p:name="华安"
              p:gender="男"
              p:married="true"
              p:birthdate-ref="birthdate" />

</beans>

注: 设以上内容存在于类路径下的 io/malajava/ioc/injection/injection-setter-p-namespace.xml 文件中。

使用 p 命名空间时需要注意:

  1. Java 语言中 8 种基本数据类型的属性可以直接在等号之后书写 字面值
  2. 8 种基本数据类型对应的包装类类型的属性可以直接在等号之后书写 字面值
  3. java.lang.String 类型的属性可以直接在等号之后书写 字面值
  4. 所有引用类型(包括包装类类型和String类型)的属性都可以使用 -ref 来引用其它 bean

另外,千万要注意,只有在 <beans> 标记中增加了以下内容才可以使用p命名空间:

    xmlns:p="http://www.springframework.org/schema/p"
  • Tesging
public class SetterInjectionTest2 {

    public static void main(String[] args) {

        DateFormat df = new SimpleDateFormat( "yyyy-MM-dd" );

        // 指定 Configuration Metadata
        String configLocation = "classpath:io/malajava/ioc/injection/injection-setter-p-namespace.xml" ;

        // 创建 Spring IoC 容器
        ApplicationContext iocContainer = new ClassPathXmlApplicationContext( configLocation ) ;

        // 从容器中获取 bean
        Sinaean s = iocContainer.getBean( "huaan" , Sinaean.class );
        System.out.println( s ); // s.toString()
        System.out.println( df.format( s.getBirthdate() ) );

    }

}

2、Constructor-based dependency injection


2.1、使用 <constructor-arg> 标签实现参数注入
  • Java Bean
    参见前面步骤中的 Sinaean 类
  • Configuration Metadata

设以下内容存在于类路径下的 io/malajava/ioc/injection/injection-constructor-arg.xml 文件中。

    <!-- Sinaean(Integer id, String name, char gender) -->
   <bean id="first" class="io.malajava.ioc.injection.Sinaean">
       <!-- 使用 constructor-arg 的 name 属性可以指定 构造方法中的 参数名称 -->
       <constructor-arg name="id" value="1001" />
       <constructor-arg name="gender" value="女" />
       <constructor-arg name="name" value="赵敏" />
   </bean>

    <!-- Sinaean(Integer id, String name)  -->
    <bean id="second" class="io.malajava.ioc.injection.Sinaean">
        <!-- 使用 constructor-arg 的 index 属性 指定 构造方法 中参数的索引-->
        <constructor-arg index="0" value="1002" />
        <constructor-arg index="1" value="韩小昭" />
    </bean>

    <!-- Sinaean(Integer id, String name ,char gender)  -->
    <bean id="third" class="io.malajava.ioc.injection.Sinaean">
        <!-- 使用 constructor-arg 的 type 属性 指定 构造方法 中参数的索类型-->
        <!-- 根据类型来确定 哪个值 应该放在传递给哪个参数 ( 仅限构造方法中没有两个以上相同类型参数 的情况 ) -->
        <constructor-arg type="java.lang.Integer" value="1003" />
        <constructor-arg type="java.lang.String" value="殷离" />
        <constructor-arg type="char" value="女" />
    </bean>

    <!-- Sinaean(Integer id, String name ,char gender)  -->
    <bean id="fourth" class="io.malajava.ioc.injection.Sinaean">
        <constructor-arg index="0" name="id" type="java.lang.Integer" value="1004" />
        <constructor-arg index="1" name="name" type="java.lang.String" value="周芷若" />
        <constructor-arg index="2" name="gender" type="char" value="女" />
    </bean>

对于以下类型的参数来说,可以直接使用 <constructor-arg 的 value 来指定参数值:

  1. Java 语言中 8 种基本数据类型
  2. 8 种基本数据类型对应的包装类类型
  3. java.lang.String 类型

所有引用类型(包括包装类类型和String类型),都可以通过 <constructor-arg 的 ref 来指定参数值(即引用其它的bean),比如:

    <!-- public Integer( int value ) -->
    <bean id="fourthId" class="java.lang.Integer">
        <constructor-arg type="int" value="1004" />
    </bean>

    <!-- public String( String source ) -->
    <bean id="fourthName" class="java.lang.String">
        <constructor-arg type="java.lang.String" value="周芷若" />
    </bean>

    <bean id="fourth" class="io.malajava.ioc.injection.Sinaean">
        <constructor-arg name="id" ref="fourthId" />
        <constructor-arg name="name" ref="fourthName" />
        <constructor-arg name="gender" value="女" />
    </bean>

所有引用类型(包括包装类类型和String类型),也都可以在 <constructor-arg 标签内部通过 <bean> 标签来创建相应对象后再通过构造注入:

    <!-- public Integer( int value ) -->
    <bean id="fourthId" class="java.lang.Integer">
        <constructor-arg type="int" value="1004" />
    </bean>

    <!-- public String( String source ) -->
    <bean id="fourthName" class="java.lang.String">
        <constructor-arg type="java.lang.String" value="周芷若" />
    </bean>

    <bean id="fourth" class="io.malajava.ioc.injection.Sinaean">
        <constructor-arg name="id">
            <bean class="java.lang.Integer">
                <constructor-arg type="int" value="1004" />
            </bean>
        </constructor-arg>
        <constructor-arg name="name">
            <bean class="java.lang.String">
                <constructor-arg type="java.lang.String" value="周芷若" />
            </bean>
        </constructor-arg>
        <constructor-arg name="gender" value="女" />
    </bean>

实际应用时可以根据实际情况,灵活应用。

  • Testing
public class ConstructorInjectionTest1 {

    public static void main(String[] args) {

        // 指定 Configuration Metadata
        String configLocation = "classpath:io/malajava/ioc/injection/injection-constructor-arg.xml" ;

        // 创建 Spring IoC 容器
        ApplicationContext iocContainer = new ClassPathXmlApplicationContext( configLocation ) ;

        Sinaean s = null ;

        // 从容器中获取 bean
        s = iocContainer.getBean( "first" , Sinaean.class );
        System.out.println( s ); // s.toString()

        s = iocContainer.getBean( "second" , Sinaean.class );
        System.out.println( s ); // s.toString()

        s = iocContainer.getBean( "third" , Sinaean.class );
        System.out.println( s ); // s.toString()

        s = iocContainer.getBean( "fourth" , Sinaean.class );
        System.out.println( s ); // s.toString()

    }

}
2.2、使用 c 命名空间 实现参数注入
  • Java Bean
    参见前面步骤中的 Sinaean 类
  • Configuration Metadata

在 Spring 配置文件中通过添加以下内容来使用 c 命名空间:

       xmlns:c="http://www.springframework.org/schema/c"

添加后的 <beans > 标签如下所示:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>

设以下内容存在于类路径下的 io/malajava/ioc/injection/injection-constructor-c.xml 文件中。

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

   <!-- 使用 c 命名空间,可以通过 c:_index 形式指定构造方法的参数值 -->
   <!-- 因为这里依次指定了 三 个参数,因此会调用 Sinaean(Integer,String,char) -->
   <bean id="first"
         class="io.malajava.ioc.injection.Sinaean"
         c:_0="2001"
         c:_1="令狐冲"
         c:_2="男" />



   <!-- 因为依次指定了 两 个参数,因此会调用 Sinaean(Integer,String) -->
   <bean id="second"
         class="io.malajava.ioc.injection.Sinaean"
         c:_0="2999"
         c:_1="东方不败" />

   <!-- 使用 c 命名空间,也可以通过 c:argumentName 来指定构造方法的参数值  -->
   <!-- 根据参数名称,这里调用的构造方法是 Sinaean( Integer id ,String name ,char gender )-->
   <bean id="third"
         class="io.malajava.ioc.injection.Sinaean"
         c:id="2002"
         c:name="仪琳"
         c:gender="女" />

   <!-- public Integer( int value ) -->
   <bean id="fourthId" class="java.lang.Integer">
       <constructor-arg type="int" value="1004" />
   </bean>

   <!-- public String( String source ) -->
   <bean id="fourthName" class="java.lang.String">
       <constructor-arg type="java.lang.String" value="周芷若" />
   </bean>

   <!-- 对于任意引用类型(包括包装类和String)的参数来说,都可以通过 -ref 形式来引用其它 bean -->
   <bean id="fourth"
         class="io.malajava.ioc.injection.Sinaean"
         c:id-ref="fourthId"
         c:name-ref="fourthName"
         c:gender="女" />

</beans>
  • Testing
public class ConstructorInjectionTest2 {

    public static void main(String[] args) {

        // 指定 Configuration Metadata
        String configLocation = "classpath:io/malajava/ioc/injection/injection-constructor-c.xml" ;

        // 创建 Spring IoC 容器
        ApplicationContext iocContainer = new ClassPathXmlApplicationContext( configLocation ) ;

        Sinaean s = null ;

        // 从容器中获取 bean
        s = iocContainer.getBean( "first" , Sinaean.class );
        System.out.println( s ); // s.toString()

        s = iocContainer.getBean( "second" , Sinaean.class );
        System.out.println( s ); // s.toString()

        s = iocContainer.getBean( "third" , Sinaean.class );
        System.out.println( s ); // s.toString()

        s = iocContainer.getBean( "fourth" , Sinaean.class );
        System.out.println( s ); // s.toString()

    }

}

转载于:https://my.oschina.net/malajava/blog/2967639

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值