吃透 Spring Bean 生命周期:从源码底层到实战落地

世界杯积分规则 2026-10-06 13:56:51 9729

前言

为什么Spring能成为Java企业级开发的事实标准?核心就是IoC(控制反转)与AOP(面向切面编程)两大特性,而Bean的生命周期,正是IoC容器的核心灵魂。 很多开发者用了多年Spring,却只停留在@Autowired注入Bean的表层,一旦遇到Bean初始化顺序异常、循环依赖报错、扩展点执行不符合预期、启动时资源加载失败等问题,就无从下手;面试时被问到生命周期,也只能背出几个零散的阶段,无法串联底层逻辑。 本文基于Spring Framework 6.2.3(Spring Boot 3.4.4) ,从底层源码、执行流程、扩展点实战、生产问题排查全维度,用通俗的语言讲透Bean生命周期的完整逻辑,所有示例均可直接编译运行,兼顾入门理解与深度进阶,帮你彻底打通Spring的任督二脉。

一、前置认知:Bean与普通Java对象的核心区别很多人入门时会混淆:Bean不就是Java对象吗?为什么要单独讲生命周期? 这里先给出100%准确的定义:

普通Java对象:通过new关键字手动实例化,对象的创建、属性赋值、销毁全由开发者自己控制,生命周期仅与JVM的垃圾回收相关。Spring Bean:由Spring IoC容器统一管理的对象,容器会按照固定的流程,完成Bean的实例化、属性注入、初始化、销毁全生命周期管控,同时提供了大量可插拔的扩展点,允许开发者在生命周期的任意节点介入,定制Bean的行为。一句话总结:普通对象的命运握在开发者手里,Bean的命运握在Spring容器手里。

二、Bean生命周期完整总览先给出Spring 6.x标准的Bean生命周期全流程流程图,执行顺序100%匹配源码逻辑:

其中初始化操作的子流程与销毁操作的子流程,也有固定的执行顺序,后续会分阶段深度拆解。

三、生命周期全阶段深度拆解本章节所有源码均对应Spring Framework 6.2.3的AbstractAutowireCapableBeanFactory核心类,所有示例均基于JDK 17编写。

阶段1:BeanDefinition扫描、注册与BeanFactoryPostProcessor扩展容器启动的第一步,不是直接创建Bean,而是解析并注册Bean的元数据定义。

核心概念BeanDefinition是Bean的元数据载体,相当于Bean的“出生证明”,包含了Bean的全量信息:全类名、作用域、是否懒加载、依赖关系、初始化方法、销毁方法等。Spring容器完全基于这个元数据来创建Bean实例。

执行流程容器启动时,扫描@Component、@Service、@Controller、@Bean等注解标注的类/方法,解析生成对应的BeanDefinition将所有BeanDefinition注册到BeanDefinitionRegistry(Bean定义注册中心)执行所有BeanFactoryPostProcessor实现类,允许开发者在Bean实例化之前,修改、新增、删除BeanDefinition核心特性BeanFactoryPostProcessor是容器级别的扩展点,执行时机在所有Bean实例化之前,整个容器生命周期只执行一次,可对Bean的元数据进行批量修改。

实战示例:自定义BeanFactoryPostProcessor动态修改Bean定义pom.xml核心依赖:

代码语言:javascript复制

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

org.springframework.boot

spring-boot-starter-parent

3.4.4

com.jam.demo

bean-lifecycle-demo

0.0.1-SNAPSHOT

bean-lifecycle-demo

17

1.18.30

2.0.53

33.2.1-jre

3.5.7

8.4.0

2.6.0

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-test

test

org.projectlombok

lombok

${lombok.version}

provided

com.alibaba.fastjson2

fastjson2

${fastjson2.version}

com.google.guava

guava

${guava.version}

com.baomidou

mybatis-plus-boot-starter

${mybatis-plus.version}

com.mysql

mysql-connector-j

${mysql.version}

runtime

org.springdoc

springdoc-openapi-starter-webmvc-ui

${springdoc.version}

org.springframework.boot

spring-boot-maven-plugin

org.projectlombok

lombok

自定义BeanFactoryPostProcessor实现类:

