BeanFactoryPostProcessor注册BeanPostProcessor顺序晚于@PostConstruct
问题根因
该现象本质是违反了Spring容器生命周期的执行约定,在BeanFactoryPostProcessor(以下简称BFPP)执行阶段手动注册BeanPostProcessor(以下简称BPP),导致BPP注册时机错位,具体逻辑如下:
- Spring
AbstractApplicationContext#refresh方法的核心执行顺序是固定的:- 加载全部Bean定义信息
- 按优先级顺序执行所有BFPP,该阶段的设计目标仅为修改Bean定义,不会处理BPP的统一注册与排序
- 扫描容器中所有BPP类型的Bean定义,完成实例化、优先级排序后统一注册到BPP拦截链,其中处理
@PostConstruct注解的内置BPPCommonAnnotationBeanPostProcessor就是在这个阶段完成注册的 - 实例化剩余全量单例Bean,执行完整生命周期:实例化→属性注入→Aware回调→BPP的
postProcessBeforeInitialization→@PostConstruct等初始化方法→BPP的postProcessAfterInitialization
- 你在自定义BFPP的
postProcessBeanFactory方法中手动调用beanFactory.addBeanPostProcessor()注册MapstructFactory,属于在第2阶段提前注入BPP。该阶段为了执行BFPP逻辑,Spring会提前实例化BFPP本身及其依赖的Bean(比如你代码中的MapstructRegistry),这些提前实例化的Bean会直接走完初始化流程(包括@PostConstruct回调),此时你的自定义BPP要么还未完成注册,要么注册时这些Bean已经过了BPP拦截阶段,自然不会触发你的BPP逻辑。 - 除此之外,你手动new出来直接添加到BPP链的MapstructFactory不会被Spring纳入统一的BPP排序管理,后续第3阶段统一注册BPP时,可能会把你手动添加的BPP排到内置BPP的后面,最终表现就是你的BPP执行在@PostConstruct回调之后。同时手动实例化的BPP本身不会被Spring容器管理,其自身的依赖注入、生命周期回调、AOP增强都会失效。
最佳处理方案
最符合Spring设计规范的方案是完全移除自定义BFPP,直接将BPP作为普通Bean注册到容器中,交给Spring在BPP统一注册阶段自动完成实例化、排序、注册,从根本上避免生命周期错位问题。
修改后的代码如下:
- 删除原有的MapstructFactoryRegister类,无需通过BFPP手动注册BPP
- 直接在自动配置类中注册MapstructFactory Bean:
@AutoConfiguration public class MapstructAutoConfiguration { @Bean // 如果需要控制BPP执行顺序,可通过@Order注解或实现Ordered接口调整优先级 // 例如下方配置会让该BPP在最高优先级执行,确保在@PostConstruct处理前完成拦截 // @Order(Ordered.HIGHEST_PRECEDENCE) public MapstructFactory mapstructFactory(MapstructRegistry registry) { return new MapstructFactory(registry); } }
该方案的优势:
- 完全匹配Spring生命周期流程,不会出现Bean漏拦截的问题,所有Bean(包括容器启动早期初始化的基础设施Bean)都会被BPP正常拦截
- 支持通过@Order注解灵活调整BPP执行顺序,可根据业务需求配置在@PostConstruct执行前/后触发逻辑
- MapstructFactory作为Spring管理的Bean,自身的依赖注入、生命周期回调、AOP增强都能正常生效
如果存在特殊场景必须通过BFPP注册BPP(极不推荐),需要额外做两个处理:
- 让自定义BFPP实现
PriorityOrdered接口,设置最高执行优先级,尽可能减少BFPP执行阶段提前初始化的Bean数量 - 手动添加BPP后,自行调整BPP列表的顺序,避免被后续统一注册流程打乱执行优先级
内容的提问来源于stack exchange,提问作者livk-cloud
相关产品推荐
相关产品推荐

