¶Ioc 概览
¶Bean 属性定制:
Spring 框架提供了一系列用于定制 Bean 的接口, 主要是 Bean 生命周期相关的回调方法( Lifecycle Callbacks )
¶生命周期相关的回调方法:
为了和Spring 对Bean 的管理相互影响,可以通过实现 InitializingBean 和 DisposableBean 接口,分别定义 Bean 的 afterPropertiesSet() 和 desyory() 方法,指定在Bean 属性设置完成和 Bean 销毁时的动作
在Spring 的内部,是通过接口 BeanPostProcessor 的实现类执行相关回调方法的,因此如果你想实现任何 Spring 没有提供的生命周期相关的动作,可以通过实现 BeanPostProcessor 接口来完成。
初始化回调
afterPropertiesSet() :在Bean 所有必须的属性设置完成之后进行一些其他的初始化工作;
但是并不推荐继承 InitializingBean 接口来实现这一回调方法,因为会和Spring 的代码相耦合;建议通过 @PostConstruct 注解 , 或者指定对象的初始化方法
1
2
3
4
5
6
7
8<bean id = "exampleInitBean" class = "examples.ExampleBaen" init-method = "init">
// xml 格式的配置元数据,指定 init-method 方法,作为初始化回调
public class ExcepleBane{
public void init{
//初始化回调
}
}
生命周期结束回调
destory() : Bean 销毁方法
同样为了避免和 Spring 代码过度耦合,建议使用 @PreDestory 注解,或者在配置元数据中指定销毁方法,在
中指定 destory-method 指向的方法作为 Bean 生命周期销毁时的回调。 1
2
3
4
5<bean id ="exampleInitBean" class="examples.ExampleBaen" destory-method="cleanup">
//指定 destory-method 方法作为生命周期结束回调
public void cleanup(){
//销毁动作
}
默认的Bean初始化和销毁回调方法
在约定俗成的情况下,人们通常会将初始化方法和销毁方法命名为类似 *init(),initialize(),dispose()*等,理想情况下这些命名方式是跨项目的规范,因此所有开发者都会遵守并保证它们的一致性(方法用途?)。
你可以配置Spring 容器在每一个Bean中查找 标准化 的初始化和销毁方法,这意味着,你可以在不进行任何配置(例如在配置元数据指定 init-method )的情况下,将一个命名为 init() 的方法作为 Bean 初始化方法使用,IOC容器会在Bean 创建之后调用它。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class DefaultBlogServie implements BolgService{
private BlogDao blogDao;
public void setBlogDao(BlogDao blogDao){
//属性注入
this.blogDao = blogDao;
}
public void init(){
//会作为默认的初始化方法,检查属性设置之类的
if(this.blogDao == null){
throw new IllegalStateException("The [blogDao] property must be set");
}
}
}在元数据中这样定义:
1
2
3
4
5
6<beans default-init-method="init">
<!-- 区别在于没有在 bean 定义本身去指定销毁方法,等于是使用了全部配置;也可以由此延伸出,如果定义了特定的销毁方法,将会覆盖全局设置 -->
<bean id= "blogService" class = "com.somthing.DefaultBlogService">
<property name = "blogDao" reg = "blogDao">
</bean>
</beans>标签中的 default-init-method 会让 Spring 容器将下面所有Bean 中命名为 init() 的方法作为初始化方法,在适当的时候被反射调用 (default-destroy-method 作用类似于 default-init-method ) 在上面一点的前提下,也可以指定具体Bean 的初始化和销毁方法,覆盖全局设置
Spring 容器会保证配置的初始化回调,会在Bean的所有依赖都被满足后立刻被调用。这意味着初始化回调(init 方法)是在原生Bean 引用上进行的,发生在 AOP 等动作之前。目标 Bean 对象完全创建出来之后,AOP拦截器调用链才会开始应用。
如果Bean 的定义和代理定义相分离,你甚至可以通过代理调用原生Bean 对象。(不理解)
因此,将拦截器放置在初始化方法中是不稳妥的,这样会将拦截器或者代理的代码和Bean的生命周期耦合起来,当你直接操作原生Bean 对象时,还会产生奇怪的语义。(不理解)
结合Spring 生命周期机制 :
Spring 2.5版本之后,可以有三种途径控制Bean生命周期:
InitializingBean
和DisposableBaen
回调接口- 定制
init()
和destory()
方法 @PostConstruct
和@PreDestory
注解
如果同时配置了使用了多个Bean生命周期控制的方法,当它们的方法名称不一样时,初始化(init)方法和销毁方法都会按照一定的顺序执行:
初始化生命周期方法执行顺序 :
@PostConstruct 注解修饰的方法 > InitializationBean.afterPropertiesSet()方法 > 自定义的 init() 方法 (参照前文)
生命周期结束方法执行顺序:
@PreDestory() 注解方法 > DisposableBean.destory() > 自定义的 destory() 方法
如果配置的生命周期方法名称一样,则配置的初始化方法和生命周期结束方法仅会执行一次
启动停止回调:
Lifecycle
接口定义了任意对象所必须的满足生命周期的要求的方法(启停方法和状态反馈等)所有受到Spring 容器管理的对象都实现了Lifecycle接口,以
ApplicationContext
为例,当它接收到启动或者停止信号( 例如运行时接收到的 stop/restart 信号 ),它会级联所有定义在当前context 中的Lifecycle接口实现对象,进行同样的方法调用。这个动作会委派给LifecycleProcessor
接口实现:LifecycleProcessor 接口定义如下:
1
2
3
4public interface LifecycleProcessor extends Lifecycle{
void onRefresh();
void onClose();
}这里看不出它是如何进行级联其他定义在当前context中的对象的方法进行调用的,可以看看它的默认实现类:
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63public class DefaultLifecycleProcessor implements LifecycleProcessor,BeanFactoryAuare{
public void start() {
//这个方法有没有可能是进行Bean对象级联启动的方法?
startBeans(false);
this.running = true;
}
//startBeans(Boolean)
private void startBeans(boolean autoStartupOnly) {
//从方法名看起来像是获取所有具备生命周期Bean对象的方法
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
Map<Integer, LifecycleGroup> phases = new HashMap<>();
lifecycleBeans.forEach((beanName, bean) -> {
//设置的不自动重启,判断了Bean 的类型,以及当 SmartLifeCycle 类型的 Bean 是否需要根据容器的刷新而重启
if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {
int phase = getPhase(bean);
LifecycleGroup group = phases.get(phase);
if (group == null) {
group = new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly);
phases.put(phase, group);
}
group.add(beanName, bean);
}
});
if (!phases.isEmpty()) {
List<Integer> keys = new ArrayList<>(phases.keySet());
Collections.sort(keys);
for (Integer key : keys) {
phases.get(key).start();
}
}
}
//getLifecycleBeans
protected Map<String, Lifecycle> getLifecycleBeans() {
//获取了在DefaultLifecycleProcessor 注入的 beanFactory 对象,将ConfigurableListableBeanFactory的子类强转
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
Map<String, Lifecycle> beans = new LinkedHashMap<>();
//获取指定类型的Bean的名称,这里获取了所有具备生命周期方法实现的Bean对象的名称
String[] beanNames = beanFactory.getBeanNamesForType(Lifecycle.class, false, false);
for (String beanName : beanNames) {
//获取真正的bean对象的名称,剥离多余的前缀信息
String beanNameToRegister = BeanFactoryUtils.transformedBeanName(beanName);
boolean isFactoryBean = beanFactory.isFactoryBean(beanNameToRegister);
String beanNameToCheck = (isFactoryBean ? BeanFactory.FACTORY_BEAN_PREFIX + beanName : beanName);
//判断是否是单例Bean对象,是否是实现了FactoryBean等
if ((beanFactory.containsSingleton(beanNameToRegister) &&
(!isFactoryBean || matchesBeanType(Lifecycle.class, beanNameToCheck, beanFactory))) ||
matchesBeanType(SmartLifecycle.class, beanNameToCheck, beanFactory)) {
//根据名称获取Bean对象
Object bean = beanFactory.getBean(beanNameToCheck);
if (bean != this && bean instanceof Lifecycle) {
//保存所有获取到的符合条件的Bean对象
beans.put(beanNameToRegister, (Lifecycle) bean);
}
}
}
return beans;
//(这个流程很类似于读取配置元数据然后保存Bean信息)
}
}到这里可以比较明了地看到,它是如何级联所有继承了Lifecycle 接口Bean进行生命周期方法触发的
可以注意到 LifecycleProcessor 也是继承了 Lifecycle 接口的, 并且新增了额外的 onRefresh() 和 onClose() 方法响应容器的刷新和关闭
注意 Lifecycle 接口只是简单的进行显性启动,停止通知的接口,再容器刷新的时候它并不会实现自动刷新。因此如果需要在Bean启动时进行更加精细的控制,考虑实现 SmartLifecycle 接口代替。
同时,需要注意停止通知并不保证会发生在销毁动作之前。在常规的停止操作中,所有实现了 Lifecycle 的Bean 对象会在通用销毁回调方法执行之间收到停止信号。然而,在容器生命周期活动范围内的热更新或者试图放弃更新的操作,都会导致Baen仅仅调用 destory() 方法进行销毁
依赖对象之间的启动和停止顺序可以很重要的,如果两个对象之间存在依赖关系,那么毫无疑问,依赖方会在它依赖的对象启动之后启动,且会在它的依赖方停止之前停止。然而,有时候它们彼此之间的直接依赖关系是不明确的,你可能只知道某些指定类型的Bean对象需要在另一些指定类型的Bean对象之前启动。在这种情况下,接口 SmartLifecycle 给定了另一个选项 –
getPhase()
方法 – 返回 Bean 对象启动阶段类似的指标1
2
3
4public interface SmartLifecycle extends Lifecycle ,Phased{
boolean isAutoStartup();
void stop(Runnable callback);
}容器当启动时,启动阶段优先级最低的对象最先启动。容器停止时,按照启动阶段优先级倒序进行停止操作。因此,实现了 SmartLifercycle 接口并且
getPhase()
接口返回 Integer.MAX_VALUE 的对象将会第一个启动且最后一个停止。同时也表明了,任意没有实现 SmartLifecycle 接口的Bean对象,返回的启动阶段值肯定是0 – 即最后一个启动,第一个停止。因此,负值的启动阶段优先级,表明当前对象会在标准组件启动之后启动,并在它们停止之前停止,同样适用于正值的启动优先级值。接口 SmartLifecycle 定义的 stop 方法接收一个回调动作。任意实现该接口的类都需要在
shutdown()
方法调用完成之后调用run()
回调方法。//TODO (没看懂)像之前提到的那样,
LifecycleProcessor
接口定义了响应上下文 刷新和关闭的 的回调方法。后者(上下文关闭)就像是明确调用了容器的stop()
方法。
ApplicationContextAware 和 BeanNameAware 接口
当
ApplicationContext
容器创建了一个实现了org.springframework.context.ApplicationContextAware
接口的对象实例,该实例将会持有指向该容器的引用,接口代码如下:1
2
3public interface ApplicationContextAware{
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}因此,这些 bean 对象可以通过代码区操作创建它们的容器对象,或者通过将接口前强转成该容器的子类进行使用。其中一个用途就是借此检索其它的 Bean 对象。但是建议不要这么做,因为这样会将自己的代码和Spring 容器耦合得比较紧密,而且违反了 IoC 风格。
在 Spring 2.5 版本,自动注入是另一种可以获得 ApplicationContext 容器引用的方法。传统的构造注入和类型注入模式可以分别为构造函数和setter方法提供容器依赖作为参数使用。开启基于注解的依赖注入模式,通过
@Autowired
注解可以更加灵活地实现将 ApplicationContext 作为字段,构造参数或者方法参数等进行依赖注入。当 ApplicationContext 容器创建一个实现了 org.apringframework.baens.factory.BeanNameAware 接口的类,该类会持有自身作为 Bean被定义的名称的引用。如下所示:
1
2
3public interface BeanNameAware{
void setBeanName(String name) throws BeansException;
}这个回调方法会在普通Bean对象的特性设置完成之后,Bean初始化方法调用之前调用,例如在 InitialicationBean ,afterPropertiesSet 或者自定义的初始化方法之前。
其它注入相关接口:
ApplicationContextAware
:会注入 ApplicationContext 的依赖ApplicationEventPublisherAware
: 注入可封闭的 ApplicationContext 容器的事件发布者依赖BeanClassLoaderAware
: 注入加载当前bean 的类加载器BeanFactoryAware
: 注入定义的 BeanFactoryBeanNameAware
: 注入声明的Bean 的名称引用ResourceLoaderAware
: 定义的加载外部配置文件的加载器ServletContextAware
: 当前容器运行的 ServletContext ,仅在web 类型容器上下文生效
需要注意,实现这些接口去访问Spring 中的对象,会使代码耦合,因此建议仅在必要的时候使用,例如自己定义的作为基础Bean 对象使用的情况下。
小结
- 为 Bean 对象指定初始化和销毁等生命周期方法,可以在Bean 定义上配置init-method 和 destory-method ,容器会根据其中配置的方法名去查找该对象的初始化和销毁方法在合适的时候进行调用
- 也可以在 Beans 标签中为其内所有的 Bean 对象指定默认初始化和销毁的生命周期方法,注意这个配置是可以被 Bean 自身的配置覆盖的。
- 在 Spring 2.5 版本之后可以通过 @PostConstruct 注解,实现 InitializationBean.afterPropertiesSet()方法 , 自定义的 init() 方法 控制 Bean 初始化生命周期,通过 @PreDestory() 注解, 实现 DisposableBean.destory() , 自定义的 destory() 方法控制Bean 的销毁生命周期 , 它们的执行顺序也正如描述所示,但是需要注意 : 如果同时实现了三个不同名称的生命周期方法(多个初始化或销毁),则三个方法都会执行;如果定义了相同名称的生命周期方法,则按照优先级顺序,仅会执行其中一个。
- 所有受 Spring 容器管理的对象都会继承
Lifecycle
接口。然后当ApplicationContext
自身接收到了启停信号,它会级联所有实现了Lifecycle
接口的对象,并调用这些对象的启停生命周期方法。