Java 确保某个Bean类被最后执行的几种实现方式

 更新时间:2021年03月09日 16:36:17   作者:梦在旅途  
这篇文章主要介绍了Java 确保某个BeanDefinitionRegistryPostProcessor Bean被最后执行的几种实现方式,帮助大家更好的理解和学习使用Java,感兴趣的朋友可以了解下

一、事出有因

​ 最近有一个场景,因同一个项目中不同JAR包依赖同一个组件,但依赖组件的版本不同,导致无论使用哪个版本都报错(无法同时兼容两个JAR包中所需的方法调用),经过分析发现差异的部份是在一个BEAN中的方法出入参不同而矣,故考虑通过动态替换掉这个存在兼容性的BEAN,换成我们自己继承自该BEAN类并实现适配兼容方法,从而最终解决组件版本不兼容问题;

二、解决方案困境

但在实现的编码过程中发现,原依赖的那个BEAN并不是普通的通过标注@Compent之类的注解实现的注册的BEAN,而是由自定义的BeanDefinitionRegistryPostProcessor BEAN类中动态注册的BEAN,这样BEAN的注册顺序是“无法确定”的,我原本想通过自定义一个BeanDefinitionRegistryPostProcessor BEAN类,在postProcessBeanDefinitionRegistry方法中通过找到原依赖BEAN的名字,然后移除该名称对应的BEAN定义信息(BeanDefinition),最后再以原BEAN的名字定义并注册成为我自己的适配器的BEAN类,这样就实现了“移花接木”的功能,然而想法是OK的但最终运行起来,发现BEAN并没有成功被替换,究其原因发现,原来我自己定义的BeanDefinitionRegistryPostProcessor BEAN类是优先于原依赖的那个问题BEAN所对应的BeanDefinitionRegistryPostProcessor BEAN类之前执行的,这样就会导致在我的自定义BeanDefinitionRegistryPostProcessor BEAN类postProcessBeanDefinitionRegistry方法中并没有找到原依赖BEAN名字对应的BeanDefinition,也就无法进行正常的替换了,如果说文字难看懂,可以见如下图所示:

三、柳暗花明,终级解决方案

既然问题根源找到,那确保一个自定义的BeanDefinitionRegistryPostProcessor 类被最后定义为Bean、且被最后执行成为关键(至少得比原依赖的那个问题BEAN所对应的BeanDefinitionRegistryPostProcessor BEAN类【如:OldBeanDefinitionRegistryPostProcessor】之后执行才行),因为这样我们才能获得原依赖的问题Bean的BeanDefinition,才能进行正常的替换BeanDefinition,最终达到原来依赖问题Bean的自动都依赖到新的适配器Bean,从而可以控制修改问题方法的中的逻辑(比如:兼容、降级)。当然,我估计此时有人会想说,何必这么麻烦,一个AOP切面不就搞定了吗?通过实现@Around切面,把有问题的方法拦截替换成自己的适配方法逻辑,这种确实也是一种有效手段,但我认为不够优雅,而且代码的可读性不强且未必能覆盖所有方法,比如:如果涉及问题方法内部依赖的内部方法(如protected)过多或依赖的其它BEAN过多时,可能就会导致这个切面类里面要复制一堆的原问题BEAN类中的内部方法到切面类中,但这样带来的风险就是代码重复及原代码更新后导致的不一致等隐性问题,故我的原则是:如果只是简单的替换原有方法且逻辑不复杂的可以使用AOP切面来解决,但如果涉及复杂的业务逻辑且内部依赖过多,这时采取代理、适配或装饰可能更为合适一些。

好了,如下就是我要分享的三种:确保一个自定义的BeanDefinitionRegistryPostProcessor 类被最后定义为Bean、且被最后执行的实现方式。

第一种实现方案

第一种:通过嵌套注册自定义的BeanDefinitionRegistryPostProcessor 类BEAN的方式,这种方式实现思路是:PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors会先执行已获得BeanDefinitionRegistryPostProcessor BEAN集合,执行完这些BEAN集合后(这里我称为第一轮或第一层),会再次尝试获取第二轮、第三轮一直到获取的BeanDefinitionRegistryPostProcessor BEAN集合全部处理完成为止,框架相关代码片段如下:

			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();
			}

实现方式代码如下:

//如下是第一层自定义的BeanDefinitionRegistryPostProcessor BEAN,内部再注册真正用于替换BEAN目的NewBeanDefinitionRegistryPostProcessor BEAN
//author:zuowenjun
@Component
public class FirstDynamicBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {
 @Override
 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
 BeanDefinitionBuilder beanDefinitionBuilder=BeanDefinitionBuilder.genericBeanDefinition(NewBeanDefinitionRegistryPostProcessor.class);
beanDefinitionRegistry.registerBeanDefinition("newBeanDefinitionRegistryPostProcessor",beanDefinitionBuilder.getBeanDefinition());
 System.out.printf("【%1$tF %1$tT.%1$tL】%s,FirstDynamicBeanPostProcessor.postProcessBeanDefinitionRegistry%n", new Date());
 }

 @Override
 public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
 System.out.printf("【%1$tF %1$tT.%1$tL】%s,FirstDynamicBeanPostProcessor.postProcessBeanFactory%n", new Date());
 }
}