代码语言:javascript复制package com.jam.demo.processor;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.BeansException;

import org.springframework.beans.factory.config.BeanDefinition;

import org.springframework.beans.factory.config.BeanFactoryPostProcessor;

import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

import org.springframework.stereotype.Component;

/**

* 自定义BeanFactoryPostProcessor,实现BeanDefinition动态修改

* @author ken

*/

@Slf4j

@Component

publicclass CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

/**

* 容器加载所有BeanDefinition之后,Bean实例化之前执行

* @param beanFactory 可配置的Bean工厂

* @throws BeansException 处理过程中抛出的异常

*/

@Override

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

log.info("【BeanFactoryPostProcessor】执行开始,容器中已注册的BeanDefinition数量:{}", beanFactory.getBeanDefinitionCount());

// 获取指定Bean的定义信息

String beanName = "lifecycleDemoBean";

if (beanFactory.containsBeanDefinition(beanName)) {

BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);

// 修改Bean为懒加载模式

beanDefinition.setLazyInit(true);

log.info("【BeanFactoryPostProcessor】修改Bean[{}]的懒加载属性为true", beanName);

}

log.info("【BeanFactoryPostProcessor】执行结束");

}

}

阶段2:Bean实例化(Instantiation)当BeanDefinition注册完成,且BeanFactoryPostProcessor执行完毕后,容器会开始创建Bean实例。

核心规则单例Bean(默认@Scope("singleton")):容器启动时完成实例化,存入单例池全局唯一原型Bean(@Scope("prototype")):每次调用getBean()时才会触发实例化,每次创建新实例懒加载Bean(@Lazy):首次被获取时才会触发实例化底层源码对应AbstractAutowireCapableBeanFactory#createBean → doCreateBean → createBeanInstance

核心逻辑先检查单例池singletonObjects中是否已存在该Bean,存在则直接返回不存在则通过反射机制,匹配并调用Bean的构造方法,生成一个裸对象(仅完成实例化,未进行属性填充和初始化,所有属性均为默认值)实例化完成后,属性填充之前,会将该Bean的早期对象工厂ObjectFactory放入三级缓存,为解决循环依赖提供支撑关键注意点实例化只是完成了JVM层面的对象创建,此时的对象还未完成依赖注入,无法正常使用,很多开发者在构造方法中调用依赖Bean的方法,会出现空指针异常,根本原因就是构造方法执行时,属性填充还未开始。

阶段3:属性填充(Populate)实例化完成后,容器进入属性填充阶段,也就是我们常说的依赖注入(DI)。

底层源码对应AbstractAutowireCapableBeanFactory#populateBean

核心逻辑解析Bean中所有标注了@Autowired、@Value、@Resource等注解的属性/setter方法从容器中查找匹配的依赖Bean,若依赖Bean未创建,会先触发依赖Bean的完整生命周期通过反射机制,完成属性的赋值操作关键特性Spring的依赖注入只会处理容器管理的Bean,手动通过new创建的对象,其标注了@Autowired的属性不会被容器注入,这是生产中常见的空指针诱因。

示例:验证实例化与属性填充的执行顺序代码语言:javascript复制package com.jam.demo.bean;

import jakarta.annotation.PostConstruct;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Component;

/**

* 生命周期演示Bean,验证实例化与属性填充顺序

* @author ken

*/

@Slf4j

@Component(value = "lifecycleDemoBean")

publicclass LifecycleDemoBean {

private DependencyBean dependencyBean;

/**

* 构造方法:对应实例化阶段

*/

public LifecycleDemoBean() {

log.info("【阶段2-实例化】LifecycleDemoBean构造方法执行,此时dependencyBean是否为null:{}", dependencyBean == null);

}

/**

* 依赖注入:对应属性填充阶段

* @param dependencyBean 依赖的Bean实例

*/

@Autowired

public void setDependencyBean(DependencyBean dependencyBean) {

this.dependencyBean = dependencyBean;

log.info("【阶段3-属性填充】setDependencyBean执行,此时dependencyBean是否为null:{}", dependencyBean == null);

}

@PostConstruct

public void init() {

log.info("初始化阶段执行,dependencyBean已完成注入");

}

}

代码语言:javascript复制package com.jam.demo.bean;

