SpringIOC容器

本文章用的环境紧贴上一篇

各种类型的装配

前言

  1. 只保留spring-beans.xml的内容,所以将spring-beans1.xml的bean标签复制到spring-beans.xml中
  2. 当要装配的实体对象中还有实体对象时,可以用下面两种方法装配
<bean id="s2" class="top.leafii.springdemo.demo1helloworld.domain.Student">
        <property name="id" value="AA"></property>
        <property name="name">
            <value>mikutown</value>
        </property>
        <!--以上两种方式无差异-->
        <!--什么时候用以上的那种子标签呢?如果value里有很多很多很多文字就可以用这个方式了 增强代码的可读性-->
        <property name="stuNo" value="stu007"/>
<!--第一种方式:<property name="student" ref="s1"/>-->
<!--第二种方式:-->
        <property name="student">
            <bean class="top.leafii.springdemo.demo1helloworld.domain.Student">
                <property name="id" value="aa"/>
                <property name="name">
                    <value>yyss</value>
                </property>
                <!--以上两种方式无差异-->
                <!--什么时候用以上的那种子标签呢?如果value里有很多很多很多文字就可以用这个方式了 增强代码的可读性-->
                <property name="stuNo" value="stu009"/>
            </bean>
        </property>
    </bean>

  1. 如图所示,可以看到 context可以看到Student但是Student看不到context,IOC可以控制Student类的生命周期,目前的情况而言,两个对象分别是applicationContext和student,applicationContext可以控制student,这种控制称为正常控制,反之则不行

IOC(Inversion of Control): 反转控制(控制反转)

在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需要的资源,在这样的模式下开发人员往往需要知道在具体容器中特定资源的获取方式,增加了学习成本,同时降低了开发效率。

反转控制的思想完全颠覆了应用程序组件获取资源的传统方式:反转了资源的获取方向——改由容器主动的将资源推送给需要的组件,开发人员不需要知道容器是如何创建资源对象的,只需要提供接收资源的方式即可,极大的降低了学习成本,提高了开发的效率。这种行为也称为查找的被动形式。

DI(Dependency Injection):依赖注入

IOC 的另一种表述方式:即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器的资源注入。相对于 IOC 而言,这种表述更直接。

IOC 容器在 Spring 中的实现

  • 在通过 IOC 容器读取 Bean 的实例之前,需要先将 IOC 容器本身实例化。
  • Spring 提供了 IOC 容器的两种实现方式
  1. BeanFactory:IOC 容器的基本实现,是 Spring 内部的基础设施,是面向Spring 本身的,不是提供给开发人员使用的。
  2. ApplicationContext:BeanFactory 的子接口,提供了更多高级特性。面向Spring 的使用者,几乎所有场合都使用 ApplicationContext 而不是底层的BeanFactory。
  • ApplicationContext 的主要实现类
  1. ClassPathXmlApplicationContext:对应类路径下的 XML 格式的配置文件
  2. FileSystemXmlApplicationContext:对应文件系统中的 XML 格式的配置文件

依赖注入的方式

创建实体类Student

class Student{
    private int studentId;
    private String studentName;
    private int age;
    //构造getter,setter忽略
}

通过 bean 的 setXxx()方法赋值

<bean id="s1" class="com.helloworld.bean.Student">
        <!-- 使用property子元素为bean的属性赋值,实际是调用setter方法,若实体类没有setter方法,报错 -->
        <property name="studentId" value="1001"/>
        <property name="stuName" value="Tom2015"/>
        <property name="age" value="20"/>
</bean>

通过 bean 的构造器赋值

  • Spring 自动匹配合适的构造器
<bean id="s2" class="com.helloworld.bean.Student" >
    <constructor-arg value= "10010"/>
    <constructor-arg value= "Tom2015"/>
    <constructor-arg value= "20"/>
</bean >
  • 通过索引值指定参数位置
<bean id="s3" class="com.helloworld.bean.Student" >
    <constructor-arg value= "10010" index="0"/>
    <constructor-arg value= "Tom2015"  index="1"/>
    <constructor-arg value= "20"  index="2"/>
</bean >
  • 通过类型区分重载的构造器
<bean id="s3" class="com.helloworld.bean.Student" >
    <constructor-arg value= "10010" index="0" type="java.lang.Integer" />
    <constructor-arg value= "Tom2015"  index="1"  type="java.lang.String"/>
    <constructor-arg value= "20"  index="2"  type="java.lang.Integer" />
