一、Spring框架中Bean的生命周期
可以分为以下几个主要阶段:
1. 实例化: 当Spring容器启动并读取配置元数据(如XML配置、注解或Java配置类)时,它会创建Bean定义的实例。对于 singleton scope 的Bean,这会在容器启动时发生一次;而对于 prototype scope,每次请求Bean时都会创建新实例。
2. 属性填充: Spring通过反射调用Bean实例的setter方法或使用构造器注入来设置依赖项。这包括注入其他Bean、基本类型的值或集合类型的属性。
3. Aware接口注入: 如果Bean实现了Spring的Aware接口(如BeanNameAware、ApplicationContextAware等),Spring会调用相应的回调方法,允许Bean知道它们所在的容器或它们的Bean名称等信息。
4. 初始化前处理: 如果Bean定义中指定了InitializingBean接口,Spring会调用其afterPropertiesSet()方法。这是在自定义初始化回调之前执行的,可以用来执行一些初始化工作。
5. 自定义初始化方法: 开发者可以通过@PostConstruct注解或在Spring配置中指定init-method属性来定义自定义的初始化方法。Spring会在所有必需的属性设置之后调用这些方法。
6. Bean准备就绪: 至此,Bean已经完全初始化并准备好被应用程序使用。对于 singleton Beans,它们将被放入Spring容器的单例缓存中。
7. 使用Bean: 应用程序可以通过依赖查找或自动装配来使用Bean。
8. 销毁前处理: 如果Bean实现了DisposableBean接口,当容器关闭时,Spring会调用其destroy()方法。此外,也可以使用@PreDestroy注解或在配置中指定destroy-method来执行自定义的销毁逻辑。
9. 销毁: 对于非 singleton scope 的Bean(如 prototype),Spring容器不负责销毁,而是由Java垃圾收集器来处理。对于 singleton scope 的Bean,当Spring容器关闭时,会执行销毁方法,释放资源。
二、Spring Bean生命周期的案例,使用Java配置方式来展示各个阶段
1. 定义一个简单的Bean类,包含初始化和销毁方法:
java
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class ExampleBean {
public ExampleBean() {
System.out.println("2. Bean实例化 - 调用构造函数");
}
// 使用@PostConstruct注解标记初始化方法
@PostConstruct
public void init() {
System.out.println("3. 初始化 - 调用@PostConstruct注解的方法");
}
// 使用@PreDestroy注解标记销毁方法
@PreDestroy
public void cleanup() {
System.out.println("9. 销毁 - 调用@PreDestroy注解的方法");
}
public void sayHello() {
System.out.println("bean内部方法:Hello from ExampleBean!");
}
// 自定义初始化方法,现在看来@PostConstruct注解已经可以满足需求了,但是还是留着自定义初始化方法的例子
private void customInit() {
System.out.println("customInit - 调用customInit");
}
}
2. 创建Spring的Java配置类,配置ExampleBean
java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
// 定义Bean
@Bean(initMethod = "customInit", destroyMethod = "cleanup")
public ExampleBean exampleBean() {
System.out.println("1. Bean实例化 - Spring调用配置方法创建Bean");
return new ExampleBean();
}
}
尽管我们已经在ExampleBean中使用了@PostConstruct和@PreDestroy注解,这里为了演示配置方式,仍然在@Bean注解中指定了初始化和销毁方法。实际上,直接使用注解通常是首选。
3. 编写一个主类来启动Spring容器并使用Bean:
java
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainApp {
public static void main(String[] args) {
System.out.println("0. Spring容器初始化开始");
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println("4. Bean已准备就绪,可以使用");
ExampleBean bean = context.getBean(ExampleBean.class);
bean.sayHello();
System.out.println("8. Spring容器关闭开始");
((AnnotationConfigApplicationContext) context).close();
System.out.println("10. Spring容器已关闭");
}
}
当运行MainApp时,控制台输出将按照以下顺序显示:
Spring容器初始化开始
Bean实例化 - Spring调用配置方法创建Bean
Bean实例化 - 调用构造函数
初始化 - 调用@PostConstruct注解的方法
customInit - 调用customInit
- Bean已准备就绪,可以使用
bean内部方法:Hello from ExampleBean!
Spring容器关闭开始
销毁 - 调用@PreDestroy注解的方法
Spring容器已关闭
注:@PostConstruct和initMethod
@PostConstruct
来源 : @PostConstruct是JSR-250规范的一部分,它是Java EE提供的标准注解,用于在依赖关系设置之后但在Bean被任何其他Bean使用之前调用初始化方法。
使用场景 : 当你需要一个跨框架的标准化初始化注解时,或者你的应用不仅仅局限于Spring框架,那么使用@PostConstruct更为合适。
优点: 标准化,不绑定到特定框架,提高了代码的可移植性。
init-method
来源 : init-method是Spring框架特有的配置方式,通过在Spring的XML配置文件或Java配置类中的@Bean注解里指定初始化方法名称。
使用场景: 如果你的应用完全基于Spring框架,并且希望利用Spring提供的更多特性或与Spring的其他配置风格保持一致,那么使用init-method可能更合适。
@PostConstruct 和 initMethod 都可以用来指定Bean的初始化方法,通过上面的案例可知, @PostConstruct 注解更加标准化,最先进行初始化。而 initMethod 更像是一个初始化后,自定义的方法,两者往往可以互换使用,也可以共存使用,但**@PostConstruct**标准化更加规范。