import lombok.extern.slf4j.Slf4j;

import org.springframework.stereotype.Component;

/**

* 依赖Bean示例

* @author ken

*/

@Slf4j

@Component

public class DependencyBean {

public DependencyBean() {

log.info("DependencyBean实例化完成");

}

}

运行后控制台会清晰打印:实例化构造方法执行时,dependencyBean为null;属性填充set方法执行时,dependencyBean已完成注入,完美验证执行顺序。

阶段4:Aware接口回调属性填充完成后,容器会检查当前Bean是否实现了Aware系列接口,若实现了,会按固定顺序回调对应的接口方法,将Spring容器的底层组件注入到Bean中。

核心作用Aware接口是Spring提供的容器资源注入通道,让Bean能够感知到自身在容器中的信息,以及获取容器的底层核心组件。

固定执行顺序(Spring 6.x标准)BeanNameAware:注入当前Bean在容器中的名称BeanClassLoaderAware:注入加载当前Bean的ClassLoaderBeanFactoryAware:注入当前的BeanFactory实例EnvironmentAware:注入Environment环境配置对象EmbeddedValueResolverAware:注入占位符解析器,用于解析${}配置ResourceLoaderAware:注入资源加载器(仅ApplicationContext环境生效)ApplicationEventPublisherAware:注入事件发布器MessageSourceAware:注入国际化消息源ApplicationContextAware:注入ApplicationContext应用上下文对象示例:Aware接口回调顺序验证代码语言:javascript复制package com.jam.demo.bean;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.BeansException;

import org.springframework.beans.factory.*;

import org.springframework.context.ApplicationContext;

import org.springframework.context.ApplicationContextAware;

import org.springframework.context.EnvironmentAware;

import org.springframework.core.env.Environment;

import org.springframework.stereotype.Component;

/**

* Aware接口回调顺序验证Bean

* @author ken

*/

@Slf4j

@Component

publicclass AwareDemoBean implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware,

EnvironmentAware, ApplicationContextAware {

/**

* 回调Bean名称

* @param beanName 当前Bean在容器中的名称

*/

@Override

public void setBeanName(String beanName) {

log.info("【阶段4-Aware回调】1.BeanNameAware.setBeanName执行,beanName:{}", beanName);

}

/**

* 回调类加载器

* @param classLoader 加载当前Bean的类加载器

*/

@Override

public void setBeanClassLoader(ClassLoader classLoader) {

log.info("【阶段4-Aware回调】2.BeanClassLoaderAware.setBeanClassLoader执行");

}

/**

* 回调Bean工厂

* @param beanFactory 当前Bean所属的BeanFactory

* @throws BeansException 异常

*/

@Override

public void setBeanFactory(BeanFactory beanFactory) throws BeansException {

log.info("【阶段4-Aware回调】3.BeanFactoryAware.setBeanFactory执行");

}

/**

* 回调环境配置

* @param environment 环境配置对象

*/

@Override

public void setEnvironment(Environment environment) {

log.info("【阶段4-Aware回调】4.EnvironmentAware.setEnvironment执行");

}

/**

* 回调应用上下文

* @param applicationContext 应用上下文对象

* @throws BeansException 异常

*/

@Override

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

log.info("【阶段4-Aware回调】5.ApplicationContextAware.setApplicationContext执行");

}

}

运行后控制台会严格按照上述顺序打印回调日志,无任何偏差。

阶段5:BeanPostProcessor前置处理Aware接口回调完成后,容器会执行所有BeanPostProcessor实现类的postProcessBeforeInitialization方法,也就是初始化前置处理。

核心定义BeanPostProcessor是Bean级别的扩展点,对容器中所有的Bean生效,在Bean初始化之前执行,允许开发者对Bean实例进行修改、包装、替换。

底层源码对应AbstractAutowireCapableBeanFactory#initializeBean → applyBeanPostProcessorsBeforeInitialization

核心特性Spring大量核心功能基于此扩展点实现:

@PostConstruct注解的处理,由CommonAnnotationBeanPostProcessor的前置处理方法完成@Autowired注解的属性注入,由AutowiredAnnotationBeanPostProcessor完成数据校验、参数转换等功能,均基于此扩展点实现示例:自定义BeanPostProcessor前置处理代码语言:javascript复制package com.jam.demo.processor;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.BeansException;

