hooyantsing's Blog

P07 - P19 IOC容器-Bean管理XML方式

字数统计: 3.9k阅读时长: 19 min
2022/05/06

视频源:尚硅谷Spring框架视频教程(spring5源码级讲解)

IOC 操作 Bean 管理

1. 什么是 Bean 管理(两个操作):

  1. Spring 创建对象;
  2. Spring 注入属性。

2. Bean 管理操作有两种方式

  1. 基于 xml 配置文件方式实现;
  2. 基于注解方式实现

IOC 操作 Bean 管理(基于 xml 方式)

1. 基于 xml 方式创建对象

1
<bean id="user" class="com.atguigu.spring5.User"></bean>
  1. 在 spring 配置文件中,使用 bean 标签,标签里面添加对应属性,就可以实现对象创建;
  2. 在 bean 标签有很多属性,介绍常用的属性;
    • id 属性:唯一标识,通过它从 IOC 中获取对象;
    • class 属性:类全路径(包类路径)。
  3. 创建对象时,默认执行无参构造方法。

2. 基于 xml 方式注入属性

  1. DI:依赖注入,就是注入属性。

3. 使用 set方法 注入属性

model 类

1
2
3
4
5
6
7
8
9
10
11
12
public class Book {
private String bname;
private String bauthor;

public void setBname(String bname) {
this.bname = bname;
}

public void setBauthor(String bauthor) {
this.bauthor = bauthor;
}
}

spring 配置文件

1
2
3
4
5
6
7
8
<bean id="book" class="com.atguigu.spring5.Book">
<!-- 使用 property 标签完成属性注入
name 属性:类里面属性名称
value 属性:向属性注入的值
-->
<property name="bname" value="易筋经"></property>
<property name="bauthor" value="达摩老祖"></property>
</bean>

4. 使用 有参构造方法 注入属性

model 类

1
2
3
4
5
6
7
8
9
public class Orders {
private String oname;
private String address;

public Orders(String oname,String address) {
this.oname = oname;
this.address = address;
}
}

spring 配置文件

1
2
3
4
<bean id="orders" class="com.atguigu.spring5.Orders">
<constructor-ages name="oname" value="电脑"</constructor-ages>
<constructor-ages name="address" value="China"></constructor-ages>
</bean>

由于存在有参构造方法,并且没有显式无参构造方法。在编辑 xml 文件时,仅写 bean 不含 constructor-ages 标签时,class 属性会报错。

5. 使用 p命名空间 注入属性

可以简化基于 xml 配置方式。

Step 01:添加 p命名空间 在配置文件中

image-20220507091455484

1
xmlns:p="http://www.springframework.org/schema/p"

Step 02:简化配置代码

1
2
3
4
5
6
7
8
<!-- 简化前 -->
<bean id="book" class="com.atguigu.spring5.Book">
<property name="bname" value="易筋经"></property>
<property name="bauthor" value="达摩老祖"></property>
</bean>

<!-- 简化后 -->
<bean id="book" class="com.atguigu.spring5.Book" b:bname="易筋经" b:bauthor="达摩老祖"></bean>

IOC 操作 Bean 管理(xml 注入其他类型属性)

1. 字面量

  1. null 值

    1
    2
    3
    <property name="address">
    <null/>
    </property>
  2. 属性值包含特殊符号

    method 01:字符转义

    1
    <property name="address" value="&lt;&lt;南京&gt;&gt;"></property>

    method 02:把带特殊符号的内容写到 CDATA

    1
    2
    3
    <property name="address">
    <value><![CDATA[<<南京>>]]></value>
    </property>

2. 注入属性 - 外部 bean

UserService

1
2
3
4
5
6
7
8
9
10
11
12
public class UserService {    
// 创建 UserDao 类型属性,生成 set 方法
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}

public void add() {
System.out.println("service add");
userDao.update();
}
}

UserDao 接口

1
2
3
public interface UserDao {
public void update();
}

UserDaoImpl

1
2
3
4
5
6
public class UserDaoImpl implements UserDao {
@Override
public void update() {
System.out.println("dao update");
}
}

spring 配置文件

1
2
3
4
5
6
7
8
<bean id="userService" class="com.atguigu.spring5.service.UserService">
<!--
name 属性:类里面属性名称
ref 属性:创建 userDao 对象 bean 标签 id 值
-->
<property name="userDao" ref="userDaoImpl"></property>
</bean>
<bean id="userDaoImpl" class="com.atguigu.spring5.dao.UserDaoImpl"></bean>

3. 注入属性 - 内部 bean

一对多关系:部门和员工

