Spring 配置使用 - 装配 Bean

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring 配置使用 - 装配 Bean相关的知识,希望对你有一定的参考价值。

Bean 声明配置

Spring 利用 Ioc 容器管理 Bean,前提是 Bean 需要在配置文件声明(定义)。

声明一个 Bean 需要的基本元素如下:

元素 作用
class 全限定类名,即 Bean 的完整包名,是必不可少的元素。
id 标识,在整个 Ioc 容器中必须唯一,用来区分不同的 Bean。若 Bean 未指定 id,则 Ioc 容器会为其自动生成一个标识;只能配置一个。
name 标识符或名称, 作用与别名一致;在整个 Ioc 容器汇总必须唯一,但是可以配置多个。
alias 别名,在整个 Ioc 容器汇总必须唯一,但是可以配置多个。

声明一个 Bean 的配置如下:

<!-- 1.只配置必须的全限定类名(class),由Ioc 容器为其生成一个标识 -->
<bean class="com.demo.Animals"/>

<!-- 2.指定 id,该标识在 Ioc 容器中必须唯一 -->
<bean id="animals" class="com.demo.Animals"/>

<!-- 3.指定 name,该标识在 Ioc 容器中必须唯一,同一个 Bean 中 name 可以相同 -->
<!--   可配置一个或多个,第一个会被当作标识,其余的被当作别名 -->
<bean name="animals,Animals" class="com.demo.Animals"/>

<!-- 4.同时指定 id 和 name -->
<bean id="animals" name="animals" class="com.demo.Animals"/>

<!-- 5.同时指定 id 和 alias -->
<!--   alias 在 Bean 中不能单独存在,必须指定 id 或 name -->
<!--   alias 可配置一个或多个,标识在整个 Ioc 容器唯一,在 Bean 中可以 相同 -->
<!--   alias 元素的 name 表示 Bean 的 id 或 name -->
<bean id="animals"  class="com.demo.Animals"/>
<alias name="animals" alias="a"/>
<alias name="animals" alias="b"/>

<!-- 6.同时指定 name 和 alias -->
<bean name="animals"  class="com.demo.Animals"/>
<alias name="animals" alias="a"/>
<alias name="animals" alias="a"/>

注意事项:

  • id 在同一个 Bean 中只能指定一个,name 和 alias 可以指定多个

  • 同时存在 id 和 name 时,id 作为标识,name 会被当作别名

  • 存在多个 name 时,第一个 name 会被当作 标识,其余的 name 会被当作别名;

  • 多个 name 或 alias 之间可以用分号(;)、空格、逗号(,)隔开。


Bean 调用方式

在 Spring Ioc 容器声明了 Bean 之后,就表示将 Bean 交给 Spring 去管理,由 Spring 负责 Bean 的整个生命周期,包括 Bean 的创建、销毁等等。

这就是所谓的控制反转(Ioc),将 Bean 的控制权交给 Spring,我们无需常见它,只需调用它即可。

通过 Spring Ioc 容器调用 Bean 的方式有三种:根据类型、根据名称、根据名称+类型。

注意事项:

  • 若 Ioc 容器中存在多个同一类型的 Bean 时,根据类型取得 Bean 时会抛出 NoUniqueBeanDefinitionException 异常

  • 若找不到指定名称或类型的 Bean时,会抛出 NoSuchBeanDefinitionException 异常

// 配置文件路径
String path = "/WebRoot/WEB-INF/spring-bean.xml";

// 取得 Spring 的 Ioc 容器(也称应用上下文)
ApplicationContext context = new FileSystemXmlApplicationContext(path);

// 由 Ioc 容器获取定义的 Bean:

// ①根据 Bean 的类型(容器存在多个相同类型 Bean,抛出异常)
Animals animal = (Animals) context.getBean(Animals.class);

// ②根据 Bean 的名称Animals animal = (Animals) context.getBean("animals");

// ③根据 Bean 的类型和名称
Animals animal = (Animals) context.getBean("Animals",Animals.class);

观察上面的代码,发现在没有创建的 Bean(即创建实例,如 new)情况下,实现了对 Bean 的调用。

实际上是在创建 Ioc 容器时,它已经利用反射通过 Bean 的无参构造函数完成了 Bean 的实例化,所以才可以直接调用。


Bean 注入方式

Bean 的注入,也称依赖注入,其实它包含了两个部分:依赖和注入。

  • 依赖:指得是类与类之间的关系,也可以理解为 Bean 与 Bean 之间的联系。它也可以是参数与类之间的关系。
// ① 对于类 Animals 来说,此时依赖就是它的构造参数:name。
public class Animals {

    public Animals(String name){
        //do something...
    }
}

// ② 对于类 Animals 来说,此时依赖是它的成员变量:Cat 类。
public class Animals {