import org.springframework.beans.factory.config.BeanPostProcessor;

import org.springframework.stereotype.Component;

/**

* 自定义BeanPostProcessor实现类

* @author ken

*/

@Slf4j

@Component

publicclass CustomBeanPostProcessor implements BeanPostProcessor {

/**

* Bean初始化之前执行

* @param bean 当前Bean实例

* @param beanName 当前Bean名称

* @return 处理后的Bean实例(可替换原实例)

* @throws BeansException 处理异常

*/

@Override

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

// 仅处理我们的演示Bean,避免全量Bean打印日志过多

if ("lifecycleDemoBean".equals(beanName) || "awareDemoBean".equals(beanName)) {

log.info("【阶段5-前置处理】BeanPostProcessor.before执行,beanName:{}", beanName);

}

return bean;

}

/**

* Bean初始化之后执行

* @param bean 当前Bean实例

* @param beanName 当前Bean名称

* @return 处理后的Bean实例(可替换原实例)

* @throws BeansException 处理异常

*/

@Override

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

if ("lifecycleDemoBean".equals(beanName) || "awareDemoBean".equals(beanName)) {

log.info("【阶段7-后置处理】BeanPostProcessor.after执行,beanName:{}", beanName);

}

return bean;

}

}

阶段6:Bean初始化操作前置处理完成后,进入Bean的核心初始化阶段,开发者可在此阶段实现自定义的初始化逻辑,Spring提供了三种实现方式,执行顺序固定不可更改。

固定执行顺序标注了@PostConstruct注解的方法实现了InitializingBean接口的afterPropertiesSet方法Bean定义中指定的init-method方法(@Bean(initMethod = "xxx"))顺序底层逻辑@PostConstruct由CommonAnnotationBeanPostProcessor的前置处理方法执行,而前置处理在初始化操作之前,因此最先执行;afterPropertiesSet在invokeInitMethods方法中优先执行,之后才会执行自定义的init-method方法。

底层源码对应AbstractAutowireCapableBeanFactory#invokeInitMethods

示例:三种初始化方式执行顺序验证代码语言:javascript复制package com.jam.demo.bean;

import jakarta.annotation.PostConstruct;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.InitializingBean;

import org.springframework.stereotype.Component;

/**

* 初始化方式顺序验证Bean

* @author ken

*/

@Slf4j

@Component

publicclass InitDemoBean implements InitializingBean {

/**

* 第一种初始化方式:@PostConstruct注解

*/

@PostConstruct

public void postConstructInit() {

log.info("【阶段6-初始化】1.@PostConstruct注解方法执行");

}

/**

* 第二种初始化方式:InitializingBean接口实现

* @throws Exception 初始化异常

*/

@Override

public void afterPropertiesSet() throws Exception {

log.info("【阶段6-初始化】2.InitializingBean.afterPropertiesSet方法执行");

}

/**

* 第三种初始化方式:自定义init-method方法

* 需在@Bean注解中指定initMethod = "customInit"

*/

public void customInit() {

log.info("【阶段6-初始化】3.自定义init-method方法执行");

}

}

代码语言:javascript复制package com.jam.demo.config;

import com.jam.demo.bean.InitDemoBean;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

/**

* Bean配置类,指定init-method

* @author ken

*/

@Configuration

publicclass BeanConfig {

@Bean(initMethod = "customInit", name = "initDemoBean")

public InitDemoBean initDemoBean() {

returnnew InitDemoBean();

}

}

运行后控制台会严格按照上述顺序打印初始化日志,100%匹配源码逻辑。

阶段7:BeanPostProcessor后置处理初始化操作完成后,容器会执行所有BeanPostProcessor实现类的postProcessAfterInitialization方法,也就是初始化后置处理。

核心作用在Bean初始化完成后,对Bean进行最终的修改、包装、替换,Spring AOP的动态代理对象,就是在这个阶段生成的!这是Spring最核心的特性之一。

底层源码对应AbstractAutowireCapableBeanFactory#initializeBean → applyBeanPostProcessorsAfterInitialization