一个部门有多个员工,一个员工属于一个部门。部门是一,员工是多。

Dept

1
2
3
4
5
6
7
// 部门类
public class Dept {
private String dname;
public void setDname (String dname) {
this.dname = dname;
}
}

Emp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 员工类
public class Emp {
private String ename;
private String gender;
// 员工属于某一个部门,使用对象形式表示
private Dept dept;

public void setDept(Dept dept) {
this.dept = dept;
}
public void setEname(String ename) {
this.ename = ename
}
public void setGender(String gender) {
this.gender = gender;
}
}

spring 配置文件

1
2
3
4
5
6
7
8
9
10
11
12
<!-- 内部 bean -->
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
<!-- 设置两个普通属性 -->
<property name="ename" value="lucy"></property>
<property name="gender" value="girl"></property>
<!-- 设置对象类型属性 -->
<property name="dept">
<bean id="dept" class="com.atguigu.spring5.bean.Dept">
<property name="dname" value="安保部"></property>
</bean>
</property>
</bean>

4. 注入属性 - 级联赋值

method 01

spring 配置文件

1
2
3
4
5
6
7
8
9
10
11
<!-- 级联赋值 -->
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
<!-- 设置两个普通属性 -->
<property name="ename" value="lucy"></property>
<property name="gender" value="girl"></property>
<!-- 设置对象类型属性 -->
<property name="dept" ref="dept"></property>
</bean>
<bean id="dept" class="com.atguigu.spring5.bean.Dept">
<property name="dname" value="安保部"></property>
</bean>

method 02

Emp 对其修改,增加 Dept 的 get 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 员工类
public class Emp {
private String ename;
private String gender;
// 员工属于某一个部门,使用对象形式表示
private Dept dept;

// 生成 dept 的 get 方法
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
public void setEname(String ename) {
this.ename = ename
}
public void setGender(String gender) {
this.gender = gender;
}
}

spring 配置文件

1
2
3
4
5
6
7
8
9
10
<!-- 级联赋值 -->
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
<!-- 设置两个普通属性 -->
<property name="ename" value="lucy"></property>
<property name="gender" value="girl"></property>
<!-- 设置对象类型属性 -->
<property name="dept" ref="dept"></property>
<property name="dept.dname" value="安保部"></property>
</bean>
<bean id="dept" class="com.atguigu.spring5.bean.Dept"></bean>

IOC 操作 Bean 管理(xml 注入集合属性)

1. 注入数组类型属性

2. 注入 List 集合类型属性

3. 注入 Map 集合类型属性

4. 注入 Set 集合类型属性

Stu

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Stu {
// 1 array 类型属性
private String[] courses;
// 2 list 类型属性
private List<String> list;
// 3 map 类型属性
private Map<String,String> maps;
// 4 set 类型属性
private Set<String> sets;

// setXXX() ...

}

spring 配置文件

1
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
<bean id="stu" class="com.atguigu.spring5.collectiontype.Stu">
<!-- array 类型属性注入 -->
<property name="courses">
<array>
<value>Java</value>
<value>Python</value>
</array>
</property>
<!-- list 类型属性注入 -->
<property name="list">
<list>
<value>Java</value>
<value>Python</value>
</list>
</property>
<!-- map 类型属性注入 -->
<property name="maps">
<map>
<entry key="java" value="Java"></entry>
<entry key="python" value="Python"></entry>
</map>
</property>
<!-- set 类型属性注入 -->
<property name="sets">
<set>
<value>Java</value>
<value>Redis</value>
</set>
</property>
</bean>

5. 在集合里面设置对象类型值

Stu 基础上增加代码

1
2
3
4
5
public class Stu {
// more code ...
private List<Course> courseList;
// setCourseList() ...
}

Course

1
2
3
4
public class Course {
private String cname;
// setCname() ...
}

spring 配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<bean id="stu" class="com.atguigu.spring5.collectiontype.Stu">
<!-- list 类型属性注入 对象类型 -->
<property name="courseList">
<list>
<ref bean="course1"></ref>
<ref bean="course2"></ref>
</list>
</property>
</bean>
<bean id="course1" class="com.atguigu.spring5.collectiontype.Course">
<property name="cname" value="Java"></property>
</bean>
<bean id="course2" class="com.atguigu.spring5.collectiontype.Course">
<property name="cname" value="Python"></property>
</bean>

6. 把集合注入部分提取出来

Step 01 在 spring 配置文件中引入命名空间 util

image-20220517093718982

1
2
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"

Step 02 使用 util 标签完成 list 集合注入

Book

1
2
3
4
public class Book {
private List<String> list;
// setList() ...
}

