7.9.1 依赖关系注入之后的行为

7.9.1 依赖关系注入之后的行为

Spring提供两种方式在Bean全部属性设置成功后执行特定行为。

  • 使用init-method属性
  • 实现InitializingBean接口

第一种方式:使用init-method属性指定某个方法应在Bean全部依赖关系设置结束后自动执行。使用这种方式不需要将代码与Spring的接口耦合在一起,代码污染小。
第二种方式:也可达到同样的效果,就是让Bean类实现InitializingBean接口,该接口提供一个方法:void afterPropertiesSet() throws ExceptionSpring容器会在为该Bean注入依赖关系之后,调用该Bean所实现的afterPropertiesSet()方法。

程序示例

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
E:\workspace_QingLiangJiJavaEEQiYeYingYongShiZhang5\lifecycle-init
└─src\
├─beans.xml
├─lee\
│ └─BeanTest.java
└─org\
└─crazyit\
└─app\
└─service\
├─Axe.java
├─impl\
│ ├─Chinese.java
│ └─SteelAxe.java
└─Person.java

Chinese.java

下面是该Bean实现类的代码。

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package org.crazyit.app.service.impl;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.*;
import org.springframework.context.*;
import org.crazyit.app.service.*;

public class Chinese
implements
Person,
InitializingBean,
BeanNameAware,
ApplicationContextAware
{
private Axe axe;
public void setBeanName(String beanName)
{
System.out.println("===setBeanName===");
}
public void setApplicationContext(ApplicationContext ctx)
{
System.out.println("===setApplicationContext===");
}
public Chinese()
{
System.out.println("Spring实例化主调bean:Chinese实例...");
}
// axe的setter方法
public void setAxe(Axe axe)
{
System.out.println("Spring调用setAxe()执行依赖注入...");
this.axe = axe;
}
public void useAxe()
{
System.out.println(axe.chop());
}
// 测试用的初始化方法
public void init()
{
System.out.println("正在执行初始化方法 init...");
}
// 实现InitializingBean接口必须实现的方法
public void afterPropertiesSet() throws Exception
{
System.out.println("正在执行初始化方法 afterPropertiesSet...");
}
}

上面程序中的粗体字代码定义了一个普通的init方法,实际上这个方法的方法名是任意的,并不定是init0, Spring也不会对这个init()方法进行任何特别的处理。只是配置文件使用init-method属性指定该方法是一个”生命周期方法”。
增加init-method="init"来指定init()方法应在Bean全部属性设置结束后自动执行,如果它不实现InitializingBean接口,上面的Chinese类没有实现任何Spring的接口,只是增加一个普通的init()方法,它依然是一个普通的Java文件,没有代码污染。

实现Initializingbean接口的Bean类必须实现afterPropertiesSet()方法。
除此之外,该实现类还实现了Spring提供的BeanNameAwareApplicationContextAware接口,并实现了这两个接口中定义的setBeanName()setApplicationContext()方法,这样即可观察到Spring容器在创建Bean实例、调用setter方法执行依赖注入、执行完setBeanName()setApplicationContext()方法之后,自动执行Bean的初始化行为(包括init-method指定的方法和afterPropertiesSet()方法)。

beans.xml

下面的配置文件指定org.crazyit.app.service.impl.Chineseinit()方法是一个生命周期方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="GBK"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="steelAxe"
class="org.crazyit.app.service.impl.SteelAxe" />
<!-- 配置chinese Bean,使用init-method="init"
指定该Bean所有属性设置完成后,自动执行init方法 -->
<bean id="chinese" class="org.crazyit.app.service.impl.Chinese"
init-method="init">
<property name="axe" ref="steelAxe" />
</bean>
</beans>

运行结果

1
2
3
4
5
6
7
8
Spring实例化依赖bean:SteelAxe实例...
Spring实例化主调bean:Chinese实例...
Spring调用setAxe()执行依赖注入...
===setBeanName===
===setApplicationContext===
正在执行初始化方法 afterPropertiesSet...
正在执行初始化方法 init...
钢斧砍柴真快

代码分析

通过上面的执行结果可以看出,当SpringsteelAxe注入chinese bean(即完成依赖注入)之后,以及调用了setBeanName()setApplicationContext()方法之后, Spring先调用该BeanafterPropertiesSet()方法进行初始化,再调用init-method属性所指定的方法进行初始化
对于实现InitializingBean接口的Bean,无须使用init-method属性来指定初始化方法,配置该Bean实例与配置普通Bean实例完全一样, Spring容器会自动检测Bean实例是否实现了特定生命周期接口,并由此决定是否需要执行生命周期方法。

不推荐实现InitializingBean接口方式

如果某个Bean类实现了InitializingBean接口,当该Bean的所有依赖关系被设置完成后, Spring容器会自动调用该Bean实例的afterPropertiesSet()方法。其执行结果与采用init-method属性指定生命周期方法几乎一样。但实现InitializingBean接口污染了代码,是侵入式设计,因此不推荐采用。

既使用init-method属性又实现InitializingBean接口时

如果既采用init-method属性指定初始化方法,又实现InitializingBean接口来指定初始化方法, Spring容器会执行两个初始化方法:先执行InitializingBean接口中定义的方法,然后执行init-method属性指定的方法