一、前言
在上一篇文章中,我们探讨了Spring框架中的构造注入和set注入。现在,我们来进一步探讨Spring框架中依赖注入的基础用法。
二、内容
2.1 注入内部Bean
什么是注入内部bean?
实际上,我们在上一篇文章中记录的就是注入外部bean,我们再来回顾一下。
来看下面这个配置文件:
xml
<bean id="userDaoBean" class="com.example.dao.UserDao"/>
<bean id="userServiceBean" class="com.example.service.UserService">
<property name="userDao" ref="userDaoBean"/>
</bean>
外部Bean的特点就是将bean
定义到外边,然后在property
标签中使用ref
属性进行注入。通常这种方式是常用。
那相对应的,就是内部bean。来看下面这个例子:
xml
<bean id="userServiceBean" class="com.example.service.UserService">
<property name="userDao">
<bean class="com.example.dao.UserDao"/>
</property>
</bean>
在这个示例中,userServiceBean
包含一个名为 userDao
的属性,而这个属性的值是一个内部的 UserDao
Bean。这样,你可以在 userServiceBean
中直接定义和配置 UserDao
,而不必在外部先定义它。
内部Bean 的特点就是在一个bean
标签内部property
标签中,再嵌套一个bean标签,这样也是可以的(了解即可)。
小结一下,在Spring框架中,注入内部Bean是一种方式,它允许你在一个bean的配置中直接嵌套定义另一个bean,而不必在外部先定义然后再引用。这通常在某些情况下非常方便,例如,如果你只需要在一个特定的地方使用该内部bean,而不需要在整个应用程序中共享它。
2.2 注入简单类型
(1)如何注入
我们之前在进行注入的时候,对象的属性是另一个对象。那如果对象的属性是int类型呢?同样可以使用 set 注入的方式来赋值。
当我们需要给一个对象的属性赋予基本数据类型的值时,就应该使用<value>
元素或 value
属性,而不是<ref>
。
这是因为基本数据类型不能作为引用传递,而是直接赋予值。
我们来看一个简单的例子:
首先定义一个User
类,提供一个名为age
的属性,并为该属性提供一个setter方法:
java
package com.example.beans;
public class User {
private int age;
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"age=" + age +
'}';
}
}
接下来,编写Spring配置文件,通过<bean>
元素为User
对象的 age
属性赋值:
xml
<bean id="userBean" class="com.example.beans.User">
<!-- 为int类型属性赋值,使用value属性或者value标签 -->
<property name="age" value="20"/>
</bean>
在这个配置文件中,我们使用了value
属性来为age
属性赋值为20。我们还可以选择使用<value>
标签,如下所示:
xml
<bean id="userBean" class="com.example.beans.User">
<property name="age">
<value>20</value>
</property>
</bean>
需要特别注意的是,当你需要给简单类型的属性赋值时,使用value
属性或value
标签,而不要使用ref
属性,因为ref
通常用于引用其他bean对象,而不是为简单类型的属性赋值。
(2)简单类型包括哪些?
前面讲了,简单类型在配置文件中无法使用普通的对象引用方式进行注入,而是通过使用value
属性或value
标签来进行注入赋值。
那么,什么被 spring 认为是简单类型呢?
我们来看一下源码:
java
public static boolean isSimpleProperty(Class<?> type) {
Assert.notNull(type, "'type' must not be null");
return isSimpleValueType(type) || type.isArray() && isSimpleValueType(type.getComponentType());
}
public static boolean isSimpleValueType(Class<?> type) {
return Void.class != type && Void.TYPE != type &&
(ClassUtils.isPrimitiveOrWrapper(type) ||
Enum.class.isAssignableFrom(type) ||
CharSequence.class.isAssignableFrom(type) ||
Number.class.isAssignableFrom(type) ||
Date.class.isAssignableFrom(type) ||
Temporal.class.isAssignableFrom(type) ||
URI.class == type ||
URL.class == type ||
Locale.class == type ||
Class.class == type);
}
**通过源码分析得知,简单类型包括:**基本数据类型、基本数据类型对应的包装类、String或其他的CharSequence子类、Number子类、Date子类、Enum子类、URI、URL、Temporal子类、Locale、Class、包括以上简单值类型对应的数组类型。
1. 基本数据类型和对应的包装类:
- 基本数据类型包括int、float、double、boolean等,它们不是对象,而是直接存储在栈上的值。
- 对应的包装类包括Integer、Float、Double、Boolean等,它们是对象版本的基本数据类型。
在Spring中,你可以使用<property>
元素的 value
属性来为这些属性赋值,如下所示:
xml
<bean id="exampleBean" class="com.example.ExampleBean">
<property name="intValue" value="42" />
<property name="booleanValue" value="true" />
</bean>
2. 字符串类型(CharSequence子类):
Spring也支持将字符串类型的值赋予属性,包括String、StringBuilder、StringBuffer等。
xml
<bean id="exampleBean" class="com.example.ExampleBean">
<property name="stringValue" value="Hello, Spring!" />
</bean>
3. 数字类型(Number子类):
Spring可以处理数字类型的属性,如整数、浮点数等,包括Byte、Short、Integer、Long、Float、Double等。
xml
<bean id="exampleBean" class="com.example.ExampleBean">
<property name="doubleValue" value="3.14" />
</bean>
4. 枚举类型(Enum子类):
如果你的属性是枚举类型,Spring可以正确地处理它。
xml
<bean id="exampleBean" class="com.example.ExampleBean">
<property name="dayOfWeek" value="MONDAY" />
</bean>
5. 日期类型(Date子类和Temporal子类):
Spring支持Date和Temporal(如LocalDate、LocalDateTime等)类型的属性。
xml
<!--注意:value后面的日期字符串格式不能随便写,必须是Date对象toString()方法执行的结果。-->
<bean id="exampleBean" class="com.example.ExampleBean">
<property name="dateValue" value="Sun Nov 05 15:44:09 CST 2023" />
</bean>
6. URI和URL类型:
如果你的属性是URI或URL类型,你可以像下面这样为它们赋值:
xml
<!--spring6之后,会自动检查url是否有效,如果无效会报错 -->
<bean id="exampleBean" class="com.example.ExampleBean">
<property name="uriValue" value="http://www.example.com" />
</bean>
7. 语言环境类型(Locale):
Locale是用于处理国际化的一种数据类型,也可以在Spring中赋值。
xml
<!--java.util.Locale 主要在软件的本地化时使用。它本身没有什么功能,更多的是作为一个参数辅助其他方法完成输出的本地化。-->
<bean id="exampleBean" class="com.example.ExampleBean">
<property name="localeValue" value="en_US" />
</bean>
8. 类类型(Class):
Class类型表示一个类的类型,也可以通过value属性进行注入。
xml
<bean id="exampleBean" class="com.example.ExampleBean">
<property name="classValue" value="java.lang.String" />
</bean>
总结而言,Spring对简单类型的处理非常灵活,允许你使用value
属性来为这些属性赋值,而不需要引用其他bean。
需要注意的是:
- 如果将
Date
视为简单类型,必须确保日期字符串格式符合Date
的toString()
方法的格式,否则无法正确注入。- 在Spring 6之后,如果注入的是
URL
类型,Spring会进行有效性检测,如果提供的URL字符串不是有效的URL,将引发错误。
2.3 注入数组
当数组中的元素是简单类型时,我们可以使用以下方式配置 Spring bean:
xml
<bean id="student" class="com.example.beans.Student">
<property name="favoriteFoods">
<array>
<value>食物1</value>
<value>食物2</value>
<value>食物3</value>
</array>
</property>
</bean>
在上述示例中,我们创建了一个名为"student
"的Spring bean,它是一个Student
对象,其中包含一个名为"favoriteFoods
"的属性。该属性是一个字符串数组,通过<array>
标签来定义,而每个元素使用<value>
标签包含。
当数组中的元素是非简单类型时,例如一个订单Order
包含多个商品Goods
时,可以使用以下方式配置:
xml
<bean id="goods1" class="com.example.beans.Goods">
<property name="name" value="西瓜"/>
</bean>
<bean id="goods2" class="com.example.beans.Goods">
<property name="name" value="苹果"/>
</bean>
<bean id="order" class="com.example.beans.Order">
<property name="goods">
<array>
<ref bean="goods1"/>
<ref bean="goods2"/>
</array>
</property>
</bean>
在这个示例中,我们创建了两个Spring bean,分别为"goods1"和"goods2",它们都是Goods
对象,并分别设置了名称属性。然后,我们创建了一个名为"order"的Spring bean,它是一个Order
对象,其中包含一个名为"goods"的属性,该属性是一个对象数组,通过<array>
标签来定义,而每个元素使用<ref>
标签来引用之前定义的"goods1"和"goods2" bean。
因此总结一下:
- 如果数组中是简单类型,使用
<value>
标签来定义元素。 - 如果数组中是非简单类型,使用
<ref>
标签来引用其他bean,这些bean定义了非简单类型的对象。
2.4 注入集合
(1)List
List集合是一种有序可重复的数据结构,可以包含简单类型或引用类型的元素。在Spring配置文件中,我们可以使用 <list>
标签来实现List集合的注入,同时根据集合元素的类型使用 <value>
标签或 <ref>
标签。
当List集合中包含简单类型的元素时,可以使用 <value>
标签将这些元素注入到bean中,如下所示:
xml
<bean id="people" class="com.example.beans.People">
<property name="names">
<list>
<value>张三</value>
<value>李四</value>
<value>王五</value>
</list>
</property>
</bean>
在上述示例中,我们创建了一个名为"people
"的Spring bean,它是一个People
对象,其中包含一个名为"names"的属性。该属性是一个List集合,通过 <list>
标签来定义,而每个元素使用 <value>
标签包含。
如果List集合中包含引用类型的元素,可以使用 <ref>
标签将这些元素注入到bean中,如下所示:
xml
<bean id="student1" class="com.example.beans.Student">
<property name="friends">
<list>
<ref bean="friend1"/>
<ref bean="friend2"/>
</list>
</property>
</bean>
在这个示例中,我们创建了两个Spring bean,分别为"friend1"和"friend2",它们都是其他Student
对象的引用。然后,我们创建了一个名为"student1"的Spring bean,它是一个Student
对象,其中包含一个名为"friends"的属性。该属性是一个List集合,通过 <list>
标签来定义,而每个元素使用 <ref>
标签来引用其他的bean。
因此总结一下:
- 注入List集合时,使用
<list>
标签。 - 如果List集合中包含简单类型,使用
<value>
标签定义元素。 - 如果List集合中包含引用类型,使用
<ref>
标签引用其他bean。
(2)Set
Set 集合是一种无序且不可重复的数据结构,可以包含简单类型或引用类型的元素。在Spring配置文件中,我们可以使用 <set>
标签来实现Set集合的注入,同时根据集合元素的类型使用 <value>
标签或 <ref>
标签。
当Set集合中包含简单类型的元素时,可以使用 <value>
标签将这些元素注入到bean中,如下所示:
xml
<bean id="people" class="com.example.beans.People">
<property name="phones">
<set>
<value>手机号1...</value>
<value>手机号2...</value>
</set>
</property>
</bean>
在上述示例中,我们创建了一个名为"people"的Spring bean,它是一个People
对象,其中包含一个名为"phones"的属性。该属性是一个Set集合,通过 <set>
标签来定义,而每个元素使用 <value>
标签包含。
如果Set集合中包含引用类型的元素,可以使用 <ref>
标签将这些元素注入到bean中,如下所示:
xml
<bean id="student1" class="com.example.beans.Student">
<property name="friends">
<set>
<ref bean="friend1"/>
<ref bean="friend2"/>
</set>
</property>
</bean>
在这个示例中,我们创建了两个Spring bean,分别为"friend1"和"friend2",它们都是其他Student
对象的引用。然后,我们创建了一个名为"student1"的Spring bean,它是一个Student
对象,其中包含一个名为"friends"的属性。该属性是一个Set集合,通过 <set>
标签来定义,而每个元素使用 <ref>
标签来引用其他的bean。
因此总结一下:
- 当注入Set集合时,使用
<set>
标签。 - 如果Set集合中包含简单类型,使用
<value>
标签定义元素。 - 如果Set集合中包含引用类型,使用
<ref>
标签引用其他bean。
(3)Map
Map集合是一种键值对集合,其中键和值可以是简单类型或引用类型的元素。在Spring配置文件中,我们可以使用 <map>
标签来实现Map集合的注入,同时根据键和值的类型使用 <entry>
标签的 key
和 value
属性或 key-ref
和 value-ref
属性。
当Map集合中的键和值都是简单类型时,可以使用 <entry>
标签的 key
和 value
属性将这些元素注入到bean中,如下所示:
xml
<bean id="people" class="com.example.beans.People">
<property name="addrs">
<map>
<!-- 如果key和value都是简单类型,可以使用 key 和 value 属性 -->
<entry key="1" value="北京大兴区"/>
<entry key="2" value="上海浦东区"/>
<entry key="3" value="深圳宝安区"/>
</map>
</property>
</bean>
在上述示例中,我们创建了一个名为"people"的Spring bean,它是一个People
对象,其中包含一个名为"addrs"的属性。该属性是一个Map集合,通过 <map>
标签来定义,而每个 <entry>
标签包含了键和值的简单类型属性。
如果Map集合中的键和值是引用类型,可以使用 key-ref
和 value-ref
属性将这些元素注入到bean中,类似于以下示例:
xml
<bean id="address1" class="com.example.beans.Address">
<!-- 设置Address的属性值 -->
</bean>
<bean id="address2" class="com.example.beans.Address">
<!-- 设置Address的属性值 -->
</bean>
<bean id="people" class="com.example.beans.People">
<property name="addrs">
<map>
<!-- 如果key和value是引用类型,使用 key-ref 和 value-ref 属性 -->
<entry key-ref="address1" value-ref="address2"/>
</map>
</property>
</bean>
在这个示例中,我们创建了两个Spring bean,分别为"address1"和"address2",它们都是其他Address
对象的引用。然后,我们创建了一个名为"people"的Spring bean,它是一个People
对象,其中包含一个名为"addrs"的属性。该属性是一个Map集合,通过 <map>
标签来定义,而每个 <entry>
标签使用 key-ref
和 value-ref
属性来引用其他的bean。
因此总结一下:
- 当注入Map集合时,使用
<map>
标签。 - 如果键和值都是简单类型,可以使用
<entry>
标签的key
和value
属性。 - 如果键和值是引用类型,使用
<entry>
标签的key-ref
和value-ref
属性。
2.5 注入字符串
注入空字符串时,可以使用 <value>
标签,并将其内容留空,或者可以使用空的 value
属性。以下是示例:
xml
<bean id="person" class="com.example.beans.Person">
<!-- 空串的第一种方式 -->
<property name="email">
<value/>
</property>
<!-- 空串的第二种方式 -->
<property name="phone" value=""/>
</bean>
在上述示例中,我们创建了一个名为"person"的Spring bean,它是一个Person
对象,其中包含两个属性,"email"和"phone"。这两个属性都被注入了空字符串。
2.6 注入null
当我们需要将null
值注入到bean的属性时,有两种方式可以实现。
第一种方式:不为属性赋值,即不包含属性标签。
xml
<bean id="person" class="com.example.beans.Person" />
在这个示例中,我们创建了一个名为"person"的Spring bean,它是一个Person
对象,但没有为属性赋值,因此属性值默认为null。
第二种方式:使用 <null>
标签。
xml
<bean id="person" class="com.example.beans.Person">
<property name="email">
<null/>
</property>
</bean>
在这个示例中,我们同样创建了一个名为"person"的Spring bean,它是一个Person
对象,但在属性"email"上使用了 <null>
标签。这将显式地将属性"email"的值设为null。
2.7 注入特殊符号
XML中有5个特殊字符,分别是:<、>、'、"、&。这些特殊字符在XML中会被解析为XML语法的一部分,因此如果这些字符直接出现在XML文本中,可能会导致解析错误。
解决方案包括两种:
方案一:使用转义字符代替特殊字符。 以下是特殊字符和对应的转义字符:
-
>
转义为>
-
<
转义为<
-
'
转义为'
-
"
转义为"
-
&
转义为&
例如:
xml
<bean id="mathBean" class="com.example.beans.Math">
<property name="result" value="2 < 3"/>
</bean>
这样可以确保特殊字符被正确解析,并不会干扰XML的结构。
方案二:使用CDATA区域包装包含特殊字符的字符串。 这种方式将字符串放在 <![CDATA[]]>
区域内,确保其中的文本不会被XML解析器解析。注意,这种方式只能在使用 <value>
标签时使用。
例如:
xml
<bean id="mathBean" class="com.example.beans.Math">
<property name="result">
<!-- 只能使用 <value> 标签 -->
<value><![CDATA[2 < 3]]></value>
</property>
</bean>
这样的做法告诉XML解析器不解析CDATA区域内的文本,保留原始的特殊字符。
三、总结
在本篇文章中,我们进一步探讨了Spring依赖注入的基础用法。
首先,我们学习了如何注入内部Bean,这种方式允许我们在一个bean
的配置中直接嵌套定义另一个bean,提供了更灵活的配置选项。接着,我们探讨了如何注入简单类型,以及注入数组、注入集合等。最后我们了解了如何注入空字符串和null
值,以及如何处理XML中的特殊字符。
希望通过本篇文章,我们能了解如何配置和管理Spring应用程序中的bean。