【Java】基础配置和管理Spring中的bean

一、前言

在上一篇文章中,我们探讨了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视为简单类型,必须确保日期字符串格式符合DatetoString()方法的格式,否则无法正确注入。
  • 在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> 标签的 keyvalue 属性或 key-refvalue-ref 属性。

当Map集合中的键和值都是简单类型时,可以使用 <entry> 标签的 keyvalue 属性将这些元素注入到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-refvalue-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-refvalue-ref 属性来引用其他的bean。

因此总结一下:

  • 当注入Map集合时,使用 <map> 标签。
  • 如果键和值都是简单类型,可以使用 <entry> 标签的 keyvalue 属性。
  • 如果键和值是引用类型,使用 <entry> 标签的 key-refvalue-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文本中,可能会导致解析错误。

解决方案包括两种:

方案一:使用转义字符代替特殊字符。 以下是特殊字符和对应的转义字符:

  • > 转义为 &gt;

  • < 转义为 &lt;

  • ' 转义为 '

  • " 转义为 &quot;

  • & 转义为 &amp;

例如:

xml 复制代码
<bean id="mathBean" class="com.example.beans.Math">
    <property name="result" value="2 &lt; 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。

相关推荐
IT_陈寒4 小时前
闭包陷阱让我加了两天班,JavaScript你真行
前端·人工智能·后端
易协同低代码5 小时前
通达OA核心类库TD类深度解析
后端
Gopher_HBo5 小时前
Go语言学习笔记(十八)Gin处理Session
后端
谭光志6 小时前
工具塞满上下文窗口怎么办?深度拆解 AI Agent Tool Search 按需加载实现原理
前端·后端·ai编程
她说..6 小时前
Java 默认值设置方式
java·开发语言·后端·springboot
foggyprojects6 小时前
从0开始,一句话启动AI DataAgent
后端·数据分析·ai编程
郡杰6 小时前
一些基础和问题解决
后端
陈随易7 小时前
前端项目部署只要30秒
前端·后端·程序员
YIAN7 小时前
从零手写文件读取 MCP 服务:一文吃透 Model Context Protocol 全链路通信原理
前端·后端·mcp
Imchendiana7 小时前
《狂人日记NO.9》— 前后端一把梭,我的全栈实录
前端·后端