一、简介
Spring Bean 的生命周期在整个 Spring 中占有很重要的位置,从BeanFactory或ApplicationContext取得的实例为Singleton,也就是预设为每一个Bean的别名只能维持一个实例,而不是每次都产生一个新的对象使用Singleton模式产生单一实例,在spring中,singleton属性默认是true,只有设定为false,则每次指定别名取得的Bean时都会产生一个新的实例,Spring只帮我们管理单例模式Bean的完整生命周期,对于prototype的bean,Spring在创建好交给使用者之后则不会再管理后续的生命周期。
二、图例
生命周期图如下:
也可以概括为:
三、图例说明
1、实例化一个Bean
2、按照Spring上下文对实例化的Bean进行配置,也就是IOC注入
3、如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String)方法,传递的参数就是Spring配置文件中Bean的id值
4、如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(BeanFactory),传递的是Spring工厂自身
5、如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文
6、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,
BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用那个的方法,也可以被应用于内存或缓存技术;
7、如果Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。
8、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法
9、当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用那个其实现的destroy()方法;
10、最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。
什么是 Spring Bean
这是一个非常简单而又很复杂的问题,通常来说,Spring beans 就是被 Spring 容器所管理的 Java 对象,来看一个简单的例子
package com.zoltanraffai;
public class HelloWorld {
private String message;
public void setMessage(String message){
this.message = message;
}
public void getMessage(){
System.out.println("My Message : " + message);
}
}
在基于 XML 的配置中, beans.xml 为 Spring 容器管理 bean 提供元数据
什么是 Spring 容器
Spring 容器负责实例化,配置和装配 Spring beans,下面来看如何为 IoC 容器配置我们的 HelloWorld POJO
<?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-3.0.xsd">
<bean id = "helloWorld" class = "com.zoltanraffai.HelloWorld">
<property name = "message" value = "Hello World!"/>
</bean>
</beans>
现在,它已经被 Spring 容器管理了,接下来的问题是:我们怎样获取它?
BeanFactory 和 ApplicationContext 的不同点
BeanFactory 接口
这是一个用来访问 Spring 容器的 root 接口,要访问 Spring 容器,我们将使用 Spring 依赖注入功能,使用 BeanFactory 接口和它的子接口特性:
- Bean 的实例化/串联
通常情况,BeanFactory 的实现是使用懒加载的方式,这意味着 beans 只有在我们通过 getBean() 方法直接调用它们时才进行实例化实现 BeanFactory 最常用的 API 是 XMLBeanFactory这里是如何通过 BeanFactory 获取一个 bean 的例子:
package com.zoltanraffai;
import org.springframework.core.io.ClassPathResource;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.xml.XmlBeanFactory;
public class HelloWorldApp{
public static void main(String[] args) {
XmlBeanFactory factory = new XmlBeanFactory (new ClassPathResource("beans.xml"));
HelloWorld obj = (HelloWorld) factory.getBean("helloWorld");
obj.getMessage();
}
}
ApplicationContext 接口
ApplicationContext 是 Spring 应用程序中的中央接口,用于向应用程序提供配置信息它继承了 BeanFactory 接口,所以 ApplicationContext 包含 BeanFactory 的所有功能以及更多功能!它的主要功能是支持大型的业务应用的创建特性:
- Bean instantiation/wiring
- Bean 的实例化/串联
- 自动的 BeanPostProcessor 注册
- 自动的 BeanFactoryPostProcessor 注册
- 方便的 MessageSource 访问(i18n)
- ApplicationEvent 的发布
与 BeanFactory 懒加载的方式不同,它是预加载,所以,每一个 bean 都在 ApplicationContext 启动之后实例化这里是 ApplicationContext 的使用例子:
package com.zoltanraffai;
import org.springframework.core.io.ClassPathResource;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.xml.XmlBeanFactory;
public class HelloWorldApp{
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml");
HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
obj.getMessage();
}
}
总结
ApplicationContext 包含 BeanFactory 的所有特性,通常推荐使用前者。但是也有一些限制情形,比如移动应用内存消耗比较严苛,在那些情景中,使用更轻量级的 BeanFactory 是更合理的。然而,在大多数企业级的应用中,ApplicationContext 是你的首选。
Spring Bean 生命周期之“我从哪里来”?懂得这个很重要
Spring bean 的生命周期很容易理解。实例化 bean 时,可能需要执行一些初始化以使其进入可用 (Ready for Use)状态。类似地,当不再需要 bean 并将其从容器中移除时,可能需要进行一些清理,这就是它的生命周期
上一篇文章 面试还不知道BeanFactory和ApplicationContext的区别? T getBean(String name, Class<T> requiredType)
方法从 Spring 容器中获取bean,区别是,前者是懒加载形式,后者是预加载的形式。那么问题来了:
这些 Spring Beans 是怎么生成出来的呢?
在正式回答这个问题之前,先解答一些有关 Java Bean, Spring Bean 和 Spring IoC 容器这些概念性的疑惑,我希望通过下面这个例子形象说明这些问题:
小学生 (Java Bean)通过提交资料申请(元数据配置)加入了少先队(Spring Ioc 容器),学习了一些精神与规定之后,变成了少先队员(Spring Bean)
从这里可以看出,Java Bean 和 Spring Bean 都是具有特定功能的对象,小学生还是那个小学生,只不过加入了少先队之后有了新的身份,新的身份要按照组织 (Spring Ioc)的规定履行特定义务
来看下图加深一下了解
首先要有容器,实例化 Spring Ioc 容器是非常简单的,接口 org.springframework.context.ApplicationContext
表示Spring IoC容器,负责实例化,配置和组装上述 bean。容器通过读取配置元数据获取有关要实例化,配置和组装的对象的指令。配置元数据通常以XML,Java 注解或代码的形式表示。它允许你自己表达组成应用程序的对象以及这些对象之间丰富的相互依赖性,比如这样:
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"spring.xml", "spring1.xml"});
有了容器,我们需要做哪些处理,使其内部对象变为
Ready for Use
我们需要通过 Spring 容器实例化它们,Spring 为我们提供了三种方式:
三种初始化方式
InitializingBean
Spring 为我们提供了 InitializingBean
接口
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
我们可以通过实现InitializingBean
afterPropertiesSet
内完成实例化的工作,但是 Spring Framework 官方并不建议我们通过这种方法来完成 Bean 的实例化,这是一种强耦合的方式,我们看到框架层面才会用到这个方法。
@PostConstruct
这种方式是 Spring 非常提倡的一种方式,我们通常将其标记在方法上即可,通常习惯将这个方法起名为 init()
@PostConstruct
public void init() {
System.out.println("Inside init() method...");
}
init-method
你应该见过这种初始化方式:
public class MyClass {
public void init() {
// perform post-creation logic here
}
}
@Configuration
public class AppConfig {
@Bean(initMethod = "init")
public MyClass myclass() {
return new MyClass ();
}
}
你也应该见过这种配置方式:
<bean id="myClass" class="com.demo.MyClass" init-method="init"/>
没错,这只是同样功能的不同实现方式罢了以上就是三种初始化 Spring Beans 的方式,我们在框架中看到过三种方式在组合使用,那么组合使用的调用顺序是什么呢?
- 首先@PostConstruct 会被最先调用
- 其次
InitializingBean.afterPropertiesSet()
方法将会被调用 - 最后调用通过 XML 配置的 init-method 方法或通过设置 @Bean 注解 设置 initMethod 属性的方法
了解了这些,你也就了解了 Spring Bean 是怎么来的了
通过图示来说明一下:
组合使用,这个调用顺序很难记忆吗吗?
PostConstruct
(P)
,afterPropertiesSet(A)
,init-method(I)
PAI (圆周率π)
BeanPostProcessor
BeanPostProcessor 接口,大家也应该有印象,里面只有两个方法:
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object var1, String var2) throws BeansException;
Object postProcessAfterInitialization(Object var1, String var2) throws BeansException;
}
看方法名,BeforeInitialization 和 AfterInitialization,我们应该猜得出,这是在上述三种方式的前和后,算是一种全局的切面思想,我们经常会使用 postProcessAfterInitialization
方法,通过读取 Bean 的注解完成一些后续逻辑编写与属性的设定,现在 Ready for Use
之前是这样:
在 Ready for Use
摘抄自网络,便于检索查找。