    private Cat cat;

    //省略 sertter/getter...
}

// ③ 对于类 Animals 来说,此时没有依赖关系,也就没所谓的注入。
public class Animals {
  • 注入:其实就是实例化的过程,比如上面的例子,我们可以指定 name = “dog”,然后起将其赋值给 Animals 的构造参数,这个过程就是注入。

在 Spring 中注入方式有四种:构造器注入、setter 注入、静态工厂注入、实例工厂注入。


1.构造器注入

首先来看在未使用 Spring 之前是如何通过构造函数实例化一个类。

public class Animals {

    public Animals(String name){
        //...
    }

    public static void main(String [ ] args) {
        String name ="dog";
        Animals a = new Animals(name);
    }
}

通过 Spring 的构造器注入构造参数并实例化它

  • 在配置文件中定义 Bean
<!-- <constructor-arg> 标签代表:构造函数的参数,共有 3 种方式可以指定参数值 -->

<!-- ①通过参数名称 -->
<bean id="animals" class="com.demo.Animals" >
    <constructor-arg name="name" value="dog"/>
</bean>

<!-- ②通过参数序列号 -->
<bean id="animals" class="com.demo.Animals" >
    <constructor-arg index="0" value="dog"/>
</bean>

<!-- ③通过参数类型 -->
<bean id="animals" class="com.demo.Animals" >
    <constructor-arg type="java.lang.String" value="dog"/>
</bean>
  • 通过 Ioc 容器实例化
String path = "/WebRoot/WEB-INF/spring-bean.xml";   
ApplicationContext context = new FileSystemXmlApplicationContext(path);

2.setter 注入

首先来看在未使用 Spring 之前是如何通过 setter 方法指定成员变量并调用它

public class Animals {
    private String name;

    public String getName() {
        return name;
    }

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

    public static void main(String [ ] args) {
        Animals a = new Animals();
        a.setName("dog");
        System.out.println(a.getName());
    }
}

通过 Spring 的 setter 注入指定成员变量的值

  • 在配置文件中定义 Bean
<!-- <property> 标签代表 Bean 的成员变量  -->
<bean id="animals" class="com.demo.Animals" >
    <property name="name" value="dog"/>
</bean>
  • 通过 Ioc 容器调用
ApplicationContext context = ...
Animals a = (Animals) context.getBean(Animals.class);
System.out.println(a.getName());

3.静态工厂注入

静态工厂指的是简单工厂模式,因为其创建实例的方法是静态方法,因此也称静态工厂模式。

下面来看如何通过静态工厂模式创建一个类。

// 产品类:Animals 
public class Animals {
    public Animals(String name) {
        System.out.println("Creating Animal,Name is "+name);
    }
}

// 静态工厂类:AnimalsFactory 
public class AnimalsFactory {

    public static Animals newInstance() {
        return new Animals();
    }

    public static void main(String [ ] args) {
        Animals a = AnimalsFactory .newInstance();
    }
}

通过 Spring 的 静态工厂注入创建具体的产品类

  • 在配置文件中定义 Bean
<!-- factory-method 代码该 Bean 的静态工厂方法  -->
<!-- constructor-arg 表示通过静态工厂方法传递给产品类的构造参数  -->

<bean id ="animalsFactory" class="com.demo.AnimalsFactory" factory-method="newInstance">
    <constructor-arg name="name" value="dog"/>      
</bean>
  • 通过 Ioc 容器调用
// 此时已经完成 Animals 类的实例化
ApplicationContext context = ...

// 调用,注意 -> getBean 的参数
Animals a = context.getBean("animalsFactory",Animals.class);

4.实例工厂注入

同样是上面的例子,这里修改下工厂类。利用实例工厂类创建产品类。

public class AnimalsFactory {

    public Animals newInstance(String name) {
        return new Animals(name);
    }

    public static void main(String [ ] args) {
        AnimalsFactory af = new AnimalsFactory();
        Animals a = af.newInstance("dog");
    }
}

通过 Spring 的 实例工厂注入创建具体的产品类。

<bean id="animalsFactory" class="com.demo.AnimalsFactory"/>

<bean factory-bean="animalsFactory" factory-method="newInstance">
    <constructor-arg name="name" value="dog"/>
</bean>

Bean 注入类型

1.注入基本数据类型

上面介绍 Bean 注入方式时采用例子就是注入基本数据类型。

<bean id="animals"  class="com.demo.Animals" >
    <property name="name" value="dog"/>
</bean>

注意:

  • property 标签当中的 value,全部都是字符串类型,,由 Spring 容器将此字符串转换成属性所需要的类型,如果转换出错,将抛出相应的异常。

  • 若 value 值是 boolean 类型,Spring 对其有容错处理,如用常见的(true/false)外,还可以使用(1/0)、(on/off)、(yes/no)表示。