//用于将原依赖的问题Bean替换为同名的新的适配器Bean(下文中所有替换方式最终都要使用该类)
public class NewBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
 @Override
 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
 System.out.printf("【%1$tF %1$tT.%1$tL】%s,NewBeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry%n",new Date());
 boolean isContainsSpecialBean = ((DefaultListableBeanFactory) beanDefinitionRegistry).containsBean("old问题Bean名称");
 if (isContainsSpecialBean) {
 beanDefinitionRegistry.removeBeanDefinition("old问题Bean名称");
 BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(DemoCompentB.class);
 beanDefinitionBuilder.addConstructorArgValue(((DefaultListableBeanFactory) beanDefinitionRegistry).getBean(NewBeanAdapter.class)); //NewBeanAdapter为继承自old问题Bean的装饰者、适配器类
 AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
 beanDefinition.setPrimary(true);
 beanDefinitionRegistry.registerBeanDefinition("old问题Bean名称", beanDefinition);
 }

 }

 @Override
 public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
 System.out.printf("【%1$tF %1$tT.%1$tL】%s,NewBeanDefinitionRegistryPostProcessor.postProcessBeanFactory%n",new Date());
 }
}

最终执行的顺序如下:(可以看到NewBeanDefinitionRegistryPostProcessor是在OldBeanDefinitionRegistryPostProcessor之后执行的,这样就可以正常替换Bean定义了)

FirstDynamicBeanPostProcessor.postProcessBeanDefinitionRegistry (第一轮)

OldBeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry (第一轮)

NewBeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry (第二轮)

FirstDynamicBeanPostProcessor.postProcessBeanFactory

OldBeanDefinitionRegistryPostProcessor.postProcessBeanFactory

NewBeanDefinitionRegistryPostProcessor.postProcessBeanFactory

第二种实现方案

第二种:通过额外定义一个BeanDefinitionRegistryPostProcessor BEAN并实现PriorityOrdered、BeanFactoryAware接口,确保该BEAN最先被执行(Order=0),然后在postProcessBeanDefinitionRegistry方法中通过applicationContext.setDependencyComparator设置自定义的排序器,达到排序BeanDefinitionRegistryPostProcessor BEAN集合的执行顺序,这种方式实现思路是:在执行BeanDefinitionRegistryPostProcessor BEAN集合前会调用sortPostProcessors方法进行排序,而排序规则又依赖于DependencyComparator,通过控制排序规则实现间接控制执行顺序,先看框架的代码片段:

	private static void sortPostProcessors(List<?> postProcessors, ConfigurableListableBeanFactory beanFactory) {
		Comparator<Object> comparatorToUse = null;
		if (beanFactory instanceof DefaultListableBeanFactory) {
			comparatorToUse = ((DefaultListableBeanFactory) beanFactory).getDependencyComparator();
		}
		if (comparatorToUse == null) {
			comparatorToUse = OrderComparator.INSTANCE;
		}
		postProcessors.sort(comparatorToUse);
	}

//如下是invokeBeanFactoryPostProcessors方法片段:
sortPostProcessors(currentRegistryProcessors, beanFactory);
				registryProcessors.addAll(currentRegistryProcessors);
				invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);

实现方式代码如下:

@Component
 public static class FirstBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered
 , BeanFactoryAware {

 private BeanFactory beanFactory;

 @Override
 public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
 this.beanFactory=beanFactory;
 }

 @Override
 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
 ((DefaultListableBeanFactory) beanFactory).setDependencyComparator(new OrderComparator(){
 @Override
 protected int getOrder(Object obj) {
  if (obj instanceof NewBeanDefinitionRegistryPostProcessor){ //如果是NewBeanDefinitionRegistryPostProcessor则将它的排序序号设置为最大
  return Integer.MAX_VALUE;
  }
  return super.getOrder(obj)-1; //其余的全部设为比它小1
 }
 });
 System.out.printf("【%1$tF %1$tT.%1$tL】%s,FirstBeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry%n", new Date());
 }

 @Override
 public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
 System.out.printf("【%1$tF %1$tT.%1$tL】%s,FirstBeanDefinitionRegistryPostProcessor.postProcessBeanFactory%n", new Date());
 }

 @Override
 public int getOrder() {
 return 0;//确保
 }
 }

最终执行的顺序如下:(NewBeanDefinitionRegistryPostProcessor在OldBeanDefinitionRegistryPostProcessor后面执行)

FirstBeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry (第1批:实现PriorityOrdered执行)

OldBeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry (第3批:普通BEAN执行)

NewBeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry (第3批:普通BEAN执行)

FirstBeanDefinitionRegistryPostProcessor.postProcessBeanFactory

OldBeanDefinitionRegistryPostProcessor.postProcessBeanFactory

NewBeanDefinitionRegistryPostProcessor.postProcessBeanFactory