关键特性如果Bean被AOP切面拦截,在后置处理阶段,会返回一个动态代理对象替换原始的目标对象,最终存入单例池的是代理对象,而非原始对象,这也是AOP能够实现功能增强的底层原理。

阶段8:Bean就绪,存入单例池后置处理完成后,Bean的完整创建流程就结束了,此时的Bean已经是完全初始化完成、可正常使用的成熟对象。

单例Bean:会被存入Spring IoC容器的单例池singletonObjects(一个ConcurrentHashMap)中,后续所有获取该Bean的请求,都会直接从单例池中返回,不会重复创建原型Bean:不会存入单例池,每次调用getBean()都会重新执行完整的生命周期,创建新的实例阶段9:Bean销毁流程当Spring容器关闭时(调用ConfigurableApplicationContext#close()方法),会触发单例Bean的销毁流程,原型Bean不会被容器管理销毁,由JVM垃圾回收处理。

固定执行顺序标注了@PreDestroy注解的方法实现了DisposableBean接口的destroy方法Bean定义中指定的destroy-method方法(@Bean(destroyMethod = "xxx"))底层源码对应DefaultSingletonBeanRegistry#destroySingleton → DisposableBeanAdapter#destroy

示例:三种销毁方式执行顺序验证代码语言:javascript复制package com.jam.demo.bean;

import jakarta.annotation.PreDestroy;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.DisposableBean;

import org.springframework.stereotype.Component;

/**

* 销毁方式顺序验证Bean

* @author ken

*/

@Slf4j

@Component

publicclass DestroyDemoBean implements DisposableBean {

/**

* 第一种销毁方式:@PreDestroy注解

*/

@PreDestroy

public void preDestroy() {

log.info("【阶段9-销毁】1.@PreDestroy注解方法执行");

}

/**

* 第二种销毁方式:DisposableBean接口实现

* @throws Exception 销毁异常

*/

@Override

public void destroy() throws Exception {

log.info("【阶段9-销毁】2.DisposableBean.destroy方法执行");

}

/**

* 第三种销毁方式:自定义destroy-method方法

* 需在@Bean注解中指定destroyMethod = "customDestroy"

*/

public void customDestroy() {

log.info("【阶段9-销毁】3.自定义destroy-method方法执行");

}

}

代码语言:javascript复制package com.jam.demo.config;

import com.jam.demo.bean.DestroyDemoBean;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

/**

* Bean配置类,指定destroy-method

* @author ken

*/

@Configuration

publicclass DestroyBeanConfig {

@Bean(destroyMethod = "customDestroy", name = "destroyDemoBean")

public DestroyDemoBean destroyDemoBean() {

returnnew DestroyDemoBean();

}

}

容器关闭时,控制台会严格按照上述顺序打印销毁日志,可用于生产环境的优雅停机、资源释放等场景。

四、生产场景实战落地实战1:基于InitializingBean实现系统启动资源预热生产场景:系统启动时,将数据库中的字典数据、系统配置加载到本地内存,避免每次接口请求都查询数据库,大幅提升接口响应速度。

代码语言:javascript复制package com.jam.demo.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

import com.google.common.collect.Maps;

import com.jam.demo.entity.SysDict;

import com.jam.demo.mapper.SysDictMapper;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.InitializingBean;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import org.springframework.util.CollectionUtils;

import java.util.List;

import java.util.Map;

/**

* 系统字典服务,启动时预热字典数据到内存

* @author ken

*/

@Slf4j

@Service

publicclass SysDictService implements InitializingBean {

/**

* 本地内存缓存,存储字典数据

*/

privatefinal Map DICT_CACHE = Maps.newConcurrentMap();

@Autowired

private SysDictMapper sysDictMapper;

/**

* 系统启动时执行,加载字典数据到内存

* @throws Exception 加载异常

*/

@Override

public void afterPropertiesSet() throws Exception {

log.info("【系统预热】开始加载字典数据到本地缓存");

LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper()

.eq(SysDict::getStatus, 1);

List dictList = sysDictMapper.selectList(queryWrapper);

if (CollectionUtils.isEmpty(dictList)) {

log.warn("【系统预热】未查询到可用的字典数据");

return;

}

dictList.forEach(dict -> DICT_CACHE.put(dict.getDictCode(), dict.getDictValue()));

log.info("【系统预热】字典数据加载完成,共加载{}条数据", DICT_CACHE.size());

}

/**

* 根据字典编码获取字典值

* @param dictCode 字典编码

* @return 字典值

*/

public String getDictValue(String dictCode) {

return DICT_CACHE.get(dictCode);

}

}

配套MyBatis-Plus实体类与Mapper:

代码语言:javascript复制package com.jam.demo.entity;

import com.baomidou.mybatisplus.annotation.IdType;

import com.baomidou.mybatisplus.annotation.TableId;

import com.baomidou.mybatisplus.annotation.TableName;

import io.swagger.v3.oas.annotations.media.Schema;

import lombok.Data;

import java.io.Serializable;

/**

* 系统字典实体类

* @author ken

*/

@Data

@TableName("sys_dict")

@Schema(description = "系统字典实体")

publicclass SysDict implements Serializable {

privatestaticfinallong serialVersionUID = 1L;

@TableId(type = IdType.AUTO)

@Schema(description = "主键ID")

private Long id;

@Schema(description = "字典编码")

private String dictCode;

@Schema(description = "字典值")

private String dictValue;

@Schema(description = "状态:0-禁用 1-启用")

private Integer status;

}

代码语言:javascript复制package com.jam.demo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;

import com.jam.demo.entity.SysDict;

import org.apache.ibatis.annotations.Mapper;

/**

* 系统字典Mapper

* @author ken

*/

@Mapper

public interface SysDictMapper extends BaseMapper {

}

配套MySQL建表语句:

代码语言:javascript复制CREATE TABLE`sys_dict` (

`id`bigintNOTNULL AUTO_INCREMENT COMMENT'主键ID',

`dict_code`varchar(100) NOTNULLCOMMENT'字典编码',

`dict_value`varchar(500) NOTNULLCOMMENT'字典值',

`status`tinyintNOTNULLDEFAULT'1'COMMENT'状态:0-禁用 1-启用',

PRIMARY KEY (`id`),

UNIQUEKEY`uk_dict_code` (`dict_code`)

) ENGINE=InnoDBDEFAULTCHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='系统字典表';

实战2:基于BeanPostProcessor实现全局接口日志切面生产场景:替代传统AOP,实现所有Controller接口的入参、出参、响应时间全量日志打印,性能更优,扩展更灵活。

代码语言:javascript复制package com.jam.demo.processor;

import com.alibaba.fastjson2.JSON;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.BeansException;

import org.springframework.beans.factory.config.BeanPostProcessor;

import org.springframework.stereotype.Component;

import org.springframework.web.bind.annotation.RestController;

import java.lang.reflect.Proxy;

/**

* 全局Controller日志处理器

* @author ken

*/

@Slf4j

@Component

publicclass ControllerLogPostProcessor implements BeanPostProcessor {

@Override

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

// 仅处理标注了@RestController的Bean

if (bean.getClass().isAnnotationPresent(RestController.class)) {

log.info("【日志增强】为Controller[{}]创建日志代理对象", beanName);

// 生成JDK动态代理对象,实现接口日志打印

return Proxy.newProxyInstance(

bean.getClass().getClassLoader(),

bean.getClass().getInterfaces(),

(proxy, method, args) -> {

long startTime = System.currentTimeMillis();

String className = bean.getClass().getSimpleName();

String methodName = method.getName();

// 打印入参

log.info("【接口请求】{}.{} 入参:{}", className, methodName, JSON.toJSONString(args));

Object result;

try {

// 执行目标方法

result = method.invoke(bean, args);

// 打印出参

log.info("【接口响应】{}.{} 出参:{}", className, methodName, JSON.toJSONString(result));

} catch (Exception e) {

log.error("【接口异常】{}.{} 执行异常", className, methodName, e);

throw e;

} finally {

// 打印响应时间

long endTime = System.currentTimeMillis();

log.info("【接口耗时】{}.{} 执行耗时:{}ms", className, methodName, (endTime - startTime));

}

return result;

}

);

}

return bean;

}

}