spring 配置文件

1
2
3
4
5
6
7
8
9
<util:list id="bookList">
<value>易筋经</value>
<value>九阴真经</value>
<value>九阳神功</value>
</util:list>

<bean id="book" class="com.atguigu.spring5.collectiontype.Book">
<property name="list" ref="bookList"></property>
</bean>

IOC 操作 Bean 管理(FactoryBean)

P15

1. Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)

  1. 普通 bean:在配置文件定义 bean 类型就是返回类型;
  2. 工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样(用于创建复杂 bean 对象)。

Step 01 创建类,让这个类作为工厂 bean,实现接口 FactoryBean

Step 02 实现接口里面的方法,在实现的方法中定义返回的 bean 类型

MyBean

1
2
3
4
5
6
7
8
9
public class MyBean implements FactoryBean<Course> {
@Override
public Course getObject() throws Exception {
Course course = new Course();
course.setCname("abc");
return course;
}
// more @Override ...
}

spring 配置文件

1
<bean id="myBean" class="com.atguigu.spring5.factorybean.MyBean"></bean>

测试

1
2
3
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
Course course = context.getBean("myBean",Course.class);
System.out.println(course);

IOC 操作 Bean 管理(bean 作用域)

在 Spring 里面,默认情况下 bean 是单实例对象(单例)

1. 如何设置单实例还是多实例

在 spring 配置文件 bean 标签里面有属性 scope 用于设置单实例还是多实例:

  • singleton:默认值,单实例;
  • prototype:多实例;
  • request:将对象放到 request 中;
  • session:将对象放到 session 中。

常用:singleton 和 prototype;不常用:request 和 session,若使用建议先查阅相关资料。

1
2
3
<bean id="book" class="com.atguigu.spring5.collectiontype.Book" scope="prototype">
<!-- more code ... -->
</bean>
singleton 和 prototype 区别
  1. singleton 单实例,prototype 多实例;
  2. 设置 scope 值是 singleton 时,加载 spring 配置文件时就会创建实例对象;设置 scope 值是 prototype 时,在调用 getBean() 方法时创建多实例对象。

IOC 操作 Bean 管理(bean 生命周期)

P17

1. 生命周期

从对象创建到对象销毁的过程。

2. bean 生命周期(简化版)

  1. 通过构造器创建 bean 实例(执行无参构造方法);
  2. 为 bean 的属性设置值核对其他 bean 引用(调用 set 方法);
  3. 调用 bean 的初始化方法(需要开发者配置初始化方法);
  4. bean 可以使用了(对象获取到了);
  5. 当容器关闭时,调用 bean 的销毁方法(需要开发者配置销毁方法)。

这里只是简单的介绍了一下 bean 生命周期,技术细节还请查阅专业的技术文档。

eg

Order

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Orders {
private String orderName;

public Orders() {
System.out.println("第一步:执行无参构造方法创建bean实例");
}

public void setOrderName(String orderName) {
this.orderName = orderName;
System.out.println("第二步:调用set方法设置属性值");
}

//初始化方法
public void initMethod(){
System.out.println("第四步:执行初始化方法");
}

//销毁方法
public void destroyMethod(){
System.out.println("第七步:执行销毁方法");
}
}

spring 配置文件

1
2
3
4
5
6
<bean id="orders" class="com.oymn.spring5.Orders" init-method="initMethod" destroy-method="destroyMethod">
<property name="orderName" value="hahah"></property>
</bean>

<!--配置bean后置处理器,这样配置后整个xml里面的bean用的都是这个后置处理器-->
<bean id="myBeanPost" class="com.oymn.spring5.MyBeanPost"></bean>

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void testOrders(){

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");

Orders orders = context.getBean("orders", Orders.class);

System.out.println("第六步:获取bean实例对象");
System.out.println(orders);

//手动让bean实例销毁
context.close();
}

3. 后置处理器

MyBeanPost 后置处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//实现后置处理器,需要实现BeanPostProcessor接口
public class MyBeanPost implements BeanPostProcessor {

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("第三步:将bean实例传递给bean后置处理器的postProcessBeforeInitialization方法");
return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("第五步:将bean实例传递给bean后置处理器的postProcessAfterInitialization方法");
return bean;
}
}

spring 配置文件

配置bean后置处理器,这样配置后整个xml里面的bean用的都是这个后置处理器。

1
2
3
4
5
6
<bean id="orders" class="com.oymn.spring5.Orders" init-method="initMethod" destroy-method="destroyMethod">
<property name="orderName" value="hahah"></property>
</bean>

