文章目录

  • 1. 前言
  • 2. BeanDefinitionRegistryPostProcessors 作用
  • 3. 代码示例
  • 4. 底层实现解析
  • 5. 总结

1. 前言

最近项目中遇到这么个问题,公司底层封装的框架有UserService(接口)和 UserServiceImpl(实现类)。基于此框架开发的项目,通过如下代码就能直接从Spring容器取出 UserServiceImpl 对象:

@Autowired
private UserService userService;

现在有个项目A,UserService.save方法不满足项目需求。因此在项目中重写save方法,并且不能修改框架代码。思来想去,实现的方法可能有多种,但是都比较麻烦。最理想的方案是创建 MyUserServiceImpl 继承 UserServiceImpl,并重写相关方法,例如:

public class MyUserServiceImpl: UserServiceImpl {public void onBeforeSave(User user){//各个项目在这里重写特殊逻辑}public void onAfterSave(User user){//各个项目在这里重写特殊逻辑}
}

思路有了,那还有最后一个问题需要解决:属性注入 UserService userService 取到的对象是 UserServiceImpl ,怎么自动替换成新增的 MyUserServiceImpl?

为了解决这个问题,就需要用到Spring提供的后置处理器 PostProcessor 。

2. BeanDefinitionRegistryPostProcessors 作用

先看这张图,大致介绍了 Spring 容器初始化的过程:

如果说 Class 是Java对象的元数据信息,那么 BeanDefinition 就是 Spring Bean 的描述信息。
BeanDefinition 记录了 Spring Bean 的一些重要字段,例如:scope作用域、lazyInit是否懒加载、dependsOn、beanClassName类名等等

如图所示,黄色节点的第3点,扫描类的最后一步会执行程序员创建的 BeanDefinitionRegistryPostProcessor 对象。

因此,我们在这一步取出 UserServiceImpl 对应的BeanDefinition,然后将其 BeanClassName 改成 MyUserServiceImpl.class 。最终,在创建Bean的时候就不再创建UserServiceImpl,而是MyUserServiceImpl。

3. 代码示例

创建接口类 UserService

public interface UserService {void save();
}

创建实现类 UserServiceImpl

@Service
public class UserServiceImpl implements UserService {@Overridepublic void save() {onBeforeSave();System.out.println("执行Save方法");onAfterSave();}public void onBeforeSave(){System.out.println("UserServiceImpl.onBeforeSave");}public void onAfterSave(){System.out.println("UserServiceImpl.onAfterSave");}
}

创建应用程序启动类:

@ComponentScan("com.train")
public class SpringAnnotationApp {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringAnnotationApp.class);UserService userService = applicationContext.getBean(UserService.class);System.out.println(userService.getClass());userService.save();}
}

因为我们还没做任何处理,所以默认情况创建的是 UserServiceImpl 对象,输出结果如下

class com.train.service.impl.UserServiceImpl
UserServiceImpl.onBeforeSave
执行Save方法
UserServiceImpl.onAfterSave

重点来了,创建 MyBeanDefinitionRegistryPostProcessor 实现 BeanDefinitionRegistryPostProcessor 接口

@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {BeanDefinition beanDefinition = registry.getBeanDefinition("userServiceImpl");beanDefinition.setBeanClassName(MyUserServiceImpl.class.getName());}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
}

其他代码不动,重新运行程序,结果变了,达到了我们的要求:

class com.train.inherit.MyUserServiceImpl
MyUserServiceImpl.onBeforeSave
执行Save方法
MyUserServiceImpl.onAfterSave

4. 底层实现解析

上面讲了应用场景,接下来介绍Spring源码如何实现的。

跳过无关的代码,直接查看 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors,以下是核心代码和相关注释。主要贴出的是 BeanDefinitionRegistryPostProcessors 部分。

这个类的代码比较多,还有一部分是 BeanFactoryPostProcessors, 这里先删掉了

 public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {Set<String> processedBeans = new HashSet<>();if (beanFactory instanceof BeanDefinitionRegistry) {BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();/*** 1.从Spring容器获取实现 BeanDefinitionRegistryPostProcessor 接口的处理器* 2.到这里为止,我们自己写的Bean还没加载到容器,因此,可以理解为此处执行的是Spring自带的 BeanDefinitionRegistryPostProcessor(实现类是ConfigurationClassPostProcessor)* 3.ConfigurationClassPostProcessor作用是扫描程序员定义的,需要给Spring管理的类,并封装成BeanDefinition*/String[] postProcessorNames =beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);for (String ppName : postProcessorNames) {if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));processedBeans.add(ppName);}}sortPostProcessors(currentRegistryProcessors, beanFactory);registryProcessors.addAll(currentRegistryProcessors);invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);currentRegistryProcessors.clear();/*** 1.再次从Spring容器获取实现 BeanDefinitionRegistryPostProcessor 接口的处理器* 2.排除掉上一步执行过的PostProcessor,剩余的就是程序员定义的PostProcessor* 3.判断是否实现 Ordered 排序接口,没有实现就先跳过(保证程序员指定的按顺序执行)* 4.执行程序员写的BeanDefinitionRegistryPostProcessor方法*/postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);for (String ppName : postProcessorNames) {if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));processedBeans.add(ppName);}}sortPostProcessors(currentRegistryProcessors, beanFactory);registryProcessors.addAll(currentRegistryProcessors);invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);currentRegistryProcessors.clear();/*** 1.再次从Spring容器获取实现 BeanDefinitionRegistryPostProcessor 接口的处理器* 2.过滤掉前面2步执行过的PostProcessor,剩余的就是程序员定义的,并且没有实现 Ordered 排序接口的部分* 3.执行程序员写的BeanDefinitionRegistryPostProcessor方法*/boolean reiterate = true;while (reiterate) {reiterate = false;postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);for (String ppName : postProcessorNames) {if (!processedBeans.contains(ppName)) {currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));processedBeans.add(ppName);reiterate = true;}}sortPostProcessors(currentRegistryProcessors, beanFactory);registryProcessors.addAll(currentRegistryProcessors);invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);currentRegistryProcessors.clear();}}}