配套Controller示例:

代码语言:javascript复制package com.jam.demo.controller;

import com.jam.demo.service.SysDictService;

import io.swagger.v3.oas.annotations.Operation;

import io.swagger.v3.oas.annotations.Parameter;

import io.swagger.v3.oas.annotations.tags.Tag;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.RestController;

/**

* 字典查询Controller

* @author ken

*/

@Slf4j

@RestController

@RequestMapping("/dict")

@Tag(name = "系统字典接口", description = "系统字典查询相关接口")

publicclass DictController implements DictControllerApi {

@Autowired

private SysDictService sysDictService;

@Override

@GetMapping("/getByCode")

@Operation(summary = "根据字典编码查询字典值", description = "查询已启用的字典数据")

public String getDictByCode(

@Parameter(description = "字典编码", required = true) @RequestParam String dictCode) {

return sysDictService.getDictValue(dictCode);

}

}

代码语言:javascript复制package com.jam.demo.controller;

import io.swagger.v3.oas.annotations.Operation;

import io.swagger.v3.oas.annotations.Parameter;

import io.swagger.v3.oas.annotations.tags.Tag;

/**

* 字典接口规范

* @author ken

*/

@Tag(name = "系统字典接口", description = "系统字典查询相关接口")

public interface DictControllerApi {

@Operation(summary = "根据字典编码查询字典值", description = "查询已启用的字典数据")

String getDictByCode(

@Parameter(description = "字典编码", required = true) String dictCode);

}

