# Spring IoC 的 XML 配置

# 1. Spring 创建 Bean

Spring 允许你在一个(或多个)XML 配置文件中配置 Bean,对于 Spring IoC 容器,这个配置文件就是创建、管理 Bean 的依据。

一个 Spring 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">

</beans>

# 使用类自身的构造方法

每个 Bean 都必须提供一个唯一的名称或 id,以及一个完全限定的类名,用来让 Spring IoC 容器对其进行创建。Spring 通过类的构造方法来创建 Bean 对象。

<bean id="xxx" class="xxx.xxx.Xxx"></bean>

# 使用工厂类提供的工厂方法

<bean id="tom" class="工厂类" factory-method="工厂方法"></bean>

# 使用工厂对象提供的工厂方法

<!-- 声明创建工厂对象 -->
<bean id="factory" class="工厂类"/>

<bean id="tom" factory-bean="工厂对象id" factory-method="工厂方法"></bean>

# 2. Spring 装配简单类型属性

装配,即为 Bean 的属性进行『初始化』、『赋值』。

简单类型』是指:基本数据类型、基本数据类型包装类和字符串。

装配方式有两种:

  • 通过『构造方法』装配

  • 通过『setter』装配

# 构造方法装配

通过构造方法装配,即通过设置,要求 Spring 通过调用『有参构造方法』来创建对象。默认是调用无参构造方法

<bean> 元素内使用 constructor-arg 子元素,即可触发构造方法装配。

<bean id="..." class="...">
    <constructor-arg index="x" value="xx"/>
    ...
</bean>

index 属性表示构造函数形参『索引(从 0 开始)。如果参数的类型具有唯一性,即参数的类型互不相同,那么可以使用 type 属性,通过『参数类型』来指定构造方法和参数值。

为了简化配置,Spring 提供了一个名为 c 的 schema,来简化配置。

<beans xmlns="..."
       xmlns:xsi="..."
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="...">

    <bean id="..." class="..." c:_0="xxx" c:_1="xxx" ... />
</beans>

注意,这里出现的下划线 _,它是必须的,不要省略掉了。

使用这种简写方式,完全不用出现 costructor-arg 子元素,只需在 bean 元素中多增加几个『c:_索引="参数值"』这样的属性。

# setter 装配

通过 setter 装配,即设置 Spring 在(通过『无参』构造器)创建对象后,通过调用对象的属性的 setter 方法来为对象的属性赋值。在 bean 元素内使用 property 子元素,即可触发 setter 装配。

<bean id="..." class="...">
  <property name="xxx" value="xxx" />
  ...
</bean>

property 元素的 name 属性用于指定对象的属性名,value 属性用于指定要设置值。

为了简化配置,Spring 提供了一个名为 p 的 schema,来简化配置。

<beans xmlns="..."
       xmlns:xsi="..."
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="...">

    <bean id="tom" class="com.example.bean.Human" p:name="tom" p:age="20" />

    ...
</beans>

使用这种简写方式,完全不用出现 property 子元素,只需在 bean 元素中多增加几个『p:属性名="属性值"』这样的属性。

# 3. Spring 装配引用类型属性

对象的属性不一定都是简单类型,还有可能有『引用类型』,即对象间有 has-a 关系。为引用类型的属性赋值不同于基本类型的属性,不是使用 value,而是 ref

# 构造方法装配

<bean id="jerry" class="com.example.bean.Cat" p:name="jerry" p:age="2"/>

<bean id="tom" class="com.example.bean.Human">
    <constructor-arg index="0" value="tom"/>
    <constructor-arg index="1" value="20"/>
    <constructor-arg index="2" ref="jerry"/>
</bean>

构造方法装配的简写形式中,对于引用类型必须写成:c:_索引-ref="参数值"

<bean id="jerry" class="com.example.bean.BlackCat" c:_0="jerry" c:_1="2"/>
<bean id="tom" class="com.example.bean.Human" c:_0="tom" c:_1="20" c:_2-ref="jerry"/>

# setter 装配

<bean id="jerry" class="com.example.bean.Cat" p:name="jerry" p:age="2"/>

<bean id="tom" class="com.example.bean.Human">
    <property name="name" value="tom"/>
    <property name="age" value="20"/>
    <property name="cat" ref="jerry"/>
</bean>

属性赋值的简写形式中,对于引用类型必须写成:p:属性名-ref

<bean id="jerry" class="com.example.bean.Cat" p:name="jerry" p:age="2"/>
<bean id="tom" class="com.example.bean.Human" p:name="tom" p:age="20" p:cat-ref="jerry"/>

# 4. Spring 装配集合类型属性

更复杂的属性类型是集合类型属性:数组、List、Set、Map 。

Bean 的属性可能远不止基本类型这么简单,还有可能是基本类型的集合(List、Set 和 Map)。这种情况下,属性的赋值不再是 property - value 这种结构,而是 property - list - value 三层结构。