</bean >

p 名称空间

为了简化 XML 文件的配置,越来越多的 XML 文件采用属性而非子元素配置信息。Spring 从 2.5 版本开始引入了一个新的 p 命名空间,可以通过元素属性的方式配置 Bean 的属性。(记得导包,不然要报错)

使用 p 命名空间后,基于 XML 的配置方式将进一步简化。

<bean id="s4" class="com.helloworld.bean.Student"
p:studentId="2002" p:stuName="Jerry2016" p:age="18" />

使用p名称空间时,不可以使用property标签否则会报错

可注入的值

字面量

  • 可以使用字符串表示的值,可以通过 value 属性或 value 子节点的方式指定
  • 基本数据类型及其封装类、String 等类型都可以采取字面值注入的方式

null值

  • 设置property 属性的子节点为
<bean id="s5" class="com.helloworld.bean.Student">
        <property name="studentId" value="1001"/>
        <property name="stuName">
            <null/>
        </property>
        <property name="age" value="20"/>
</bean>
public class Person{
    private String name;
    private String address;
    private int age;
    //忽略get和set,构造方法
}

创建类Customer

public class Customer {
    private Person person;
    //忽略get和set
    public Customer(Person person) {
        this.person = person;
    }
}

内部 bean

当 bean 实例仅仅给一个特定的属性使用时,可以将其声明为内部 bean。内部bean 声明直接包含在元素里,不需要设置任何 id 或 name 属性,内部 bean 不能使用在任何其他地方。内部 bean 不能使用在任何其他地方。

<bean id="CustomerBean" class="com.common.Customer">
        <property name="person">
            <bean class="com.common.Person">
                <property name="name" value="hello" />
                <property name="address" value="address1" />
                <property name="age" value="28" />
            </bean>
        </property>
</bean>

外部bean

可以使用 ‘ref’ 属性来引用已经定义的bean对象。

<bean id="CustomerBean" class="com.common.Customer">
        <property name="person" ref="PersonBean" />
</bean>
 
<bean id="PersonBean" class="com.common.Person">
        <property name="name" value="hello" />
        <property name="address" value="address1" />
        <property name="age" value="28" />
</bean>

List注入

配置 java.util.List 类型的属性,需要指定标签,在标签里包含一些元素。这些标签可以通过指定简单的常量值,通过指定对其他 Bean 的引用。通过 指定内置 bean 定义。通过指定空元素。甚至可以内嵌其他集合。

配置 java.util.Set 需要使用标签,定义的方法与 List 一样。

配置数组需要标签,定义的方法与 List 一样。

创建类Customer

public class Customer {
    private List<String> list;
    private List<Person> persons;
}

创建类Person

public class Person{
    private String name;
    private String address;
    private int age;
    //忽略get和set,构造方法
}

注入list

<bean id="cc" class="com.spring.bean.Customer " >
    <property name= "list">
        <!-- 以字面量为值的 List 集合 -->
        <list>
            <value>张三</value>
            <value>李四</value>
        </list>
    </property>
    <property name= "persons">
        <list>
            <ref bean="p1"/>
            <ref bean="p2"/>
        </list>
    </property>
</bean>
<bean id="p1" class="com.common.Person">
        <property name="name" value="hello" />
        <property name="address" value="address1" />
        <property name="age" value="28" />
</bean>
<bean id="p2" class="com.common.Person">
        <property name="name" value="hello" />
        <property name="address" value="address1" />
        <property name="age" value="28" />
</bean>

list也可以使用array标签来装

Map注入

Java.util.Map 通过标签定义,标签里可以使用多个作为子标签。每个条目包含一个键和一个值。必须在标签里定义键。因为键和值的类型没有限制,所以可以自由地为它们指定元素。

可以将 Map 的键和值作为的属性定义:简单常量使用 key 和 value 来定义;bean 引用通过 key-ref 和 value-ref 属性定义。

创建类Customer

public class Customer {
    private Map<String,Person> map;
}

创建类Person

public class Person{
    private String name;
    private String address;
    private int age;
    //忽略get和set,构造方法
}

注入map

<bean id="cc" class="com.spring.bean.Customer " >
    <property name= "map">
        <map>
            <entry>
                <key>
                    <value>person1</value>
                </key>
                <ref bean="p1"/>
            </entry>
            <entry>
                <key>
                    <value>person2</value>
                </key>
                <ref bean="p2"/>
            </entry>
        </map>
    </property>
