目录
Spring 执行流程
Bean 的生命周期
五个阶段
深入理解 Bean 初始化
实例理解
总结梳理
经典面试题
Spring 执行流程
Bean 的生命周期
- Spring 中 Bean 的生命周就是 Bean 在 Spring 中从创建到销毁的整个过程
五个阶段
1. 实例化 Bean
- 为 Bean 对象分配内存空间
2. 设置属性
- Spring 通过反射机制给 Bean 的属性赋值
3. Bean 初始化
- 各种通知
- 初始化前置方法
- 初始化方法
- 初始化后置方法
4. 使用 Bean
5. 销毁 Bean
深入理解 Bean 初始化
各种通知接口方法
- BeanNameAware:允许 Bean 获取自己在容器中的名称
- BeanFactoryAware:允许 Bean 获取对 Beanfactory的引用
- ApplicationContextAware:允许 Bean 获取对 ApplicationContext 的引用
初始化前置方法
- 该方法可以用于执行一些预处理操作,例如 设置一些默认属性或检查依赖关系
初始化方法
- 如果使用注解方式,可以使用 @PostConstruct 注解来标记初始化方法
- 如果使用 XML 配置方式,可以在 <bean> 标签中使用 init-method 属性来指定初始化方法的名称
初始化后置方法
- 该方法可以用于执行一些后续操作,例如资源清理、启动定时任务等
注意:
- 实例化和初始化是两个完全不同的过程
- 实例化只是给Bean对象分配内存空间
- 初始化则是将程序执行权从系统级别转到用户级别,执行用户添加的业务代码
实例理解
- 创建一个 BeanComponent 类
- 在 Spring-config.xml 中使用 <bean> 标签将 BeanComponent 类的 Bean 对象注入到Spring 容器中
- 给 BeanComponent 类设置 BeanNameAware 通知、两种初始化方法、销毁时执行的方法
- sayHi 方法的创建是为了 使用 Bean 对象
import org.springframework.beans.factory.BeanNameAware; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; public class BeanComponent implements BeanNameAware { @Override public void setBeanName(String s) { System.out.println( "BeanName ——> " + s + " 执行了通知"); } /* * xml 方式的初始化方法 * */ public void myInit() { System.out.println("XML 方式的初始化方法"); } @PostConstruct public void doPostConstruct() { System.out.println("注解方式的初始化方法"); } public void sayHi() { System.out.println("执行 sayHi 方法"); } /* * 销毁时执行的方法 * 此处使用注解的方式 * */ @PreDestroy public void doPreDestroy() { System.out.println("销毁时的方法 已执行"); } }
注意:
- 在启动类中获取 BeanComponent 类的 Bean 对象,并调用其 sayHi 方法,最后销毁容器
import com.java.demo.component.BeanComponent; import org.springframework.context.support.ClassPathXmlApplicationContext; public class App { public static void main(String[] args) { // 因为 ApplicationContext 中没有销毁方法,所以此处使用 ClassPathXmlApplicationContext ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("Spring-config.xml"); BeanComponent beanComponent = context.getBean("beanComponent",BeanComponent.class); // 调用 sayHi 方法,即使用该 Bean 对象 beanComponent.sayHi(); // 将容器销毁 context.destroy(); } }
运行结果:
- 我们发现该执行结果,正好与我们的预期相符合
总结梳理
- Spring 执行流程和 Bean 的生命周期 结合理解
- 加载配置文件:Spring 容器会先加载配置文件,例如 XML 文件或注解配置,以获取Bean 的定义和相关的配置信息
- 创建容器:根据配置文件中的定义,Spring 容器会创建一个应用上下文,也就是容器对象,用于管理 Bean 的生命周期
- 实例化 Bean:Spring 容器根据配置文件中的定义,实例化 Bean 对象,这是通过调用Bean 的构造函数来完成的
- 设置 Bean 属性:一旦 Bean 实例化完成,Spring 容器会通过调用 Bean 的 setter 方法或直接访问其字段,将文件中定义的属性值注入到 Bean 中
- 调用 Bean 的初始化方法:如果 Bean 实现了 InitializingBean 接口,Spring 容器会在属性注入完成后调用其定义的初始化方法。同时如果在配置文件中指定了自定义的初始化方法。Spring 容器也会调用该方法
- 使用 Bean:在 Bean 的初始化完成后,Spring 容器会将其放入容器中,供其他 Bean 或应用程序使用
- 调用 Bean 的销毁方法:当 Spring 容器关闭时,它会销毁之前先释放的 Bean 资源,如果 Bean 实现了 DisposableBean 接口,容器会在销毁前调用其定义的销毁方法。同时,如果在配置文件中指定了自定义的销毁方法,Spring 容器也会调用该方法
经典面试题
是否可以先 执行初始化 再 执行设置属性 ?
- 不可以
- 因为初始化方法中可能会用到设置后的属性
实例理解
@Controller public class StudentController { //属性注入 @Autowired private Student student; //初始化 @PostConstruct public void init() { student.setName("xiaolin"); } }
- 如果此时先执行了初始化 init 方法
- init 方法中需要使用属性注入后得到的 student 对象
- 此时该 student 对象还未被 Spring 容器注入便被 init 方法进行修改
- 上述过程,将直接引发空指针异常