Spring6 - IOC(基于XML)
# 1. IOC 介绍
IOC 是 Inversion of Control 的简写,译为「控制反转」,它不是一门技术,而是一种设计思想,是一个重要的面向对象编程法则,能够指导我们如何设计出松耦合、更优良的程序。
Spring 通过 IOC 容器来管理所有 Java 对象的实例化和初始化,控制对象与对象之间的依赖关系。我们将由 IOC 容器管理的 Java 对象称为 Spring Bean,它与使用关键字 new 创建的 Java 对象没有任何区别。
IOC 容器是 Spring 框架中最重要的核心组件之一,它贯穿了 Spring 从诞生到成长的整个过程。
# 2. IOC 容器
# 2.1 控制反转(IOC)
控制反转是一种思想
控制反转是为了降低程序耦合度,提高程序扩展力
控制反转,反转的是什么?
将对象的创建权利交出去,交给第三方容器负责
将对象和对象之间关系的维护权交出去,交给第三方容器负责
控制反转这种思想如何实现呢?
- DI(Dependency Injection):依赖注入
# 2.2 依赖注入
DI是"Dependency Injection"的缩写,中文通常翻译为"依赖注入"。它是Spring框架中实现"控制反转"(IOC)思想的一种方式。
- 依赖注入的基本理念是,一个类不应该自己去创建或查找它所依赖的其他类的实例,而应该由外部(在Spring框架中,通常是Spring的IOC容器)负责这些实例的创建和注入。这样可以将类与类之间的耦合性降低到最低,使得代码更加灵活,更易于测试和维护。
- 举个例子,假设有一个类A,它需要使用类B的方法。在传统的设计中,A需要自己创建B的实例,或者通过某种方式查找到B的实例。但在依赖注入的设计中,A只需要声明自己需要使用B,Spring框架就会自动将B的实例注入到A中,A直接使用就可以了。
Spring中主要有三种依赖注入的方式:
- Setter注入:通过调用对象的setter方法来注入依赖。
- 构造方法注入:通过构造器来注入依赖。
- 属性注入:直接将依赖注入到字段中。
简而言之,IOC是一种设计思想 ,而 DI 是对 IOC 的一种具体实现。
Bean 管理说的是:Bean 对象的创建,以及 Bean 对象中属性的赋值(或者叫做 Bean 对象之间关系的维护)。
# 2.3 IOC 容器在 Spring 的实现
Spring 的 IOC 容器就是 IOC 思想的一个落地的产品实现。IOC 容器中管理的组件也叫做 Bean。在创建 Bean 之前,首先需要创建 IOC 容器。Spring 提供了 IOC 容器的两种实现方式:
# BeanFactory
- 基础:
BeanFactory
是Spring容器的最基础形式,提供了基本的容器功能,主要负责的是配置、创建和管理bean。 - 惰性初始化:当使用
BeanFactory
加载配置文件时,它不会立即创建配置文件中定义的bean。只有在第一次请求(通过getBean
方法)某个bean时,才会创建该bean实例。这种行为被称为“惰性初始化”或“延迟加载”。 - 面向Spring本身:
BeanFactory
是面向Spring内部使用的低级接口,不推荐开发人员直接使用。
# ApplicationContext
- 高级特性:
ApplicationContext
是BeanFactory
的子接口,添加了更多企业级功能。它完全覆盖了BeanFactory
的功能,并提供了额外的支持,如国际化、事件传播、资源加载等。 - 饥饿加载:当使用
ApplicationContext
时,容器在启动时会立即创建并初始化所有的单例bean(除非指定了延迟初始化)。这种行为被称为“饥饿加载”或“预加载”。 - 面向使用者:
ApplicationContext
是面向Spring的终端用户设计的,提供了配置和管理bean的高级接口。在实际开发中,几乎所有的场景都会使用ApplicationContext
而不是直接使用BeanFactory
。
# ApplicationContext的主要实现类
ClassPathXmlApplicationContext
:- 这是一个常用的实现类,它从类路径(即项目的
resources
目录下)加载XML配置文件。通过这种方式,你可以将Spring的配置文件放在Java项目的资源目录下,然后通过这个类读取配置,初始化Spring容器。
- 这是一个常用的实现类,它从类路径(即项目的
FileSystemXmlApplicationContext
:- 如果你的Spring配置文件不在类路径下,而是在文件系统的某个位置,那么可以使用这个实现类。它可以加载任意路径下的XML配置文件来初始化Spring容器。这个路径可以是绝对路径,也可以是相对路径。
ConfigurableApplicationContext
:- 这是一个扩展的ApplicationContext接口,提供了一些额外的功能,如刷新和关闭容器。
refresh()
方法可以重新加载配置和重新初始化容器,而close()
方法可以关闭容器,释放资源。
- 这是一个扩展的ApplicationContext接口,提供了一些额外的功能,如刷新和关闭容器。
WebApplicationContext
:- 专门为Web应用设计的ApplicationContext实现,它在普通的ApplicationContext基础上添加了对Web环境的支持。通过将Spring容器与Servlet容器(如Tomcat)集成,可以实现在Web应用运行时管理和访问Spring容器。
# 3. 基于 XML 的形式
# 3.1 创建Maven项目
- 启动IntelliJ IDEA:打开IntelliJ IDEA,等待其加载完成。
- 创建新项目:选择菜单栏中的
File
>New
>Project
,这会打开创建新项目的向导。 - 选择项目类型:在左侧的项目类型列表中,选择
Maven
。确保右侧的选项中已经勾选了Create from archetype
选项,然后点击Next
继续。 - 填写项目信息:在接下来的页面中,填写
GroupId
(通常是组织或公司的域名倒序,如com.example
)、ArtifactId
(项目名,如demo
)、Version
(版本号,如果是新项目,通常使用1.0-SNAPSHOT
)。填写完成后,点击Next
。 - 项目名称和位置:填写项目的名称,并选择一个合适的目录作为项目的位置。完成后,点击
Finish
按钮,IntelliJ IDEA会为你创建一个新的Maven项目。
# 3.2 配置Maven国内镜像源
为了加快依赖下载速度,避免网络问题,推荐配置国内的Maven镜像源。下面以配置阿里云Maven镜像为例:
打开Maven的配置文件:找到你的Maven安装目录,编辑
conf
文件夹下的settings.xml
文件。如果你是通过IntelliJ IDEA管理Maven的,可以在IDEA的Settings
或Preferences
中找到Maven设置,从而直接编辑settings.xml
文件。添加镜像源:在
<mirrors>
标签内添加一个新的<mirror>
标签,配置如下:<mirror> <id>aliyunmaven</id> <mirrorOf>*</mirrorOf> <name>阿里云公共仓库</name> <url>https://maven.aliyun.com/repository/public</url> </mirror>
1
2
3
4
5
6保存并重启IDEA:保存
settings.xml
文件的修改,并重启IntelliJ IDEA,以便更改生效。
通过上述步骤,你的Maven项目将会从阿里云的镜像源下载依赖,这通常会比从默认的Maven中央仓库下载速度更快,更稳定。
注意:在配置镜像源时,请确保你选择的镜像源是可信赖的,因为错误或过时的镜像源可能会影响项目依赖的正确性和安全性。
# 3.3 添加Spring框架支持
大家可以去中央仓库拉取, 也可以直接复制下面代码粘贴到 Spring 项目中 pom.xml 下 dependencies
标签中.
在pom.xml中添加Spring的框架支持,xml配置如下:
<dependencies>
<!--Spring context依赖-->
<!--当你引入Spring Context依赖之后,表示将Spring的基础依赖引入了-->
<dependency>
<groupId>org.Springframework</groupId>
<artifactId>Spring-context</artifactId>
<version>6.1.5</version>
</dependency>
<!--junit5测试-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.3.1</version>
</dependency>
<!--log4j2的依赖-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.19.0</version>
</dependency>
</dependencies>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
【注意事项】
我们在添加完依赖时, 一定要切记点击 IDEA 右边 maven 的 Reload, 如果 IDEA 项目中出现下图中相关依赖信息, 则表示添加依赖成功了.
# 3.4 创建启动类
创建一个包含main
方法的启动类。这个类将作为整个应用的入口点。例如,创建一个名为Application
的类:
public class Application {
public static void main(String[] args) {
// Spring 应用启动逻辑
}
}
2
3
4
5
# 3.5 添加Spring配置文件
为了让Spring框架能够了解你的Bean配置,需要创建一个Spring配置文件。通常,这个配置文件被命名为spring-config.xml
,位于资源文件夹resources
下。
- 在
src/main/resources
目录下创建一个新的文件,命名为spring-config.xml
(命名可以随意)。 - 在这个XML文件中,定义Spring Beans的配置,文件内容如下:
<?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">
<!-- 在此处添加Bean的定义 -->
</beans>
2
3
4
5
6
7
# 3.6 配置Bean对象
在spring-config.xml
文件中,你可以通过<bean>
标签来定义和配置Bean:
<bean id="student" class="com.scholar.spring6.Student"></bean>
在上面的例子中,id
属性用于指定Bean的唯一标识符,class
属性用于指定Bean的全类名。这告诉Spring容器,当应用启动时,需要创建一个student
的实例,并将其注册到Spring上下文中。
经过以上操作, 我的 bean 对象是否已经存储到 Spring 中了呢 ?
其实并没有, 上述操作只是声明了我要存储对象到 Spring 当中. 当程序正儿八经的执行的时候, Spring 采用的是懒加载, 在启动 Spring 项目的时候, 它不会立即的去将 bean 存储到 Spring 中, 这是它实现上的一个策略, 这也是广义上和狭义上的一个区别, 就类似于并发和并行两者之间的区别一样, 初学的时候, 站在广义的角度上便于理解.
# 3.7 获取并使用Bean对象
使用Spring上下文的方式获取Bean对象
- 先获取Spring上下文对象(ApplicationContext的实例)
- 再通过上下文对象提供的方法获取指定的Bean对象
# 3.7.1 先获取Spring上下文对象
public class Application {
public static void main(String[] args) {
//1. 获取Spring上下文对象(ApplicationContext的实例)
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//2.从spring容器中获取 Bean 对象 ,此处的"student" 必须和spring-config.xml配置中的Bean的id保持一致
//getBean的返回类型为Object,所以强转一下
Student student =(Student) context.getBean("student"); //该行代码就是实现IOC思想的DI操作
//3.使用Bean对象(非必须,也可以获取不用)
student.sayHi();
}
}
2
3
4
5
6
7
8
9
10
11
除了这种方法可以拿到 Spring 上下文对象之外, 我们还可以使用 BeanFactory
来作为 Spring 上下文对象. 使用 ApplicationContext
或者 BeanFactory
来获取 Spring 上下文对象, 是同样的效果, 只不过 ApplicaitonContext 是 BeanFactory 的一个子类.
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("spring-config.xml"));
# 3.7.2 获取 Bean(根据上下文对象提供的 getBean 方法)
# (1) 通过 Bean id 来获取
//根据上下文提供的方法获取bean对象
Student student =(Student) context.getBean("student");
2
# (2) 通过类型来获取
//通过类型获取bean对象
Student student =context.getBean(Student.class);
2
虽然写法简单,但是容易出问题,当同一个类型被注入 Spring 中多次(多个)的时候,就会报错
# (3) 通过 id + 类型来获取 (推荐写法)
为了结合上述两种方式的优点,并避免它们的缺陷,Spring还提供了通过id加类型同时获取Bean实例的方法。这种方式即确保了类型的安全性,也避免了因类型重复而产生的歧义:
//通过id+类型获取bean对象
Student student =context.getBean("student",Student.class);
2
这种方法是最推荐的方式,它既保证了通过id的唯一性来精确定位Bean,又通过类型确认了返回对象的类型安全,避免了强制类型转换的风险。此外,如果在Spring容器中存在多个同类型的Bean,使用这种方式可以精确地获取到指定id的Bean,从而避免了潜在的冲突和错误。
# (4) 扩展知识
如果组件类实现了接口,根据接口类型可以获取 Bean 吗?
可以,前提是 Bean 唯一
如果一个接口有多个实现类,这些实现类都配置了 Bean,根据接口类型可以获取 Bean 吗?
不行,因为 Bean 不唯一
测试通过接口类型来获取实现类的Bean
# 3.9 依赖注入之 setter 注入
创建学生类 Student
package com.boge.spring.bean;
public class Student {
private Integer id;
private String name;
private Integer age;
private String gender;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
'}';
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
然后我们的配置文件中通过 property
标签来完成setter注入:这里的name
属性对应于Student
类中的setter方法名称,而value
属性则是要注入的具体值。
<bean id="student" class="com.scholar.spring6.Student">
<!-- property 标签:通过组件类的 setXxx() 方法给组件对象设置属性 -->
<!-- name 属性:指定属性名(这个属性名是 getXxx()、setXxx() 方法定义的,和成员变量无关) -->
<!-- value 属性:指定属性值 -->
<property name="id" value="1001"></property>
<property name="name" value="张三"></property>
<property name="age" value="23"></property>
<property name="gender" value="男"></property>
</bean>
2
3
4
5
6
7
8
9
当Spring框架加载这个XML配置文件时,它会根据<bean>
标签中的class
属性创建Student
对象的实例,然后根据<property>
标签的指定,通过反射调用setter方法,将配置文件中定义的值注入到Student
对象的相应属性中。
在这儿需要注意。对应的属性我们必须要提供setter方法。
# 3.10 依赖注入之构造器注入
针对上面的设置注入中的必要条件是对应的属性必须添加相关的setter方法。我们可以通过构造注入的方式来解决。
package com.scholar.spring6;
public class User {
private Integer id;
private String name;
private Integer age;
private String gender;
// 构造函数
public User(Integer id, String name, Integer age, String gender) {
this.id = id;
this.name = name;
this.age = age;
this.gender = gender;
}
// 无参构造函数(如果有其他构造函数,无参构造函数也需要显式定义)
public User() {
}
// toString方法用于打印对象信息
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
'}';
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
在Spring的XML配置文件中,我们通过<bean>
标签定义User
类的Bean,并使用<constructor-arg>
子标签来注入构造函数的参数。这里的<constructor-arg>
标签可以通过index
、name
或直接提供value
来指定参数值。
<bean class="com.scholar.spring6.User">
<!-- 使用constructor-arg标签进行构造器注入 -->
<constructor-arg index="0" value="10086"></constructor-arg>
<constructor-arg index="1" value="boge"></constructor-arg>
<constructor-arg index="2" value="20"></constructor-arg>
<constructor-arg index="3" value="女"></constructor-arg>
</bean>
2
3
4
5
6
7
注意事项
- constructor-arg标签:用于指定构造函数的参数,可以通过
index
指定参数的位置(从0开始)、name
指定参数名称,或者直接使用value
属性来传递参数值。 - index属性:表示该参数在构造函数参数列表中的位置,从0开始编号。
- name属性:表示构造函数中参数的名称,这要求编译后的类文件保留参数名信息(Java 8+提供了这种能力),否则可能无法通过名称匹配参数
然后对应的执行效果
# 3.11 特殊值处理
# 字面量赋值
字面量指的是数据的直接表示,比如数字 10
、字符串 "hello"
等。在 Spring 的 XML 配置中,我们可以直接通过 value
属性为 Bean 的属性赋予字面量值。
<!-- 将 name 属性的值设置为字符串 "张三" -->
<property name="name" value="张三"/>
2
# null 值
在一些场景下,我们可能需要将 Bean 的某个属性显式地设置为 null
。由于直接在 value
中赋值 "null"
字符串并不代表真正的 null
值,Spring 提供了 <null/>
标签来表示属性值为 null
。
<!-- 显式地将 name 属性设置为 null -->
<property name="name">
<null />
</property>
2
3
4
注意事项
字面量赋值:使用
value
属性进行赋值时,Spring 会把这个值当作字面量处理。这意味着,如果你写value="10"
,Spring 会把这个值当作数字10
处理;如果写value="hello"
,则处理为字符串"hello"
。null 值处理:当需要为属性赋
null
值时,必须使用<null/>
标签。直接使用value="null"
实际上会将属性设置为字符串"null"
,而不是null
值。
# xml 实体
在 Spring 的 XML 配置文件中,我们有时候需要处理特殊字符,如小于号 <
。由于 <
在 XML 中用于定义标签的开始,直接使用可能会导致 XML 解析错误。为了在 XML 文件中正确表示这类特殊字符,我们可以使用 XML 实体或 CDATA 节。
XML 实体是一种特殊的标记,它允许我们在 XML 文档中使用那些在 XML 中有特殊意义的字符。常用的 XML 实体包括:
<
代表小于号<
>
代表大于号>
&
代表和号&
"
代表双引号"
'
代表单引号'
例如,当我们需要在 XML 文件中表示 a < b
这样的表达式时,可以使用 <
来代替小于号
<!-- 使用 XML 实体 < 代替小于号,避免 XML 解析错误 -->
<property name="expression" value="a < b"/>
2
# CDATA 节
另一种处理 XML 中特殊字符的方法是使用 CDATA 节。CDATA 节被用来告诉 XML 解析器,它所包含的内容应该被当作字符数据处理,而不是 XML 标记。在 CDATA 节中,你可以自由地使用任何字符,包括那些在 XML 中有特殊含义的字符。
CDATA 节的语法是 <![CDATA[内容]]>
。例如,要在 XML 配置中使用 a < b
,可以将其放在 CDATA 节中:
<property name="expression">
<!-- 使用 CDATA 节包裹特殊字符或表达式,确保它们被当作文本处理 -->
<!-- CDATA 节中的内容不会被 XML 解析器解析为 XML 标记或属性 -->
<value><![CDATA[a < b]]></value>
</property>
2
3
4
5
注意事项
- 使用 XML 实体 是处理单个特殊字符的简便方法,特别适用于简短的文本或属性值中。
- 当需要在配置中包含较长的文本或包含多个特殊字符时,CDATA 节 提供了一种更灵活的解决方案。
- 在使用 CDATA 节时,需要注意它的开始标记
<![CDATA[
和结束标记]]>
,确保它们正确无误。
# 3.12 为对象类型属性赋值
创建班级 类Clazz
package cn.youngkbt.Spring6.Bean
public class Clazz {
private Integer clazzId;
private String clazzName;
public Integer getClazzId() {
return clazzId;
}
public void setClazzId(Integer clazzId) {
this.clazzId = clazzId;
}
public String getClazzName() {
return clazzName;
}
public void setClazzName(String clazzName) {
this.clazzName = clazzName;
}
@Override
public String toString() {
return "Clazz{" +
"clazzId=" + clazzId +
", clazzName='" + clazzName + '\'' +
'}';
}
public Clazz() {
}
public Clazz(Integer clazzId, String clazzName) {
this.clazzId = clazzId;
this.clazzName = clazzName;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
修改 Student 类
在 Student 类中添加以下代码:
private Clazz clazz;
public Clazz getClazz() {
return clazz;
}
public void setClazz(Clazz clazz) {
this.clazz = clazz;
}
2
3
4
5
6
7
8
9
# 方式一:引用外部 Bean
首先,我们可以在 Spring 配置文件中单独配置 Clazz
类的 Bean,并通过 ref
属性在 Student
的配置中引用这个外部 Bean。这种方式允许我们在多个地方重用同一个 Bean 定义。
定义 Clazz
类的 Bean:
<!-- 定义一个Clazz类型的Bean -->
<Bean id="clazzOne" class="cn.youngkbt.Spring6.Bean.Clazz">
<property name="clazzId" value="1111"></property>
<property name="clazzName" value="财源滚滚班"></property>
</Bean>
2
3
4
5
为 Student
类的 clazz
属性引用 Clazz
类的 Bean:
<!-- 通过ref属性引用定义好的Clazz Bean -->
<Bean id="studentFour" class="cn.youngkbt.Spring6.Bean.Student">
...
<property name="clazz" ref="clazzOne"></property>
</Bean>
2
3
4
5
错误演示:
<Bean id="studentFour" class="cn.youngkbt.Spring6.Bean.Student">
<property name="id" value="1004"></property>
<property name="name" value="赵六"></property>
<property name="age" value="26"></property>
<property name="sex" value="女"></property>
<property name="clazz" value="clazzOne"></property>
</Bean>
2
3
4
5
6
7
如果错把 ref 属性写成了 value 属性,会抛出异常:
Caused by: Java.lang.IllegalStateException: Cannot convert value of type 'Java.lang.String' to required type
'cn.youngkbt.Spring6.Bean.Clazz' for property 'clazz': no matching editors or conversion strategy found
2
意思是不能把 String 类型转换成我们要的 Clazz 类型,说明我们使用 value 属性时,Spring 只把这个属性看做一个普通的字符串,不会认为这是一个 Bean 的 id,更不会根据它去找到 Bean 来赋值。
# 方式二:内部 Bean
我们还可以直接在 Student
类的配置中定义一个内部的 Clazz
Bean,这种内部 Bean 仅在包含它的外部 Bean 中有效,适用于当这个对象仅被一个 Bean 使用时的情况。
定义内部 Clazz
Bean:
<Bean id="studentFour" class="cn.youngkbt.Spring6.Bean.Student">
<property name="id" value="1004"></property>
<property name="name" value="赵六"></property>
<property name="age" value="26"></property>
<property name="sex" value="女"></property>
<property name="clazz">
<!-- 在一个 Bean 中再声明一个 Bean 就是内部 Bean -->
<!-- 内部 Bean 只能用于给属性赋值,不能在外部通过 XML 容器获取,因此可以省略 id 属性 -->
<Bean id="clazzInner" class="cn.youngkbt.Spring6.Bean.Clazz">
<property name="clazzId" value="2222"></property>
<property name="clazzName" value="远大前程班"></property>
</Bean>
</property>
</Bean>
2
3
4
5
6
7
8
9
10
11
12
13
14
# 方式三:级联属性赋值
级联属性赋值允许我们在不直接定义内部 Bean 的情况下,通过 .
语法直接设置某个属性的内部属性值。这种方式特别适用于已经通过 ref
引用了外部 Bean,但需要调整这个 Bean 的某些属性的场景。
使用级联属性为 Clazz
类的属性赋值:
<!-- 通过级联属性为Clazz类的属性赋值 -->
<Bean id="studentFour" class="cn.youngkbt.Spring6.Bean.Student">
...
<property name="clazz" ref="clazzOne"></property>
<!-- 直接设置clazz属性的内部属性 -->
<property name="clazz.clazzId" value="3333"></property>
<property name="clazz.clazzName" value="最强王者班"></property>
</Bean>
2
3
4
5
6
7
8
# 3.13 为数组类型属性赋值
修改Student类
在 Student 类中添加以下代码:
private String[] hobbies;
public String[] getHobbies() {
return hobbies;
}
public void setHobbies(String[] hobbies) {
this.hobbies = hobbies;
}
2
3
4
5
6
7
8
9
配置 Bean:注入到容器中的Bean的属性中可能是数组类型。那么这时我们可以通过array标签
来完成赋值
<Bean id="studentFour" class="cn.youngkbt.Spring.Bean6.Student">
<property name="id" value="1004"></property>
<property name="name" value="赵六"></property>
<property name="age" value="26"></property>
<property name="sex" value="女"></property>
<!-- ref属性:引用XML容器中某个Bean的id,将所对应的Bean为属性赋值 -->
<property name="clazz" ref="clazzOne"></property>
<property name="hobbies">
<array>
<value>抽烟</value>
<value>喝酒</value>
<value>烫头</value>
</array>
</property>
</Bean>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 3.14 为集合类型属性赋值
# 为List集合类型属性赋值
在 Clazz 类中添加以下代码:
private List<Student> students;
public List<Student> getStudents() {
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
2
3
4
5
6
7
8
9
配置 Bean:注入到容器中的Bean的属性可能是List集合。那么我们需要通过list标签来完成属性的赋值
<Bean id="clazzTwo" class="cn.youngkbt.Spring6.Bean.Clazz">
<property name="clazzId" value="4444"></property>
<property name="clazzName" value="Javaee0222"></property>
<property name="students">
<list>
<ref Bean="studentOne"></ref>
<ref Bean="studentTwo"></ref>
<ref Bean="studentThree"></ref>
</list>
</property>
</Bean>
2
3
4
5
6
7
8
9
10
11
若为 Set 集合类型属性赋值,只需要将其中的
list 标签改为 set 标签
即可
# 为 Map 集合类型属性赋值
创建教师类 Teacher:
package cn.youngkbt.Spring6.Bean;
public class Teacher {
private Integer teacherId;
private String teacherName;
public Integer getTeacherId() {
return teacherId;
}
public void setTeacherId(Integer teacherId) {
this.teacherId = teacherId;
}
public String getTeacherName() {
return teacherName;
}
public void setTeacherName(String teacherName) {
this.teacherName = teacherName;
}
public Teacher(Integer teacherId, String teacherName) {
this.teacherId = teacherId;
this.teacherName = teacherName;
}
public Teacher() {
}
@Override
public String toString() {
return "Teacher{" +
"teacherId=" + teacherId +
", teacherName='" + teacherName + '\'' +
'}';
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
在 Student 类中添加以下代码:
private Map<String, Teacher> teacherMap;
public Map<String, Teacher> getTeacherMap() {
return teacherMap;
}
public void setTeacherMap(Map<String, Teacher> teacherMap) {
this.teacherMap = teacherMap;
}
2
3
4
5
6
7
8
9
配置 Bean:
<Bean id="teacherOne" class="cn.youngkbt.Spring6.Bean.Teacher">
<property name="teacherId" value="10010"></property>
<property name="teacherName" value="大宝"></property>
</Bean>
<Bean id="teacherTwo" class="cn.youngkbt.Spring6.Bean.Teacher">
<property name="teacherId" value="10086"></property>
<property name="teacherName" value="二宝"></property>
</Bean>
<Bean id="studentFour" class="cn.youngkbt.Spring6.Bean.Student">
<property name="id" value="1004"></property>
<property name="name" value="赵六"></property>
<property name="age" value="26"></property>
<property name="sex" value="女"></property>
<!-- ref 属性:引用 XML 容器中某个 Bean 的 id,将所对应的 Bean 为属性赋值 -->
<property name="clazz" ref="clazzOne"></property>
<property name="hobbies">
<array>
<value>抽烟</value>
<value>喝酒</value>
<value>烫头</value>
</array>
</property>
<property name="teacherMap">
<map>
<entry>
<key>
<value>10010</value>
</key>
<ref Bean="teacherOne"></ref>
</entry>
<entry>
<key>
<value>10086</value>
</key>
<ref Bean="teacherTwo"></ref>
</entry>
</map>
</property>
</Bean>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 引用集合类型的 Bean
<!--list 集合类型的 Bean-->
<util:list id="students">
<ref Bean="studentOne"></ref>
<ref Bean="studentTwo"></ref>
<ref Bean="studentThree"></ref>
</util:list>
<!--map 集合类型的 Bean-->
<util:map id="teacherMap">
<entry>
<key>
<value>10010</value>
</key>
<ref Bean="teacherOne"></ref>
</entry>
<entry>
<key>
<value>10086</value>
</key>
<ref Bean="teacherTwo"></ref>
</entry>
</util:map>
<Bean id="clazzTwo" class="cn.youngkbt.Spring6.Bean.Clazz">
<property name="clazzId" value="4444"></property>
<property name="clazzName" value="Javaee0222"></property>
<property name="students" ref="students"></property>
</Bean>
<Bean id="studentFour" class="cn.youngkbt.Spring6.Bean.Student">
<property name="id" value="1004"></property>
<property name="name" value="赵六"></property>
<property name="age" value="26"></property>
<property name="sex" value="女"></property>
<!-- ref 属性:引用 XML 容器中某个 Bean 的 id,将所对应的 Bean 为属性赋值 -->
<property name="clazz" ref="clazzOne"></property>
<property name="hobbies">
<array>
<value>抽烟</value>
<value>喝酒</value>
<value>烫头</value>
</array>
</property>
<property name="teacherMap" ref="teacherMap"></property>
</Bean>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
使用 util:list
、util:map
标签必须引入相应的命名空间
<?xml version="1.0" encoding="UTF-8"?>
<Beans xmlns="http://www.Springframework.org/schema/Beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.Springframework.org/schema/util"
xsi:schemaLocation="http://www.Springframework.org/schema/util
http://www.Springframework.org/schema/util/Spring-util.xsd
http://www.Springframework.org/schema/Beans
http://www.Springframework.org/schema/Beans/Spring-Beans.xsd">
2
3
4
5
6
7
8
# 3.15 p 命名空间
引入 p 命名空间
<?xml version="1.0" encoding="UTF-8"?>
<Beans xmlns="http://www.Springframework.org/schema/Beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.Springframework.org/schema/util"
xmlns:p="http://www.Springframework.org/schema/p"
xsi:schemaLocation="http://www.Springframework.org/schema/util
http://www.Springframework.org/schema/util/Spring-util.xsd
http://www.Springframework.org/schema/Beans
http://www.Springframework.org/schema/Beans/Spring-Beans.xsd">
2
3
4
5
6
7
8
9
引入 p 命名空间后,可以通过以下方式为 Bean 的各个属性赋值
<Bean id="studentSix" class="cn.youngkbt.Spring6.Bean.Student"
p:id="1006" p:name="小明" p:clazz-ref="clazzOne" p:teacherMap-ref="teacherMap"></Bean>
2
# 3.16 引入外部属性文件
加入依赖
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-Java</artifactId>
<version>8.0.30</version>
</dependency>
<!-- 数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.15</version>
</dependency>
2
3
4
5
6
7
8
9
10
11
12
13
创建外部属性文件
jdbc.user=root
jdbc.password=youngkbt
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.driver=com.mysql.cj.jdbc.Driver
2
3
4
引入属性文件
引入 context 名称空间
<?xml version="1.0" encoding="UTF-8"?>
<Beans xmlns="http://www.Springframework.org/schema/Beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.Springframework.org/schema/context"
xsi:schemaLocation="http://www.Springframework.org/schema/Beans
http://www.Springframework.org/schema/Beans/Spring-Beans.xsd
http://www.Springframework.org/schema/context
http://www.Springframework.org/schema/context/Spring-context.xsd">
</Beans>
2
3
4
5
6
7
8
9
10
<!-- 引入外部属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
2
注意:在使用 context:property-placeholder 元素加载外包配置文件功能前,首先需要在 XML 配置的一级标签 <Beans>
中添加 context 相关的约束。
配置 Bean
<Bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</Bean>
2
3
4
5
6
测试
@Test
public void testDataSource() throws SQLException {
ApplicationContext ac = new ClassPathXmlApplicationContext("Spring-datasource.xml");
DataSource dataSource = ac.getBean(DataSource.class);
Connection connection = dataSource.getConnection();
System.out.println(connection);
}
2
3
4
5
6
7
# 3.17 基于 XML 自动装配
自动装配:
根据指定的策略,在 XML 容器中匹配某一个 Bean,自动为指定的 Bean 中所依赖的类类型或接口类型属性赋值
场景模拟
创建类 UserController
package cn.youngkbt.Spring6.autowire.controller
public class UserController {
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
public void saveUser(){
userService.saveUser();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
创建接口 UserService
package cn.youngkbt.Spring6.autowire.service
public interface UserService {
void saveUser();
}
2
3
4
5
6
创建类 UserServiceImpl 实现接口 UserService
package cn.youngkbt.Spring6.autowire.service.impl
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void saveUser() {
userDao.saveUser();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
创建接口 UserDao
package cn.youngkbt.Spring6.autowire.dao
public interface UserDao {
void saveUser();
}
2
3
4
5
6
创建类 UserDaoImpl 实现接口 UserDao
package cn.youngkbt.Spring6.autowire.dao.impl
public class UserDaoImpl implements UserDao {
@Override
public void saveUser() {
System.out.println("保存成功");
}
}
2
3
4
5
6
7
8
9
配置Bean
使用 Bean 标签的 autowire 属性设置自动装配效果
自动装配方式:byType
byType:根据类型匹配XML容器中的某个兼容类型的 Bean,为属性自动赋值
若在 XML 中,没有任何一个兼容类型的 Bean 能够为属性赋值,则该属性不装配,即值为默认值 null
若在 XML 中,有多个兼容类型的 Bean 能够为属性赋值,则抛出异常 NoUniqueBeanDefinitionException
<Bean id="userController" class="cn.youngkbt.Spring6.autowire.controller.UserController" autowire="byType"></Bean>
<Bean id="userService" class="cn.youngkbt.Spring6.autowire.service.impl.UserServiceImpl" autowire="byType"></Bean>
<Bean id="userDao" class="cn.youngkbt.Spring6.autowire.dao.impl.UserDaoImpl"></Bean>
2
3
4
5
自动装配方式:byName
byName:将自动装配的属性的属性名,作为 Bean 的 id 在 XML 容器中匹配相对应的 Bean 进行赋值
<Bean id="userController" class="cn.youngkbt.Spring6.autowire.controller.UserController" autowire="byName"></Bean>
<Bean id="userService" class="cn.youngkbt.Spring6.autowire.service.impl.UserServiceImpl" autowire="byName"></Bean>
<Bean id="userServiceImpl" class="cn.youngkbt.Spring6.autowire.service.impl.UserServiceImpl" autowire="byName"></Bean>
<Bean id="userDao" class="cn.youngkbt.Spring6.autowire.dao.impl.UserDaoImpl"></Bean>
<Bean id="userDaoImpl" class="cn.youngkbt.Spring6.autowire.dao.impl.UserDaoImpl"></Bean>
2
3
4
5
6
7
测试
@Test
public void testAutoWireByXML(){
ApplicationContext ac = new ClassPathXmlApplicationContext("autowire-xml.xml");
UserController userController = ac.getBean(UserController.class);
userController.saveUser();
}
2
3
4
5
6