实战3:基于DisposableBean实现优雅停机资源释放生产场景:容器关闭时,优雅关闭线程池、释放数据库连接、持久化内存数据,避免强制停机导致的数据丢失、资源泄漏问题。

代码语言:javascript复制package com.jam.demo.service;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.DisposableBean;

import org.springframework.stereotype.Service;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.LinkedBlockingQueue;

import java.util.concurrent.ThreadPoolExecutor;

import java.util.concurrent.TimeUnit;

/**

* 线程池管理服务,优雅停机释放资源

* @author ken

*/

@Slf4j

@Service

publicclass ThreadPoolService implements DisposableBean {

/**

* 业务处理线程池

*/

privatefinal ExecutorService businessExecutor = new ThreadPoolExecutor(

8,

16,

60L,

TimeUnit.SECONDS,

new LinkedBlockingQueue<>(1000),

r -> new Thread(r, "business-thread-" + r.hashCode()),

new ThreadPoolExecutor.CallerRunsPolicy()

);

/**

* 提交任务到线程池

* @param task 待执行的任务

*/

public void submitTask(Runnable task) {

businessExecutor.submit(task);

}

/**

* 容器关闭时执行,优雅关闭线程池

* @throws Exception 关闭异常

*/

@Override

public void destroy() throws Exception {

log.info("【优雅停机】开始关闭业务线程池");

// 停止接收新任务

businessExecutor.shutdown();

// 等待现有任务执行完成,超时30秒强制关闭

if (!businessExecutor.awaitTermination(30, TimeUnit.SECONDS)) {

log.warn("【优雅停机】线程池任务执行超时,强制关闭");

businessExecutor.shutdownNow();

}

log.info("【优雅停机】业务线程池关闭完成");

}

}

五、易混淆技术点精准辨析1. BeanFactoryPostProcessor VS BeanPostProcessor对比维度

BeanFactoryPostProcessor

BeanPostProcessor

执行时机

所有Bean实例化之前,BeanDefinition注册完成后

Bean实例化、属性填充完成后,初始化前后

作用对象

BeanDefinition(Bean的元数据)

Bean实例(已创建的对象)

执行次数

容器生命周期内仅执行一次

每个Bean创建时都会执行两次(前置+后置)

核心用途

修改/新增/删除Bean定义,容器级别的批量配置

对Bean实例进行修改、包装、代理,Bean级别的功能增强

执行顺序

早于所有Bean的实例化,早于BeanPostProcessor

晚于BeanFactoryPostProcessor执行

2. 实例化(Instantiation) VS 初始化(Initialization)对比维度

实例化

初始化

核心含义

JVM层面创建对象的过程,调用构造方法生成裸对象