  • 若 value 值是为空,则利用 null 标签表示,如:

<bean id="animals" class="com.demo.Animals" >
    <property  name="name">
        <null/>
    <property >
</bean>

2.注入集合类型

这里以 setter 注入为例,定义 Bean

public class Animals {

    // 成员变量分别为 List,Set,Array 类型时
    private List [Set] [Array] list;

    //省略 setter/getter...

}

在 xml 文件中配置 Bean

<!-- ①成员变量的类型为 List  -->
<bean id="animals" class="com.demo.Animals">
    <property name="list">
        <list>
            <value>a</value>
            <value>b</value>
        </list>
    </property>
</bean>

<!-- ②若成员变量的类型为 Set -->
<bean id="animals" class="com.demo.Animals">
    <property name="list">
        <set>
            <value>a</value>
            <value>b</value>
        </set>
    </property>
</bean>

<!-- ③若成员变量的类型为 Array -->
<bean id="animals" class="com.demo.Animals">
    <property name="list">
        <array>
            <value>a</value>
            <value>b</value>
        </array>
    </property>
</bean>

3.注入字典类型

这里以构造器注入为例,定义 Bean

public class Animals {
    public Animals(Map[Properties] map[prop]){
        //do something...
    }
}

在 xml 文件中配置 Bean

<!-- ①构造参数类型为 Map -->
<bean id="animals" class="com.demo.Animals" >
    <constructor-arg name="map">
        <map>
            <entry key="a" value="1"/>
            <entry key="b" value="2"/>
        </map>
    </constructor-arg>
</bean>

<!-- ①构造参数类型为 Properties -->
<bean id="animals" class="com.demo.Animals">
    <constructor-arg name="prop">
        <props>
            <prop key="a" >1</prop>
            <prop key="b" >2</prop>
        </props>
    </constructor-arg>
</bean>

4.引用其他 Bean

引用其他Bean的步骤与注入基本数据类型的步骤一样,可以通过构造器注入及setter注入引用其他Bean,只是引用其他Bean的注入配置稍微变化了一下。

  • 定义 Bean
public class Cat{public class Animals {
    private Cat cat;
    //省略 setter/getter...

    public Animals(){ 
    }
    public Animals(Cat cat){ 
    }
}
  • 在 XML 文件中配置 Bean
<!-- 被引用的 Bean  -->
<bean id="cat" class="com.demo.Cat"></bean>

<!-- ①通过 setter 注入 -->
<bean id="animals" class="com.demo.Animals">
    <property name="cat" ref="cat"/>
</bean>

<!-- ②通过构造器注入 -->
<bean id="animals" class="com.demo.Animals">
    <constructor-arg name="cat" ref="cat"/>
</bean>

5.注入内部定义 Bean

以上面的例子为例,内部定义 Bean 只需稍微修改下配置文件。

<!-- ①通过 setter 注入 -->
<bean id="animals" class="com.demo.Animals">
    <property name="cat" >
        <bean class="com.demo.Cat" />
    </property>
</bean>

<!-- ②通过构造器注入 -->
<bean id="animals" class="com.demo.Animals">
    <constructor-arg name="cat" >
        <bean class="com.demo.Cat" />
    </constructor-arg>
</bean>

注意:

  • 内部 Bean 对外不可见,即不能通过 Ioc 容器取得内部 Bean

Bean 初始化和销毁

当实例化一个 Bean 时,可能需要执行一个初始化操作来确保该 Bean 可用状态。同样地,当不需要 Bean 时,将其从容器中移除时,可能还需要按顺序执行一些清楚工作。

为 Bean 定义初始化和销毁操作,需要使用 init-method 和 destory-method 属性。

  • 定义 Bean
// 进入房间后要开灯,离开房间后要关灯
public class Room {

    public Room(){
        System.out.println("enter room...");
    }

    public void turnOnLights(){
        System.out.println("turn on...");
    }

    public void turnOffLights(){
        System.out.println("turn off...");
    }
}
  • 在 Xml 文件中配置
<bean class="com.demo.Room"  init-method="turnOnLights" destroy-method="turnOffLights"/>
  • 在 Ioc 容器中实例化该 Bean,在销毁它
// 实例化 Bean
FileSystemXmlApplicationContext context = ...

// 销毁 Bean
context.registerShutdownHook();  

// 输出内容:
// enter room...
// turn on...
// turn off...

以上是关于Spring 配置使用 - 装配 Bean的主要内容,如果未能解决你的问题,请参考以下文章

Spring学习系列 通过Java代码装配Bean

Spring实战读书笔记Spring装配Bean

Spring实战读书笔记Spring装配Bean

《spring实战》学习笔记-第二章:装配bean

Spring5 Java代码装配Bean

spring:按照Bean的名称自动装配User