【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。

相关推荐
小蜗牛慢慢爬行2 分钟前
有关异步场景的 10 大 Spring Boot 面试问题
java·开发语言·网络·spring boot·后端·spring·面试
goTsHgo31 分钟前
在 Spring Boot 的 MVC 框架中 路径匹配的实现 详解
spring boot·后端·mvc
waicsdn_haha43 分钟前
Java/JDK下载、安装及环境配置超详细教程【Windows10、macOS和Linux图文详解】
java·运维·服务器·开发语言·windows·后端·jdk
Q_19284999061 小时前
基于Spring Boot的摄影器材租赁回收系统
java·spring boot·后端
良许Linux1 小时前
0.96寸OLED显示屏详解
linux·服务器·后端·互联网
求知若饥1 小时前
NestJS 项目实战-权限管理系统开发(六)
后端·node.js·nestjs
左羊1 小时前
【代码备忘录】复杂SQL写法案例(一)
后端
gb42152872 小时前
springboot中Jackson库和jsonpath库的区别和联系。
java·spring boot·后端
程序猿进阶2 小时前
深入解析 Spring WebFlux:原理与应用
java·开发语言·后端·spring·面试·架构·springboot