Spring IOC 容器
Spring 容器初始化与 Bean 注入
上文 中的案例, 通过配置文件 spring.xml 配置了对象。 容器根据配置文件创建了对象,我们可以从容器中直接获取到这个对象,而不用手动创建。 如果对象属性很简单,用容器生成好像更复杂了。但是当对象很复杂,甚至引用了别处的对象,我们就不得不多次创建对象并赋值来建立多个对象之间的关系。这时,容器的优点就凸显出来: 定义一次配置文件,然后在任何地方直接从容器中获取这个对象即可。另外,利用容器创建对象,对象的生命周期直接交付给容器管理,降低了管理难度。
# DI 和 IOC 解释
- IOC(Inversion of Controle): 反转控制 控制权反转,传统从容器中获取资源的方式,都是组件主动向容器申请。控制权反转后,改由容器主动将资源推送给需要的组件,开发者只需要提供接受资源的方式即可。
- DI(Dependency Injection): 依赖注入 IOC的一种具体表述方式,即组件以一些预先定义好的方式 (e.g:setter) 接受来自容器的资源注入。
IOC 是一种思想,而 DI 是一种具体的实现。
# Spring 中的容器
IOC容器的底层实质上是一个对象工厂, Spring 提供了两种实现容器的接口
- BeanFactory: Spring 自用IOC。
- ApplicationContext: BeanFactory 的子接口,供开发者使用。
- 容器继承树:
┌───────────────┐
│ BeanFactory │
└──────┬────────┘
│
│
│
┌────────▼───────────┐
┌─────────────────┤ ApplicationContext │
│ └───┬─────────┬──────┘
│ │ │
┌──────────┴────◄──────────────┐ │ │
│ConfigurableApplicationContext│ │ │
└──────────────────────────────┘ │ │ Interface
│ │
────────────────────────────────┼─────────┼───────────────────────────
│ │
│ │ Class
│ │
│ │
┌──────────────────────────▼───┐ ┌───▼───────────────────────────┐
│ClassPathXmlApplicationContext│ │FileSystemXmlApplicationContext│
└──────────────────────────────┘ └───────────────────────────────┘
---> ConfigurableApplicationContext 接口: ApplicationContext 拓展接口,新增了
refresh()、close() 等功能。
---> ClassPathXmlApplicationContext: 根据类路径下 Xml 文件创建容器环境。
---> FilesSystemXmlApplilcationContext: 根据文件系统中 Xml 文件创建容器环境。
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
# 从容器中获取资源的两种方式
ApplicationContext ioc
- 通过 id 获取
Student stu = (Student)ioc.getBean("student");
- 通过类型获取
Student stu = (Student)ioc.getBeanById(Student.class);
Student stu = (Student)ioc.getBean("student", Student.class);
2
通过类型获取要保证容器中该类型的实例只有一个,否则会抛异常。若一个类型有多个实例,只能通过 id 来获取。
# 给 Bean 属性赋值
# 不同的赋值方式
- 通过 Bean 的 setter 方法赋值 : 通过下面的赋值方式,需要 Bean 提供 setAge() 方法。
<bean id="student" class="Student">
<property name="age" value="22">
</bean>
2
3
- 通过 Bean 的 constructor 方法赋值 : 需要 Bean 构造器属性为 public .
// 按照参数位置索引指定
<bean id="student" class="Student">
<constructor-arg value="22" index="0">
</bean>
// 按照类型自动装载参数
<bean id="student" class="Student">
<constructor-arg value="22" type="java.lang.Integer">
</bean>
2
3
4
5
6
7
8
9
- p名称空间 : 行内简化写法
<bean id="stu" class="Student" p:age="22" />
# 各种类型的赋值
给 Bean 的不同类型成员属性赋值
- 字面量 : 表示字符串
<bean id="student" class="Student" p:name="randy" />
- null :
<bean id="student" class="Student" p:name="#{null}" />
- Bean 级联属性赋值
给 Bean 引用的对象直接赋值并装载
<bean id="student" class="Student">
<property name="birth.year" value="2000">
</bean>
2
3
- 引用外部声明的 bean
<bean name="date" class="Date">
<bean name="birth" ref="date">
2
- 引用内部定义的 bean
<bean id="student" class="Student">
<property name="birth">
<bean class="Date" p:year="2000">
</property>
</bean>
2
3
4
5
# 集合类型的赋值
- list
<bean class="Student">
<property name="subjects">
<list>
<value>历史<value>
</list>
<property>
<property name="books">
<list>
<ref bean="book1"/>
<ref bean="book2"/>
</list>
</property>
</bean>
2
3
4
5
6
7
8
9
10
11
12
13
- set
<bean>
<property name="subjects">
<set>
<value>Math</value>
<value>Science</value>
</set>
</property>
</bean>
2
3
4
5
6
7
8
- map
<bean>
<property name="score">
<map>
<entry>
<key>
<value>Math</value>
</key>
<ref bean="33" />
</entry>
</map>
</property>
</bean>
2
3
4
5
6
7
8
9
10
11
12
# Bean 的分类
# 普通 bean
从容器中获取这个 Bean,返回的就是 Bean 类型的就是普通 Bean.
# FactoryBean
从容器中获取工厂 Bean,得到的资源是这个工厂 Bean getObject() 返回的 Bean 。 可以通过实现 org.springframework.beans.factory.FactoryBean
来定义一个 FactoryBean.
# Bean 的声命周期
Bean 通过 scope 这个属性来设置 bean 的作用域
<bean class="Student" scope="protorype" />
值 | 说明 |
---|---|
singleton | IOC容器中只有一个 Bean 实例, Bean 以单例模式存在 |
prototype | 每次 getBean() 时都会返回一个新的实例 |
request | 每次 Http 都会创建一个 Bean, 仅适用于 WebApplicationContext 环境 |
session | 同一个 HTTP Session 共享一个 Bean, 仅适用于 WebApplicationContext 环境 |
默认的 scope 是 singleton.
# 引用外部文件赋值
读取 .properties 配置文件中的值为 Bean 赋值
xx.properties:
info.name = randy
spring.xml:
<context:property-placeholder location="classpath:xx.properties"/>
<bean>
<property name="name" value="${info.name}"/>
</bean>
2
3
4
# 自动装配
什么是自动装配 非自动装配,就是上文中通过 xml 配置文件明确指定 bean 的属性值的方式。 自动装配就是不需要手动指定, Spring 将容器中匹配的属性自动注入到 bean 中。其原理就是根据 id 或者类型匹配。
实现自动装配的步骤:
- 在容器中生成对象
- 在指定的位置注入这些对象
# 在容器中生成对象
将一个 Bean 放入到容器中,首先要在 Bean 上加上注解让 Spring 容器来管理这些 Bean.
注解 | 说明 |
---|---|
@Component | 标识一个受 Spring 容器管理的普通组件 |
@Repository | 标识一个受 Spring 容器管理的持久化层组件 |
@Service | 标识一个受 Spring 容器管理的业务逻辑层组件 |
@Controller | 标识一个受 Spring 容器管理的控制器组件 |
加上注解后,第二步将这些组件所在的包写在 spring.xml 中,容器就会扫描这个包中的被注解的组件,并将它们添加到容器中。
<context:component-scan base-package="io.github.human0722" />
这些组件被扫描到容器中之后,会得到一个首字母小写的类名的 bean id,也可以在组件注解中指定 value 属性值自定义 bean id .
# 在指定的位置注入这些对象
在目标属性位置处通过注解获得容器的资源注入
- @Autowired : 根据类型实现自动注入;
@Component
+------------------------+ +--------------------+
@Component | | | |
+--------------------+ | +-------------+ |Inject | |
| | | | |---------------- @Autowired |
| Class Car {} |-----------| | | | public Car car; |
| | | +-------------+ | | |
+--------------------+ | car | | |
| | | |
@Component | | | |
+--------------------+ | +-------------+ | | |
| | | | | | | |
| Class Bike {} |-----------| | | | |
| | | +-------------+ | | |
+--------------------+ | bike | | |
| | | |
+------------------------+ +--------------------+
IOC Container
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
标注了该注解的属性必须被注入,如果无法在容器中找到相匹配的资源,则会抛出异常。也可以通过注解属性 required 设置允许不被设置。
如果容器中存在多个兼容的 bean, Spring 会尝试匹配 bean id 和 属性名相同的资源。也可以通过注解 @Qualifier
指定 bean id;
@Autowired注解也可以应用在数组类型的属性上,此时Spring将会把所有匹配的bean进行自动装配。
@Autowired注解也可以应用在集合属性上,此时Spring读取该集合的类型信息,然后自动装配所有与之兼容的bean。
@Autowired注解用在java.util.Map上时,若该Map的键值为String,那么 Spring将自动装配与值类型兼容的bean作为值,并以bean的id值作为键。
@Resource @Resource 按名称注入,要求提供一个 bean id,如果该属性为空,则会以属性名去容器中寻找匹配的bean.
@Inject @Inject 和 @Autowired 相似,但是没有 required 属性。
# 总结
详细介绍了如何让容器加载并初始化 Bean 到容器中,也总结了如何在需要容器中资源时,通过自动注入的方式获得资源。