5. 总结

  • 上面的代码只是举个例子,如果重写类较多的话,一个个修改比较麻烦。如果运用到实际项目中,可以采取约定的规则扫描类,然后自动遍历处理BeanDefinition。
  • Spring 提供的 PostProcessor 目的就是允许程序员对其进行扩展,本文讲解的 BeanDefinitionRegistryPostProcessor 到这里就结束了,后面的其他文章将会陆续介绍其他扩展点。

Spring重写BeanDefinitionRegistryPostProcessors进行扩展相关推荐

  1. 如何使用消息队列,Spring Boot和Kubernetes扩展微服务

    by Daniele Polencic 由Daniele Polencic 如何使用消息队列,Spring Boot和Kubernetes扩展微服务 (How to scale Microservic ...

  2. Spring源码——容器扩展ApplicationContext

    前言 内容主要参考自<Spring源码深度解析>一书,算是读书笔记或是原书的补充.进入正文后可能会引来各种不适,毕竟阅读源码是件极其痛苦的事情. 本文主要涉及书中第六章的部分,依照书中内容 ...

  3. Spring核心——IOC处理器扩展

    为什么80%的码农都做不了架构师?>>>    非侵入式框架 Spring一直标注自己是一个非侵入式框架.非侵入式设计的概念并不新鲜,目标就是降低使用者和框架代码的耦合,毕竟框架的开 ...

  4. Spring Boot中如何扩展XML请求和响应的支持

    在之前的所有Spring Boot教程中,我们都只提到和用到了针对HTML和JSON格式的请求与响应处理.那么对于XML格式的请求要如何快速的在Controller中包装成对象,以及如何以XML的格式 ...

  5. spring IOC容器的扩展

    在此之前已经完成了IOC对xml的解析和实例化工作,接下来需要分析Spring的高级版本对IOC容器的功能扩展: 代码分析如下: synchronized (this.startupShutdownM ...

  6. Spring Boot EasyUI edatagrid 扩展

    edatagrid扩展组件详解 edatagrid组件是datagrid的扩展组件,增加了统一处理CRUD的功能,可以用在数据比较简单的页面.使用的时候需要额外引入jquery.edatagrid.j ...

  7. Spring注解驱动开发-扩展原理

    1.扩展原理内容 BeanFactoryPostProcessor BeanDefinitionRegistryPostProcessor ApplicationListener @EventList ...

  8. iOS链式动画、Spring动画,TimingFunction扩展

    Summary AXAnimationChain是一个链式动画库,可以用来轻松的创建基于CAAnimation的链式动画.链的组合方式有两种,一种是组合,另一种则是链接,通过以上两种方式创建的动画,既 ...

  9. Spring注解驱动开发-扩展原理之005-Spring容器刷新第五步-invokeBeanFactoryPostProcessors(beanFactory)

    目录 5.invokeBeanFactoryPostProcessors(beanFactory);执行BeanFactoryPostProcessor的方法: 5.invokeBeanFactory ...

最新文章

  1. 微博广告分布式配置中心的构建与实践
  2. python总线 rabbitmq_Python rabbitMQ如何实现生产消费者模式
  3. javascript showModalDialog,open取得父窗口的方法
  4. mzy,struts学习(一)
  5. 为什么你应该关心领域模型?
  6. 职场生活:辞职也有大学问
  7. Spring 2 和 JPA 简介
  8. Linux装gcc经过
  9. thinkphp中I(parm)用法的注意事项
  10. 请启封,您的双态IT大会邀请函
  11. 潮流短视频必备——PR赛博朋克效果视频转场快速过渡模板
  12. Study「Word2016」:论文公式编辑时,编号右对齐
  13. 中文文本情感分析:基于机器学习方法的思路
  14. leetcode 刷道题 70 earch Insert Position 二进制搜索插入位置
  15. 串口通讯调试-串口调试助手
  16. 写在2020.11.19 周四晚上
  17. USACO3.2.4 Feed Ratios (ratios)
  18. css3 烟 蚊香_HTML5前端特效库 css3 按钮粒子烟花特效源码
  19. Github 上 annie 下载神器的安装及使用教程
  20. 区块链商品溯源系统左侧导航栏+右侧加载页面ajax html

热门文章

  1. 本地文件上传、下载服务器文件事物码
  2. “下沉市场”+“内容生态”,OTA的两道救命题?
  3. 122亿元消费券引爆首都初夏,京东618如何成为经济复苏加速引擎
  4. 编写函数,模拟内置函数 sorted()。
  5. mysql的分区技术作用_理解MySQL数据库分区管理的技术细节
  6. mysql数据库怎么安装建表_mysql数据库安装及建表注意事项
  7. 一加账号app_资讯知名种草App突然被大规模下架!官方回应
  8. numpy 图片填充_numpy/python中的洪水填充分割图像
  9. android 前摄屏幕补光,异形显示屏及其前置摄像头的拍照补光方法与流程
  10. 三十二楼层选几层最好_楼房哪些楼层不能选, 买房楼层选不好开始大忌