乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 Spring概述与IoC容器

    • Spring概述与IoC容器
    • Spring Framework 概述
    • IoC 与 DI 核心概念
    • @Configuration 详解
    • @Component 详解
    • @ComponentScan 详解
    • @Import 详解
    • @Profile 详解
    • @PropertySource 详解
    • @Service 详解
    • @Repository 详解
  • 第2章 Bean的定义与依赖注入

    • Bean的定义与依赖注入
    • @Bean 详解
    • @Autowired 详解
    • @Qualifier 详解
    • @Primary 详解
    • @Resource 详解
    • @Inject 详解
    • @Named 详解
    • @Value 详解
    • @Scope 详解
    • @Lazy 详解
  • 第3章 Bean生命周期与作用域

    • Bean生命周期与作用域
    • Bean生命周期概述
    • @PostConstruct
    • @PreDestroy
    • InitializingBean
    • DisposableBean
    • BeanPostProcessor
    • BeanFactoryPostProcessor
  • 第4章 AOP面向切面编程

    • AOP面向切面编程
    • AOP核心概念
    • @EnableAspectJAutoProxy
    • @Aspect
    • @Pointcut
    • @Before
    • @After
    • @AfterReturning
    • @AfterThrowing
    • @Around
  • 第5章 数据访问与事务管理

    • 数据访问与事务管理
    • 数据访问概述
    • @EnableTransactionManagement
    • @Transactional
    • @Transactional 的传播行为
    • @Transactional 的隔离级别
    • @Transactional 的回滚规则
    • @Transactional 的超时与只读属性
    • @TransactionalEventListener
  • 第6章 Spring Boot自动配置基础

    • Spring Boot自动配置基础
    • @SpringBootApplication 注解
    • @EnableAutoConfiguration 注解
    • @ConfigurationProperties 注解
    • @ConditionalOnClass 注解
    • @ConditionalOnMissingBean 注解
    • @ConditionalOnProperty 注解
  • 第7章 从容器到Web: Spring MVC导引

    • Spring MVC 导引
  • 第8章 扩展阅读

    • 扩展阅读
    • Spring 事件机制 — ApplicationEvent / ApplicationListener
    • @EventListener
    • SpEL — Spring 表达式语言
    • 校验 Validation — JSR-303 / JSR-380 Bean Validation
    • 类型转换与数据绑定 — Converter / DataBinder
  • 附录

    • Spring Framework 专业术语
    • Spring 核心知识点
    • Spring 面试高频考点
    • Spring 核心注解速查表

Bean生命周期概述

本章定位:Spring Core 第03章导学文档。理解 Bean 生命周期是掌握 Spring 容器运作机制的核心门槛——只有清楚知道一个对象从"出生"到"死亡"的完整历程,才能在正确的时机插入初始化逻辑、释放资源、甚至替换掉整个 Bean 实例。


定义与作用

Bean 生命周期(Bean Lifecycle) 指 Spring IoC 容器对一个 Bean 实例从创建到销毁所经历的完整阶段。容器并非简单地 new 一个对象就完事,而是在特定节点预留了扩展钩子,让开发者能够介入并定制行为。

掌握生命周期的意义在于:

  • 初始化阶段:在 Bean 可用前完成资源准备(数据库连接池预热、缓存加载、配置校验)
  • 运行阶段:确保 Bean 以正确状态参与业务逻辑
  • 销毁阶段:在容器关闭时优雅释放资源(关闭连接、清理临时文件、注销服务),避免内存泄漏

完整生命周期流程

Spring 容器管理 Bean 的完整生命周期可分为八大阶段。以下流程基于 Spring 5.x / Spring Boot 2.x 的核心容器行为:

各阶段详解

阶段核心动作开发者可介入点
实例化容器通过反射调用构造方法创建原始对象构造器参数注入、工厂方法
属性填充将配置值和依赖引用注入到 Bean 属性中无直接钩子,依赖 @Autowired 等机制
Aware 回调向 Bean 注入容器级资源(名称、工厂、上下文)实现 BeanNameAware / ApplicationContextAware 等
BPP 前处理在初始化前对 Bean 实例进行加工实现 BeanPostProcessor.postProcessBeforeInitialization()
初始化执行自定义初始化逻辑@PostConstruct / InitializingBean / init-method
BPP 后处理在初始化后对 Bean 实例进行加工,AOP 代理在此生成实现 BeanPostProcessor.postProcessAfterInitialization()
使用Bean 处于就绪状态,响应业务调用日常开发主要在此阶段工作
销毁容器关闭时释放资源@PreDestroy / DisposableBean / destroy-method

生命周期回调的三种方式