<!--配置bean后置处理器,这样配置后整个xml里面的bean用的都是这个后置处理器-->
<bean id="myBeanPost" class="com.oymn.spring5.MyBeanPost"></bean>

IOC 操作 Bean 管理(xml 自动装配)

P18

1. 什么是自动装配

根据指定装配规则(名称或者类型),Spring 自动将匹配的属性值进行注入。

2. 演示自动装配过程

bean 标签属性 autowire,配置自动装配。autowire 属性常用两个值:

  • byName:根据属性名称注入;
  • byType:根据属性类型注入。
1. 根据属性名称自动注入

根据属性名称自动装配:要求 emp中属性的名称dept 和 bean标签的id值dept 一样,才能识别。

1
2
3
4
<!--指定autowire属性值为byName-->
<bean id="emp" class="com.oymn.spring5.Emp" autowire="byName"></bean>

<bean id="dept" class="com.oymn.spring5.Dept"></bean>
2. 根据属性类型自动注入

根据属性类型自动装配:要求同一个xml文件中不能有两个相同类型的bean,否则无法识别是哪一个。

1
2
3
4
<!--指定autowire属性值为byType-->
<bean id="emp" class="com.oymn.spring5.Emp" autowire="byType"></bean>

<bean id="dept" class="com.oymn.spring5.Dept"></bean>

IOC 操作 Bean 管理(外部属性文件)

P19

eg 配置德鲁伊(druid)连接池

1. 直接配置数据库信息

1
2
3
4
5
6
7
<!-- 直接配置连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/userDB"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>

2. 引入外部属性文件配置数据库连接池

Step 00 编写外部属性文件 和 引入德鲁伊(druid)连接池依赖 jar 包

jdbc.properties 外部属性文件

1
2
3
4
prop.driverClass=com.mysql.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/userDB
prop.userName=root
prop.password=root

Step 01 在 spring 配置文件中引入命名空间 context

image-20220517172345376

1
2
xmlns:util="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/util/spring-context.xsd"

Step 02 在 spring 配置文件使用标签引入外部属性文件

1
2
3
4
5
6
7
8
9
<!-- 引入外部属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prop.driverClass}"></property>
<property name="url" value="${prop.url}"></property>
<property name="username" value="${prop.userName}"></property>
<property name="password" value="${prop.password}"></property>
</bean>
CATALOG
  1. 1. IOC 操作 Bean 管理
    1. 1.1. 1. 什么是 Bean 管理(两个操作):
    2. 1.2. 2. Bean 管理操作有两种方式
  2. 2. IOC 操作 Bean 管理(基于 xml 方式)
    1. 2.1. 1. 基于 xml 方式创建对象
    2. 2.2. 2. 基于 xml 方式注入属性
    3. 2.3. 3. 使用 set方法 注入属性
    4. 2.4. 4. 使用 有参构造方法 注入属性
    5. 2.5. 5. 使用 p命名空间 注入属性
  3. 3. IOC 操作 Bean 管理(xml 注入其他类型属性)
    1. 3.1. 1. 字面量
    2. 3.2. 2. 注入属性 - 外部 bean
    3. 3.3. 3. 注入属性 - 内部 bean
    4. 3.4. 4. 注入属性 - 级联赋值
  4. 4. IOC 操作 Bean 管理(xml 注入集合属性)
    1. 4.1. 1. 注入数组类型属性
    2. 4.2. 2. 注入 List 集合类型属性
    3. 4.3. 3. 注入 Map 集合类型属性
    4. 4.4. 4. 注入 Set 集合类型属性
    5. 4.5. 5. 在集合里面设置对象类型值
    6. 4.6. 6. 把集合注入部分提取出来
  5. 5. IOC 操作 Bean 管理(FactoryBean)
    1. 5.1. 1. Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)
  6. 6. IOC 操作 Bean 管理(bean 作用域)
    1. 6.1. 1. 如何设置单实例还是多实例
      1. 6.1.1. singleton 和 prototype 区别
  7. 7. IOC 操作 Bean 管理(bean 生命周期)
    1. 7.1. 1. 生命周期
    2. 7.2. 2. bean 生命周期(简化版)
    3. 7.3. 3. 后置处理器
  8. 8. IOC 操作 Bean 管理(xml 自动装配)
    1. 8.1. 1. 什么是自动装配
    2. 8.2. 2. 演示自动装配过程
      1. 8.2.1. 1. 根据属性名称自动注入
      2. 8.2.2. 2. 根据属性类型自动注入
  9. 9. IOC 操作 Bean 管理(外部属性文件)
    1. 9.1. 1. 直接配置数据库信息
    2. 9.2. 2. 引入外部属性文件配置数据库连接池