对象创建完成后,完成属性赋值、资源加载、自定义逻辑,让对象进入可用状态

执行时机

生命周期最早期,属性填充之前

属性填充、Aware回调完成后,Bean就绪之前

对象状态

所有属性均为默认值,依赖未注入,无法正常使用

属性已完成注入,资源已加载,可正常使用

常见踩坑

在构造方法中调用依赖Bean的方法,导致空指针

在静态方法中调用实例属性,导致空指针

3. @PostConstruct VS InitializingBean VS init-method对比维度

@PostConstruct

InitializingBean

init-method

执行顺序

最先执行

第二执行

最后执行

所属规范

JSR-250 JavaEE规范

Spring原生接口

Spring XML/注解配置

与Spring耦合

无耦合,通用Java注解

强耦合,必须实现Spring接口

无耦合,纯自定义方法

异常处理

抛出异常会终止Bean创建

抛出异常会终止Bean创建

抛出异常会终止Bean创建

推荐场景

简单初始化逻辑,无Spring耦合场景

框架级别的初始化逻辑,需要和Spring深度集成

第三方Bean的初始化,无法修改源码的场景

六、高频面试&生产问题排查解决方案问题1:@Autowired注入的属性为null,核心原因与解决方案核心原因(结合生命周期)Bean未被容器管理:通过new手动创建的对象,不会被Spring扫描,容器不会执行属性填充,导致注入为null静态属性注入:@Autowired只能注入实例属性,静态属性属于类,容器不会在实例化阶段注入静态属性初始化顺序错误:在构造方法、静态代码块中调用依赖Bean的方法,此时属性填充还未执行,属性为nullBean循环依赖:构造器注入的循环依赖,容器无法完成实例化,导致属性注入失败解决方案所有需要依赖注入的Bean,必须通过@Component等注解交给Spring容器管理,禁止手动new静态属性注入需通过setter方法注入,将注解标注在setter方法上初始化逻辑必须放在@PostConstruct或afterPropertiesSet方法中,禁止在构造方法中调用依赖Bean循环依赖场景使用setter注入替代构造器注入,开启懒加载问题2:Spring为什么能解决setter注入的循环依赖,无法解决构造器注入的循环依赖?结合生命周期的底层原理Spring通过三级缓存解决循环依赖,三级缓存的放入时机是实例化完成后,属性填充之前:

setter注入循环依赖:A依赖B,B依赖A。A先完成实例化,将早期对象工厂放入三级缓存,然后执行属性填充,需要注入B;触发B的生命周期,B实例化后属性填充需要注入A,从三级缓存中获取A的早期对象,完成B的初始化;A再完成后续的属性填充和初始化,循环依赖解决。构造器注入循环依赖:A的构造方法需要B,B的构造方法需要A。A在实例化阶段(调用构造方法)就需要B,此时A还未完成实例化,无法放入三级缓存;触发B的实例化,B的构造方法需要A,此时A还未创建,容器无法找到A的实例,直接抛出循环依赖异常。问题3:自定义的BeanPostProcessor不生效,核心原因与解决方案核心原因BeanPostProcessor未被容器扫描到:没有标注@Component注解,或不在Spring的扫描路径下循环依赖导致提前初始化:BeanPostProcessor被其他Bean依赖,导致其在容器早期就被初始化,无法对后续的Bean生效被AOP代理导致失效:BeanPostProcessor本身被AOP代理,破坏了容器的扩展点执行逻辑静态内部类未使用static修饰:非静态内部类无法被Spring实例化,导致扩展点不生效解决方案确保BeanPostProcessor实现类标注@Component注解,且在Spring的扫描路径内BeanPostProcessor禁止被其他业务Bean依赖,保持独立的容器扩展角色禁止对BeanPostProcessor实现类进行AOP切面拦截内部类实现的BeanPostProcessor必须使用static修饰七、总结Spring Bean的生命周期,是Spring IoC容器的核心灵魂,所有的Spring高级特性、扩展点、生产问题,都离不开生命周期的底层逻辑。 本文从最基础的概念定义,到全流程的源码级拆解,再到生产场景的实战落地,最后到高频问题的排查解决,完整覆盖了Bean生命周期的所有核心知识点。

站点统计