第三种实现方案

第三种:通过自定义DeferredImportSelector类并配合@Import注解,实现NewBeanDefinitionRegistryPostProcessor最后才被注册成为BEAN,最后才有机会执行,这种方式实现思路是:因为DeferredImportSelector的执行时机是在所有@Configuration类型bean解析之后。

实现方式代码如下:

 public static class BeansImportSelector implements DeferredImportSelector {
 @Override
 public String[] selectImports(AnnotationMetadata importingClassMetadata) {
 return new String[]{NewBeanDefinitionRegistryPostProcessor.class.getName()};
 }
 }

@Configuration
@Import(BeansImportSelector.class)
public class BeansConfig {
 
}

最终执行的顺序如下:(NewBeanDefinitionRegistryPostProcessor在OldBeanDefinitionRegistryPostProcessor后面执行)

OldBeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry

NewBeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry

OldBeanDefinitionRegistryPostProcessor.postProcessBeanFactory

NewBeanDefinitionRegistryPostProcessor.postProcessBeanFactory

四、引发的思考

如上就是三种实现方式,至于哪种方式最好,这要看具体的场景,第一种、第三种影响面相对较小,而第二种因为涉及更换DependencyComparator,可能影响的是全局。另外之所以会研究如上实现方式,主要原因还是因为我们的项目框架代码没有考虑扩展性及规范性,比如要动态注册BEAN,至少应实现PriorityOrdered或Order接口或指明@Order注解,这样当我们在某些特定场景需要做一下优化或替换时,则可以直接采取相同的方式但指定Order在前或在后即可,也就不用这么复杂了,比如:

@Component
public class OldBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor,Order {
 
 @Override
 public int getOrder() {
 return 100;
 }
 
 ...
}

@Component
public class NewBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor,Order {
 
 @Override
 public int getOrder() {
 return 101;//只需序号在OldBeanDefinitionRegistryPostProcessor.getOrder之后即可
 }
 
 ...
}

以上就是Java 确保某个BeanDefinitionRegistryPostProcessor Bean被最后执行的几种实现方式的详细内容,更多关于确保BeanDefinitionRegistryPostProcessor Bean执行的资料请关注脚本之家其它相关文章!

相关文章

  • sharding-jdbc读写分离原理详细解析

    sharding-jdbc读写分离原理详细解析

    这篇文章主要介绍了sharding-jdbc读写分离原理详细解析,很多时候,为了应付DB的高并发读写,我们会采用读写分离技术,读写分离指的是利用数据库主从技术(把数据复制到多个节点中),分散读多个库以支持高并发的读,需要的朋友可以参考下
    2023-12-12
  • mybatis空值插入处理的解决方法

    mybatis空值插入处理的解决方法

    本文主要介绍了mybatis空值插入处理的解决方法,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • SpringBoot依赖管理的源码解析

    SpringBoot依赖管理的源码解析

    这篇文章主要介绍了SpringBoot依赖管理的源码解析,maven提供了一套依赖管理机制,通过在pom.xml定义坐标,通过坐标从互联网的中央仓库下载依赖的构件(jar包),规范去管理依赖所有构件,这就叫依赖管理,需要的朋友可以参考下
    2023-04-04
  • 详解Mybatis-plus中更新date类型数据遇到的坑

    详解Mybatis-plus中更新date类型数据遇到的坑

    这篇文章主要介绍了详解Mybatis-plus中更新date类型数据遇到的坑,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-10-10
  • SpringBoot使用GraphQL开发Web API实现方案示例讲解

    SpringBoot使用GraphQL开发Web API实现方案示例讲解

    这篇文章主要介绍了SpringBoot使用GraphQL开发Web API实现方案,GraphQL是一个从服务端检数据的查询语言。某种程度上,是REST、SOAP、或者gRPC的替代品
    2023-04-04
  • java开发ShardingSphere的路由引擎类型示例详解

    java开发ShardingSphere的路由引擎类型示例详解

    这篇文章主要为大家介绍了java开发ShardingSphere的路由引擎类型示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • Tomcat 服务器 在45秒内未启动成功的解决方法

    Tomcat 服务器 在45秒内未启动成功的解决方法

    下面小编就为大家带来一篇Tomcat 服务器 在45秒内未启动成功的解决方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-11-11
  • Mybatis省略@Param注解原理分析

    Mybatis省略@Param注解原理分析

    这篇文章主要介绍了Mybatis省略@Param注解原理分析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • Spring Data JPA中findOne()和getOne()用法

    Spring Data JPA中findOne()和getOne()用法

    这篇文章主要介绍了Spring Data JPA中findOne()和getOne()的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • vue数据响应式原理重写函数实现数组响应式监听

    vue数据响应式原理重写函数实现数组响应式监听

    Vue的通过数据劫持的方式实现数据的双向绑定,即使用Object.defineProperty()来实现对属性的劫持,但是Object.defineProperty()中的setter是无法直接实现数组中值的改变的劫持行为的,需要的朋友可以参考下
    2023-05-05

最新评论