ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Spring 源码系列:深入理解 BeanPostProcessor 接口 —— 在 Bean 初始化前后实现属性增强、代理包装与自定义扩展

Spring 源码系列:深入理解 BeanPostProcessor 接口 —— 在 Bean 初始化前后实现属性增强、代理包装与自定义扩展 示例工程文档【免费下载链接】spring-reading涵盖了 Spring 框架的核心概念和关键功能包括控制反转IOC容器的使用面向切面编程AOP的原理与实践事务管理的方式与实现Spring MVC 的流程与控制器工作机制以及 Spring 中数据访问、安全、Boot 自动配置等方面的深入研究。此外它还包含了 Spring 事件机制的应用、高级主题如缓存抽象和响应式编程以及对 Spring 源码的编程风格与设计模式的深入探讨。项目地址https://gitcode.com/GitHub_Trending/sp/spring-reading点击查看免费下载本篇技术指南以 spring-reading 仓库中 spring-interface-beanPostProcessor 模块为骨架系统讲解 Spring 核心扩展点BeanPostProcessor的接口设计、注册与排序机制、四大核心功能并结合仓库源码完整演示前缀/后缀属性增强实战案例最后沿refresh()→preInstantiateSingletons()→getBean()→initializeBean()的调用链剖析其底层执行原理。读完本文你将掌握在 Spring 容器中编写自定义 Bean 后处理器、控制其执行顺序以及理解 AOP 代理注入时机等关键能力。一、接口描述BeanPostProcessorBean 后处理器是 Spring 提供给开发者的核心扩展接口允许对新创建的 Bean 实例进行操作和修改。它就像容器流水线上的一道质检与加工工序每当容器完成一个 Bean 的创建BeanPostProcessor都可以在 Bean 初始化前后两个时机介入对实例进行属性调整、状态校验、代理包装甚至整体替换。在 spring-reading 仓库中该主题对应的完整实践代码位于 spring-interface/spring-interface-beanPostProcessor它隶属于spring-interface聚合模块见 spring-interface/pom.xml 中声明的 modules 列表与InstantiationAwareBeanPostProcessor、DestructionAwareBeanPostProcessor、SmartInstantiationAwareBeanPostProcessor等共同构成了 Spring 的 Bean 生命周期扩展体系。二、接口源码BeanPostProcessor是 Spring 框架自2003 年 10 月 10 日开始引入的核心接口由Juergen Hoeller和Sam Brannen维护。接口本身极其精简仅定义了两个默认方法public interface BeanPostProcessor { Nullable default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } Nullable default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } }两个方法的语义对比如下方法调用时机典型用途postProcessBeforeInitialization在任何 Bean 初始化回调如InitializingBean#afterPropertiesSet或自定义init-method之前此时属性填充已完成检查标记接口、设置默认属性值、填充特殊标记postProcessAfterInitialization在任何 Bean 初始化回调之后该 Bean 已使用属性值填充完毕使用代理proxy包装 Bean如 Spring AOP 切面织入从接口源码的 Javadoc 可以提炼出两个关键机制——注册Registration与排序Ordering注册机制自动注册当启动一个AnnotationConfigApplicationContext时Spring 会自动扫描容器中定义的所有 Bean。如果某个 Bean 实现了BeanPostProcessor接口Spring 会将其自动识别并注册为后处理器手动注册通过调用ConfigurableBeanFactory#addBeanPostProcessor方法以编程方式注册。排序机制如果BeanPostProcessor实现了org.springframework.core.PriorityOrdered或org.springframework.core.Ordered接口在ApplicationContext如AnnotationConfigApplicationContext中 Spring 会依据其优先级/顺序语义对后处理器进行排序重要特例如果通过编程方式在ConfigurableBeanFactory中手动注册了BeanPostProcessorSpring 将严格按照注册顺序应用它们此时即使实现了PriorityOrdered或Ordered排序语义也会被忽略另外Order注解不适用于BeanPostProcessorBean排序只能通过实现Ordered/PriorityOrdered接口表达。三、主要功能BeanPostProcessor的能力可以归纳为以下四类修改 Bean 属性在 Bean 初始化之前或之后修改其属性。例如根据某些运行时条件为 Bean 的属性设置默认值或注入额外配置验证 Bean 状态在 Bean 初始化完成后检查其状态确保满足特定条件或业务约束不满足可抛异常中断启动返回代理 Bean最常见的用例——返回一个代理proxy包装原始 Bean。Spring AOP 正是通过后处理器在postProcessAfterInitialization阶段为目标 Bean 创建代理从而实现切面功能改变返回的 Bean 类型完全替换 Bean 实例返回一个不同类型或不同实现的对象。这是高级用例在需要偷梁换柱如替换为 Mock 对象的场景中非常必要。四、最佳实践前缀/后缀属性增强实战本仓库的spring-interface-beanPostProcessor模块提供了完整可运行的示例下面按代码职责逐一拆解。4.1 启动类上下文环境使用AnnotationConfigApplicationContext基于 Java 注解配置 Spring 容器的方式构造参数传入MyConfiguration配置类随后从容器中获取MyService类型的 Bean打印其show()方法返回值// 源码见 spring-interface/spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/BeanPostProcessorApplication.java public class BeanPostProcessorApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(MyConfiguration.class); MyService myService context.getBean(MyService.class); System.out.println(myService.show()); context.close(); } }4.2 配置类使用Bean注解定义两个 BeanMyService返回实现类MyServiceImpl与MyBeanPostProcessor返回自定义后处理器。二者都被声明为 Bean是为了确保MyService与MyBeanPostProcessor都能被 Spring 容器识别和执行// 源码见 spring-interface/spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java Configuration public class MyConfiguration { Bean public MyService myService() { return new MyServiceImpl(); } Bean public BeanPostProcessor myBeanPostProcessor() { return new MyBeanPostProcessor(); } }4.3 自定义后处理器在postProcessBeforeInitialization方法中为MyServiceImpl的message属性添加Prefix:前缀在postProcessAfterInitialization方法中追加:Suffix后缀。注意每个方法都以instanceof类型判断作为守卫仅处理目标类型并最终原样返回处理后的 Bean// 源码见 spring-interface/spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/config/MyBeanPostProcessor.java public class MyBeanPostProcessor implements BeanPostProcessor { Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof MyServiceImpl) { MyServiceImpl myService (MyServiceImpl) bean; myService.setMessage(Prefix: myService.getMessage()); } return bean; } Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof MyServiceImpl) { MyServiceImpl myService (MyServiceImpl) bean; myService.setMessage(myService.getMessage() :Suffix); } return bean; } }4.4 业务接口与实现业务接口MyService声明show()方法源码见 MyService.java实现类MyServiceImpl持有初始值为Hello from MyService的message字段并提供setMessage/getMessage访问器// 源码见 spring-interface/spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/service/MyServiceImpl.java public class MyServiceImpl implements MyService { private String message Hello from MyService; Override public String show() { return message; } public void setMessage(String message) { this.message message; } public String getMessage() { return message; } }4.5 运行与验证直接运行BeanPostProcessorApplication#main该模块为 Maven 子模块父工程为 spring-interface/pom.xmlgroupId 为com.xcs.spring使用mvn exec:java或 IDE 直接运行均可控制台输出如下Prefix: Hello from MyService :Suffix运行结果证明MyBeanPostProcessor成功在初始化前为message加了Prefix:前缀初始化后加了:Suffix后缀验证了两个回调时机确实被容器依次触发。五、执行流程时序图从启动到后处理器生效的完整调用链如下摘自已完成的时序图展示核心参与者六、源码分析从容器启动到后处理器回调下面沿调用链自顶向下追踪BeanPostProcessor的触发路径所有方法均出自 Spring 框架核心类下文括号内标注类名。6.1 上下文初始化AnnotationConfigApplicationContext构造函数AnnotationConfigApplicationContext(Class?... componentClasses)构造函数依次执行三个步骤其中refresh()是驱动整个容器启动的核心public AnnotationConfigApplicationContext(Class?... componentClasses) { this(); register(componentClasses); refresh(); }6.2refresh()→finishBeanFactoryInitialization()AbstractApplicationContext#refresh()中与本次主题相关的关键调用是实例化所有剩余非懒加载单例 BeanOverride public void refresh() throws BeansException, IllegalStateException { // ... [其余步骤略] // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); }finishBeanFactoryInitialization将任务委托给DefaultListableBeanFactory#preInstantiateSingletons()protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // ... [其余逻辑略] // 完成所有剩余非懒加载的单例Bean对象。 beanFactory.preInstantiateSingletons(); }6.3 预实例化preInstantiateSingletons()DefaultListableBeanFactory#preInstantiateSingletons()的核心目的是预先实例化所有非懒加载的单例 Bean。容器初始化完成后触发该方法对容器中每一个单例 Bean 调用getBean(beanName)进而触发实例化、初始化与依赖注入public void preInstantiateSingletons() throws BeansException { // ... [其余逻辑略] for (String beanName : beanNames) { getBean(beanName); } }6.4 获取 BeangetBean()→doGetBean()AbstractBeanFactory#getBean()委托doGetBean完成查找 Bean 定义、创建 Bean若尚未创建、处理依赖关系等核心工作Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); }doGetBean中单例 Bean 会通过DefaultSingletonBeanRegistry#getSingleton配合 Lambda 形式的ObjectFactory获取或创建实例protected T T doGetBean( String name, Nullable ClassT requiredType, Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // ... [其余逻辑略] if (mbd.isSingleton()) { sharedInstance getSingleton(beanName, () - { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { // ... [异常处理略] } }); beanInstance getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // ... [其余逻辑略] return adaptBeanInstance(name, beanInstance, requiredType); }6.5 单例保证getSingleton()DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory?)负责从单例缓存中获取已存在的实例或通过ObjectFactory创建新实例这是容器保证 Bean 单例语义的关键public Object getSingleton(String beanName, ObjectFactory? singletonFactory) { Assert.notNull(beanName, Bean name must not be null); synchronized (this.singletonObjects) { Object singletonObject this.singletonObjects.get(beanName); if (singletonObject null) { // ... [缓存未命中后的创建流程略] try { singletonObject singletonFactory.getObject(); newSingleton true; } catch (IllegalStateException ex) { // ... [异常处理略] } // ... [注册与清理逻辑略] } return singletonObject; } }6.6 创建 BeancreateBean()→doCreateBean()AbstractAutowireCapableBeanFactory#createBean()的核心是调用doCreateBean——真正进行实例化、属性填充和初始化的地方Override protected Object createBean(String beanName, RootBeanDefinition mbd, Nullable Object[] args) throws BeanCreationException { // ... [其余逻辑略] Object beanInstance doCreateBean(beanName, mbdToUse, args); // ... return beanInstance; }doCreateBean有两个主要步骤populateBean(...)属性注入将配置中XML 属性、Autowired、Value等的属性值填充到实例中initializeBean(...)执行 Bean 的初始化方法同时回调所有BeanPostProcessor的postProcessBeforeInitialization与postProcessAfterInitializationprotected Object doCreateBean(String beanName, RootBeanDefinition mbd, Nullable Object[] args) throws BeanCreationException { Object exposedObject bean; // ... [实例化、循环引用三级缓存处理略] try { // 属性注入 populateBean(beanName, mbd, instanceWrapper); // Bean初始化 BeanPostProcessor回调 exposedObject initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { // ... [异常处理略] } return exposedObject; }6.7 初始化并回调initializeBean()AbstractAutowireCapableBeanFactory#initializeBean()将两个回调时机落在applyBeanPostProcessorsBeforeInitialization与applyBeanPostProcessorsAfterInitialization非 synthetic Bean 才会触发避免对 Spring 内部合成 Bean 造成干扰protected Object initializeBean(String beanName, Object bean, Nullable RootBeanDefinition mbd) { // ... [invokeAwareMethods 等前置逻辑略] Object wrappedBean bean; if (mbd null || !mbd.isSynthetic()) { wrappedBean applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } // ... [执行 InitializingBean.afterPropertiesSet / init-method 略] if (mbd null || !mbd.isSynthetic()) { wrappedBean applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }6.8 遍历回调applyBeanPostProcessorsBeforeInitialization/applyBeanPostProcessorsAfterInitialization这两个方法遍历容器中注册的每一个BeanPostProcessor依次调用对应回调若某个后处理器返回null则立即终止链式调用并返回当前结果Override public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException { Object result existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current processor.postProcessBeforeInitialization(result, beanName); if (current null) { return result; } result current; } return result; } Override public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current processor.postProcessAfterInitialization(result, beanName); if (current null) { return result; } result current; } return result; }最终执行到自定义的MyBeanPostProcessor逻辑完成前缀与后缀的追加返回处理后的 Bean 沿调用链逐层向上传递直到context.getBean(MyService.class)拿到最终实例。七、注意事项执行顺序敏感容器中可能注册多个BeanPostProcessor若实现了Ordered接口则按Ordered值排序执行多个后处理器对同一 Bean 的链式处理顺序可能直接影响最终结果前一个的返回值是后一个的入参返回值语义两个回调方法都可以返回新 Bean 替换原始 Bean返回null会终止后续所有BeanPostProcessor的执行在applyBeanPostProcessors*中体现为立即 return关注性能BeanPostProcessor方法对容器中所有 Bean都会被调用实现需尽量高效避免在回调中做重操作否则会拖慢启动时间不要修改无状态 Bean对工具类、帮助类等无状态 Bean 做修改通常没有意义反而带来不必要的性能开销避免抛异常在回调方法中抛异常可能导致整个 Spring 容器启动失败或其他 Bean 初始化失败排序语义的边界编程式注册addBeanPostProcessor按注册顺序生效PriorityOrdered/Ordered排序语义仅对ApplicationContext自动检测场景生效且Order注解对BeanPostProcessor无效。八、总结8.1 最佳实践总结启动类BeanPostProcessorApplication初始化基于 Java 配置的 Spring 上下文获取MyServiceBean 并打印show()返回值配置类MyConfiguration通过Bean声明MyService与MyBeanPostProcessor两个 Bean确保二者被容器创建和管理实现类MyBeanPostProcessor实现BeanPostProcessor接口并覆盖两个回调通过instanceof MyServiceImpl守卫仅在postProcessBeforeInitialization加Prefix:前缀、在postProcessAfterInitialization加:Suffix后缀运行结果输出Prefix: Hello from MyService :Suffix证明message属性被成功修改。8.2 源码分析总结上下文初始化AnnotationConfigApplicationContext构造函数中的refresh()是容器启动核心实例化单例 BeanfinishBeanFactoryInitialization(beanFactory)负责实例化所有剩余非懒加载单例 Bean预实例化preInstantiateSingletons()对每个单例 Bean 调用getBean获取 BeangetBean()委托doGetBean完成实例化、初始化与依赖注入单例保证getSingleton命中缓存则直接返回否则通过ObjectFactory创建新实例Bean 创建createBean→doCreateBean完成实例化、属性填充populateBean与初始化initializeBean后处理器回调initializeBean分别在初始化前后调用applyBeanPostProcessorsBeforeInitialization与applyBeanPostProcessorsAfterInitialization链式遍历所有BeanPostProcessor自定义处理MyBeanPostProcessor最终为message完成前缀与后缀加工。延伸阅读BeanPostProcessor是 Spring Bean 生命周期扩展体系的基石同属spring-interface聚合模块的还有多个紧密相关的扩展接口可对照阅读spring-interface-instantiationAwareBeanPostProcessor在实例化前/后介入可短路默认实例化流程如MyInstantiationAwareBeanPostProcessor中屏蔽DataBase密码属性即由此接口的postProcessProperties实现spring-interface-destructionAwareBeanPostProcessorBean 销毁阶段的回调扩展spring-interface-smartInstantiationAwareBeanPostProcessor提供predictBeanType、determineCandidateConstructors等更细粒度的实例化控制spring-interface-initializingBeanafterPropertiesSet与postProcessBeforeInitialization的先后关系可结合 MyInitializingBean.java 验证借助postProcessAfterInitialization返回代理实现 AOP 的原理可参考 spring-aop-annotationAwareAspectJAutoProxyCreator 等 AOP 模块的源码佐证。赞分享示例工程文档【免费下载链接】spring-reading涵盖了 Spring 框架的核心概念和关键功能包括控制反转IOC容器的使用面向切面编程AOP的原理与实践事务管理的方式与实现Spring MVC 的流程与控制器工作机制以及 Spring 中数据访问、安全、Boot 自动配置等方面的深入研究。此外它还包含了 Spring 事件机制的应用、高级主题如缓存抽象和响应式编程以及对 Spring 源码的编程风格与设计模式的深入探讨。项目地址https://gitcode.com/GitHub_Trending/sp/spring-reading点击查看免费下载相关推荐Spring Bean后处理器与扩展点源码分析Spring Bean后处理器与扩展点源码分析 本文深入分析了Spring框架中BeanPostProcessor后处理器机制、BeanFactoryPostP示例工程文档JCSprout 实战Spring Bean 生命周期完整解析注解、接口、Aware 与 BeanPostProcessor 五条初始化/销毁路径JCSprout 实战Spring Bean 生命周期完整解析注解、接口、Aware 与 BeanPostProcessor 五条初始化/销毁路径 Spr文档知识库后端教程基于React和Redux的Scratch GUI开发架构解析与实战指南基于React和Redux的Scratch GUI开发架构解析与实战指南 Scratch GUI是麻省理工学院开发的Scratch 3.0项目图形用户界面采用前端UI组件教育上一篇Wand-Enhancer 上手攻略3分钟让 WeMod 免费版体验全面进阶下一篇WarcraftHelper兼容性修复指南1.20e到1.27b的魔兽争霸3三步告别卡顿闪退创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表