Spring 为初始化和销毁分别提供了三种回调机制,形成一条从侵入性强到解耦的演进路线:

方式初始化销毁侵入性推荐度
JSR-250 注解@PostConstruct@PreDestroy无,标准注解⭐⭐⭐ 首选
Spring 接口InitializingBeanDisposableBean强,耦合 Spring API⭐⭐ 不推荐
配置方法init-methoddestroy-method无,纯配置⭐⭐⭐ 灵活场景

团队规范建议:飞翔科技技术部在白歌架构师的推动下,统一使用 @PostConstruct 和 @PreDestroy 作为生命周期回调标准。理由是与 Spring 解耦、代码语义清晰、且被 Jakarta EE 生态广泛支持。


Bean 作用域

Bean Scope(作用域) 定义了 Spring 创建 Bean 实例的数量和可见范围。生命周期与作用域紧密相关——不同作用域的 Bean,其"从生到死"的管辖权归属不同。

作用域说明生命周期管辖适用场景
singleton默认作用域,每个 Spring 容器只有一个实例容器全程管理(创建 → 销毁)无状态服务、配置类、工具类
prototype每次请求都创建新实例容器仅负责创建,不管理销毁有状态对象、多线程独立上下文
request每个 HTTP 请求一个实例(Web 环境)请求开始创建,请求结束销毁请求级数据封装
session每个 HTTP Session 一个实例(Web 环境)Session 开始创建,Session 失效销毁用户登录状态
application每个 ServletContext 一个实例(Web 环境)应用启动创建,应用关闭销毁应用级全局配置
websocket每个 WebSocket 会话一个实例连接建立创建,连接关闭销毁长连接会话状态

关键区别:singleton 和 prototype 在 Core Container 中直接可用;request、session、application、websocket 需要在 Web-aware ApplicationContext 中使用(如 Spring MVC / Spring WebFlux 环境)。

singleton 与 prototype 的生命周期差异


完整示例:飞翔科技学生管理系统的数据源初始化

场景简述

飞翔科技(广州)为某 985 高校开发学生管理系统。CTO 大翔要求系统启动时自动初始化 HikariCP 连接池,并在应用关闭时优雅释放连接。后端开发小崔负责实现这一需求。

操作前:无生命周期管理的代码

在没有生命周期回调时,小崔只能在业务方法中手动管理连接池状态,导致首次请求延迟高,且容器关闭时连接泄漏:

@Service
public class StudentService {

    private HikariDataSource dataSource;

    // 问题1:每次调用都要检查连接池是否创建,首次请求慢
    // 问题2:容器关闭时连接池无人关闭,造成连接泄漏
    public List<Student> listAllStudents() {
        if (dataSource == null) {
            HikariConfig config = new HikariConfig();
            config.setJdbcUrl("jdbc:mysql://localhost:3306/student_db");
            config.setUsername("root");
            config.setPassword("secret");
            dataSource = new HikariDataSource(config);
        }
        // ... 查询逻辑
        return new ArrayList<>();
    }
}

运行结果:系统启动后连接池未就绪,第一次查询耗时 800ms 以上;Tomcat 热部署时旧连接池的连接未被释放,MySQL 端出现大量 Sleep 状态连接。

操作后:使用生命周期回调的完整代码

小崔重构代码,将连接池封装为独立 Bean,利用 Spring 生命周期在正确时机执行初始化和销毁:

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

@Component
public class StudentDataSource {

    private HikariDataSource dataSource;

    /**
     * 阶段五:初始化
     * 依赖注入完成后,立即预热连接池
     */
    @PostConstruct
    public void init() {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://localhost:3306/student_db");
        config.setUsername("root");
        config.setPassword("secret");
        config.setMaximumPoolSize(10);
        config.setMinimumIdle(5);
        config.setConnectionTimeout(3000);
        // 预热:启动时即创建最小空闲连接
        config.setInitializationFailTimeout(5000);

        this.dataSource = new HikariDataSource(config);
        System.out.println("[StudentDataSource] 连接池初始化完成,最小空闲连接:5");
    }

    public Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }

    /**
     * 阶段八:销毁
     * 容器关闭前,优雅关闭连接池
     */
    @PreDestroy
    public void cleanup() {
        if (dataSource != null && !dataSource.isClosed()) {
            System.out.println("[StudentDataSource] 正在关闭连接池,当前活跃连接:"
                + dataSource.getHikariPoolMXBean().getActiveConnections());
            dataSource.close();
            System.out.println("[StudentDataSource] 连接池已关闭");
        }
    }
}

