时间:2021-05-20
最近有一个场景,因同一个项目中不同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@Componentpublic 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在前或在后即可,也就不用这么复杂了,比如:
@Componentpublic class OldBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor,Order { @Override public int getOrder() { return 100; } ...}@Componentpublic class NewBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor,Order { @Override public int getOrder() { return 101;//只需序号在OldBeanDefinitionRegistryPostProcessor.getOrder之后即可 } ...}以上就是Java 确保某个BeanDefinitionRegistryPostProcessor Bean被最后执行的几种实现方式的详细内容,更多关于确保BeanDefinitionRegistryPostProcessor Bean执行的资料请关注其它相关文章!
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
一、定时任务实现的几种方式:Timer这是java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务。使用这
java调用python的几种用法如下:在java类中直接执行python语句在java类中直接调用本地python脚本使用Runtime.getRuntime
Bean常用的装配方式有3种:基于xml的装配基于Annotation(注解)的装配基于Java配置类的装配基于xml的装配在xml文件中配置Bean。如果依赖
java多线程的几种实现方法总结1.多线程有几种实现方法?同步有几种实现方法?多线程有两种实现方法,分别是继承Thread类与实现Runnabl
本文描述两种方式使用java代码执行shell命令,首先使用Runtime类调用exce方法,其次使用ProcessBuilder实例实现更灵活的方式。1.环境