</bean>
<bean id="p1" class="com.common.Person">
        <property name="name" value="hello" />
        <property name="address" value="address1" />
        <property name="age" value="28" />
</bean>
<bean id="p2" class="com.common.Person">
        <property name="name" value="hello" />
        <property name="address" value="address1" />
        <property name="age" value="28" />
</bean>

通过容器获取 bean

通过id 值获取

//1.创建IOC容器对象
ApplicationContext iocContainer =new ClassPathXmlApplicationContext("配置文件路径");
//2.根据id值获取bean实例对象
Student student = (Student) iocContainer.getBean("配置文件的id");

通过类型获取 bean

  • 从 IOC 容器中获取 bean 时,除了通过 id 值获取,还可以通过 bean 的类型获取。但如果同一个类型的 bean 在 XML 文件中配置了多个,则获取时会抛出异常,所以同一个类型的 bean 在容器中必须是唯一的。否则就会报错:
//1.创建IOC容器对象
ApplicationContext iocContainer =new ClassPathXmlApplicationContext("配置文件路径");
//2.根据id值获取bean实例对象
Student student = (Student) iocContainer.getBean(Student.class);

通过类型和id获取 bean

使用另外一个重载的方法,同时指定 bean 的 id 值和类型。

//1.创建IOC容器对象
ApplicationContext iocContainer =new ClassPathXmlApplicationContext("配置文件路径");
//2.根据id值获取bean实例对象
Student student = (Student) iocContainer.getBean("id值",Student.class);

配置信息的继承

查看下面两个 Employee 的配置,其中 dept 属性是重复的:

<bean id="dept" class="com.parent.bean.Department">
    <property name="deptId" value="100"/>
    <property name="deptName" value="IT"/>
</bean>
<bean id="emp01" class="com.parent.bean.Employee">    
    <property name="empId" value="1001"/>
    <property name="empName" value="Tom"/>
    <property name="age" value="20"/>
    <!-- 重复的属性值 -->
    <property name="dept" ref="dept"/>
</bean>
<bean id="emp02" class="com.parent.bean.Employee">
    <property name="empId" value="1002"/>
    <property name="empName" value="Jerry"/>
    <property name="age" value="25"/>
    <!-- 重复的属性值 -->
    <property name="dept" ref="dept"/>
</bean>
<!-- amazing -->

配置信息的继承:

<bean id="dept" class="com.parent.bean.Department">
    <property name="deptId" value="100"/>
    <property name="deptName" value="IT"/>
</bean>
<bean id="emp01" class="com.parent.bean.Employee">    
    <property name="empId" value="1001"/>
    <property name="empName" value="Tom"/>
    <property name="age" value="20"/>
    <!-- 重复的属性值 -->
    <property name="dept" ref="dept"/>
</bean>
<!-- 以emp01作为父bean,继承后可以省略公共属性值的配置 -->
<bean id="emp02" parent="emp01">
    <property name="empId" value="1002"/>
    <property name="empName" value="Jerry"/>
    <property name="age" value="25"/>
</bean>

Spring 允许继承 bean 的配置,被继承的 bean 称为父 bean。继承这个父 bean 的 bean 称为子 bean 子 bean 从父 bean 中继承配置,包括 bean 的属性配置子 bean 也可以覆盖从父 bean 继承过来的配置。

父 bean 可以作为配置模板,也可以作为 bean 实例。若只想把父 bean 作为模板,可以设置的 abstract 属性为 true,这样 Spring 将不会实例化这个 bean。如果一个 bean 的 class 属性没有指定,则必须是抽象 bean,并不是元素里的所有属性都会被继承。比如:autowire,abstract 等。也可以忽略父 bean 的 class 属性,让子 bean 指定自己的类,而共享相同的属性配置。但此时 abstract 必须设为 true。

bean 之间的依赖

有的时候创建一个 bean 的时候需要保证另外一个 bean 也被创建,这时我们称前面的 bean 对后面的 bean 有依赖。例如:要求创建 Employee 对象的时候必须创建 Department。

这里需要注意的是依赖关系不等于引用关系,Employee 即使依赖 Department 也可以不引用它。

<bean id="emp03" class="com.parent.bean.Employee" depends-on="dept">
    <property name="empId" value="1003"/>
    <property name="empName" value="Kate"/>
    <property name="age" value="21"/>
</bean>