Spring Boot 启动类:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class StudentManagementApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context =
            SpringApplication.run(StudentManagementApplication.class, args);

        // 模拟业务运行
        StudentDataSource ds = context.getBean(StudentDataSource.class);
        System.out.println("[Main] 获取到 DataSource Bean,准备执行业务...");

        // 模拟系统关闭
        context.close();
    }
}

运行结果及分析:

[StudentDataSource] 连接池初始化完成,最小空闲连接:5
[Main] 获取到 DataSource Bean,准备执行业务...
[StudentDataSource] 正在关闭连接池,当前活跃连接:0
[StudentDataSource] 连接池已关闭
  • 实例化 + 属性填充:Spring 容器通过无参构造器创建 StudentDataSource 实例
  • 初始化:@PostConstruct 标注的 init() 在依赖注入完成后立即执行,连接池在业务请求到达前已就绪
  • 使用:StudentService 等业务 Bean 通过注入获取已就绪的连接池
  • 销毁:context.close() 触发 @PreDestroy 标注的 cleanup(),连接池在 JVM 退出前被显式关闭,无连接泄漏

生命周期扩展点全景图

以下大图展示了 Spring 容器中所有主要扩展接口在生命周期中的位置,帮助你在团队 Code Review 时快速判断一段初始化代码是否放在了正确的位置:


易错场景与面试考点

易错场景一:prototype 作用域 Bean 的销毁回调不执行

小崔曾误以为 @PreDestroy 对所有作用域都有效,将报表生成器配置为 prototype:

@Component
@Scope("prototype")
public class ReportGenerator {
    @PreDestroy
    public void cleanup() {
        System.out.println("清理临时文件...");
    }
}

问题:prototype Bean 创建后容器不再持有其引用,cleanup() 永远不会被调用。临时文件清理必须在使用方手动触发,或改用 DisposableBean 配合自定义作用域管理。

易错场景二:在构造器中调用依赖对象

@Service
public class StudentService {
    private final StudentDao studentDao;

    public StudentService(StudentDao studentDao) {
        this.studentDao = studentDao;
        // 错误!此时 studentDao 可能尚未完全初始化
        this.studentDao.warmCache();
    }
}

正确做法:将 warmCache() 移到 @PostConstruct 方法中,确保所有依赖已完成注入且可能的其他初始化回调已执行。

面试高频题

Q1:Spring Bean 的完整生命周期是怎样的?

实例化 → 属性填充 → Aware 接口回调 → BeanPostProcessor.beforeInit → 初始化(@PostConstruct / InitializingBean / init-method)→ BeanPostProcessor.afterInit(AOP 代理生成)→ 业务使用 → 销毁(@PreDestroy / DisposableBean / destroy-method)。

Q2:@PostConstruct 和 InitializingBean 有什么区别?

@PostConstruct 是 JSR-250 标准注解,与 Spring 解耦,推荐优先使用;InitializingBean 是 Spring 专有接口,侵入性强,代码直接依赖 Spring API。两者执行时机相同,都在依赖注入完成后、Bean 就绪前触发。

Q3:为什么同类内部方法调用 AOP 不生效?

因为 this.method() 调用的是目标对象本身,而非代理对象。AOP 代理是在 BeanPostProcessor.postProcessAfterInitialization() 阶段生成的,内部调用绕过了代理层。解决方案:注入自身代理、使用 AopContext.currentProxy()、或重构避免内部调用。


本章内容导航

本章后续文档将逐一深入讲解每个生命周期扩展点:

  • @PostConstruct — JSR-250 初始化注解(推荐首选)
  • @PreDestroy — JSR-250 销毁注解(推荐首选)
  • InitializingBean — Spring 初始化接口(了解即可,不推荐新项目使用)
  • DisposableBean — Spring 销毁接口(了解即可,不推荐新项目使用)
  • BeanPostProcessor — Bean 级后置处理器(AOP 代理、日志、权限的核心机制)
  • BeanFactoryPostProcessor — 容器级后置处理器(动态修改配置元数据)

引出下一章:AOP

当你理解了 BeanPostProcessor.postProcessAfterInitialization() 这个阶段,你就已经站在了 AOP 的门口——Spring AOP 正是通过 AbstractAutoProxyCreator(一个特殊的 BeanPostProcessor)在初始化后阶段为目标 Bean 生成代理对象,从而在不修改源码的情况下插入日志、事务、权限等横切逻辑。

下一章我们将以"切面"为手术刀,解剖 Spring AOP 的代理机制、五种 Advice 类型以及 AspectJ 注解开发模式。大翔已经在架构评审会上拍桌子了:"没有 AOP 的日志系统,我不要!"

上一页
Bean生命周期与作用域
下一页
@PostConstruct