<bean id="..." class="...">

    <property name="数组属性名">
        <array>
            <value>xxx</value>
            ...
        </array>
    </property>

    <property name="List属性名">
        <list>
            <value>xxx</value>
            ...
        </list>
    </property>

    <property name="Set属性名">
        <set>
            <value>xxx</value>
            ...
        </set>
    </property>

    <property name="Map属性名">
        <map>
            <entry key="xxx" value="xx"/>
            ...
        </map>
    </property>

</bean>

如果集合是引用类型的集合,那么使用的子元素就从 value 改为 ref。map 使用的是 key-refvalue-ref

<list>
    <ref bean="myDataSource" />
    <ref bean="..." />
    <ref bean="..." />
</list>

<set>
    <ref bean="myDataSource" />
    <ref bean="..." />
    <ref bean="..." />
</set>

<map>
    <entry key ="a ref" value-ref="myDataSource"/>
    <entry key ="..." value-ref="..."/>
    <entry key ="..." value-ref="..."/>
</map>

# 5. 自动装配

尽管自动装配很强大,但是代价是降低了 Bean 配置的可读性。在实践中,建议仅在依赖关系不复杂的应用中使用。

当一个 Bean 需要访问另一个 Bean 时,你可以显示指定引用装配它。不过,Spring IoC 容器提供自动装配功能,只需要在 beanautowire 属性中指定自动装配模式就可以了。

装配模式 说明
no 默认值。不执行自动装配。你必须显示地装配所依赖对象
byName 以 Bean 的属性名为依据,装配一个与属性名同名的 Bean
byType 以 Bean 的属性类型为依据,装配一个与之同类型的 Bean
constructor 通过构造方法初始化 Bean 的属性,并依据参数的类型,装配一个与参数同类型的 Bean

# 6. 注解替代 XML 配置

在 XML 配置文件中加上 <context:component-scan base-package="Bean 所在的包路径"/> 即可开启 Spring 的『自动扫描』功能,这是使用注解替代 XML 配置的前提。

# 7. Bean 的作用域

默认情况下,Spring IoC 容器只会对一个 Bean 创建一个实例。即单例。Spring IoC 提供了 4 种『作用域』,它决定了 Spring IoC 是否闯将一个新的对象、何时创建一个新的对象。

常见有:

  • singleton(单例):默认值。在整个应用中,Spring 只为其生成一个 Bean 的实例。

  • prototype(原型):Spring 每次都会生成一个 Bean 的实例。

在 XML 配置文件中, 通过 bean 元素的 scope 属性进行设置。该属性取值:singleton | prototype | 其他 。

# 8. Spring IoC 配置数据库连接池

WARNING

由于 .xml 配置文件中不能出现 & 符号,因此 url 中需要的 & 符号,要使用它的 &amp; 编码形式来代替。

<!-- Druid 数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
  <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
  <property name="url" 
            value="jdbc:mysql://localhost:3306/scott?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false" />
  <property name="username" value="root"/>
  <property name="password" value="123456"/>
</bean>

<!-- HikariCP 数据库连接池 -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
  <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
  <property name="jdbcUrl" 
            value="jdbc:mysql://localhost:3306/scott?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false&amp;serverTimezone=UTC" /> <!-- 这里的 name 和 Druid、dbcp2 的 name 不一样 -->
  <property name="username" value="root" />
  <property name="password" value="123456" />
</bean>

这里有一个小细节,Druid 数据库连接池和 HikariCP 数据库连接池的数据库 URL 属性名并不一样,一个叫 url ,一个叫 jdbcUrl

# 9. property-placeholder

<context:property-placeholder location="classpath:jdbc.properties"/>

有时有些配置我们并不是直接『写死』在 .xml 配置文件中,而是写在 .properties 配置文件中,再让 Spring 从 .properties 配置文件中读取配置,进行利用。

Spring 通过 <context:property-placeholder> 元素提供了这个功能。

默认情况下,整个程序中无论有多少个 .xml Spring 配置文件,但是只能有一个 <context:property-placeholder ... />,此时需要开启它的 ignore-unresolvable 告知 Spring 当前的 <context:property-placeholder ... /> 并非是唯一的一个。

例如:

<context:property-placeholder location="classpath:jdbc.properties" ignore-unresolvable="true" />

<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
    <property name="driverClassName" value="${jdbc.driver-class-name}"/>
    <property name="jdbcUrl" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

这里有个小细节,jdbc.properties 中的属性的 key 不要和 DataSource 的 driverClassName、jdbcUrl、username、password 属性同名,否则会有问题:数据库连接池初始化不成功。

配置文件中的配置项按惯例都会有自定义的前缀,用以表明这个(这些)配置用于什么环境。

『完』