一个由于不同微服务框架混搭导致BeanPostProcessors处理bean异常导致的问题
前天到昨天晚上,某开发报告了一个问题,我们的一个应用程序接入了腾讯的TSF微服务框架后,使用feign访问接口,会导致token丢失,无法解决。
大体介绍下项目情况,我们的应用使用了某第三方微服务框架,不是源生的springcloud或springcloud alibaba框架,第三方厂家基于springcloud构建的微服务框架,这里我就不具体说公司名吧,我就叫E框架吧。
为什么要这么混搭呢?首选,我们的应用选择的开发框架是E框架,这个是历史问题,我接手时就是这样了。其次,当前国产化要求,一定要把应用部署TSF平台中,并且一定要使用TSF的微服务框架。因此就有了该混搭。
开发跟我描述的问题B3Propagation.FACTORY的bean没有被初始化,因为第三方框架实现了一个BeanPostProcessors,而这个BeanPostProcessors会对B3Propagation.FACTORY进行处理,处理后的B3Propagation.FACTORY重新实现了injector和extractor方法,而正是这两个方法,对请求进行处理,奖请求中的Authorization添加到了feign求请中header中,以及添加了E框架用于网关校验的其他参数。现在没必要去纠结为什么要这么做,毕竟我们用的是别人的框架,这是即成的事实,主要是解决问题。
虽然他是这么说,但我还是持怀疑态度,我查看了日志,发现E框架涉及的bean都报了以下问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @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; } |
这里会使用getBeanPostProcessors获取所有继承了BeanPostProcessors的类,通过断点我发现,初始化B3Propagation.FACTORY后,E框架的所有BeanPostProcessors都不在AbstractAutowireCapableBeanFactory的beanPostProcessors中。到这里大概有点眉目了,应该就是两个框架都去初始化了B3Propagation.FACTORY,以下是org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration初始化过程,可以看到,bean的初始化方法标记为“ConditionalOnMissingBean",意思是不存在该bean的时候,才会创建该bean。TSF框架对bean进行创建 后,E框架初始化时再进行创建时,发现bean存在,则不会再创建了。
@Bean @ConditionalOnMissingBean Propagation.Factory sleuthPropagation(SleuthProperties sleuthProperties) { if (sleuthProperties.getBaggageKeys().isEmpty() && sleuthProperties.getPropagationKeys().isEmpty()) { return B3Propagation.FACTORY; } ExtraFieldPropagation.FactoryBuilder factoryBuilder; if (this.extraFieldPropagationFactoryBuilder != null) { factoryBuilder = this.extraFieldPropagationFactoryBuilder; } else { factoryBuilder = ExtraFieldPropagation .newFactoryBuilder(B3Propagation.FACTORY); } if (!sleuthProperties.getBaggageKeys().isEmpty()) { factoryBuilder = factoryBuilder // for HTTP .addPrefixedFields("baggage-", sleuthProperties.getBaggageKeys()) // for messaging .addPrefixedFields("baggage_", sleuthProperties.getBaggageKeys()); } if (!sleuthProperties.getPropagationKeys().isEmpty()) { for (String key : sleuthProperties.getPropagationKeys()) { factoryBuilder = factoryBuilder.addField(key); } } return factoryBuilder.build(); }
所以,第一个处理方案就是如何让bean在E框架初始化后再进行初始化,于是进行了第一轮修改,于是有了如下类,有些病急乱投医的感觉:
@Configuration @AutoConfigureAfter(com.xxx.xxx.xxx.xxx.tracing.autoconfig.SDKTraceAutoConfiguration.class) public class XxxBeanConfig implements CommandLineRunner { @Autowired TraceAutoConfiguration traceAutoConfiguration; @Autowired(required = false) ExtraFieldPropagation.FactoryBuilder extraFieldPropagationFactoryBuilder; @Bean @Primary @DependsOn("sdkPropagationFactoryPostProcessor") Propagation.Factory sleuthPropagation(SleuthProperties sleuthProperties, SDKTraceAutoConfiguration sdkTraceAutoConfiguration) { if (sleuthProperties.getBaggageKeys().isEmpty() && sleuthProperties.getPropagationKeys().isEmpty()) { return B3Propagation.FACTORY; } ExtraFieldPropagation.FactoryBuilder factoryBuilder; if (this.extraFieldPropagationFactoryBuilder != null) { factoryBuilder = this.extraFieldPropagationFactoryBuilder; } else { factoryBuilder = ExtraFieldPropagation .newFactoryBuilder(B3Propagation.FACTORY); } if (!sleuthProperties.getBaggageKeys().isEmpty()) { factoryBuilder = factoryBuilder // for HTTP .addPrefixedFields("baggage-", sleuthProperties.getBaggageKeys()) // for messaging .addPrefixedFields("baggage_", sleuthProperties.getBaggageKeys()); } if (!sleuthProperties.getPropagationKeys().isEmpty()) { for (String key : sleuthProperties.getPropagationKeys()) { factoryBuilder = factoryBuilder.addField(key); } } return factoryBuilder.build(); } /** * Callback used to run the bean. * * @param args incoming main method arguments * @throws Exception on error */ @Override public void run(String... args) throws Exception { } }
我试图通过人工干预初始化顺序的方式来进行修改,除了上面已有代码,其实还用过@Order等,然并卵,断点发现,sleuthPropagation初始化后,其实E框架的SDKPropagationFactoryPostProcessor已经初始化了,但是并没有装到AbstractAutowireCapableBeanFactory的beanPostProcessors中。那这就不是初始化顺序问题了。这个时候,我本来是想解决为什么SDKPropagationFactoryPostProcessor初始化完成后,没有被装到AbstractAutowireCapableBeanFactory的beanPostProcessors里的,转念一想,这可能是框架问题,而这两个框架,我们都没有源码,就算是这个问题,也无法解决。那就手动使用SDKPropagationFactoryPostProcessor得了吧。好,得了吧,这个是内部类。。。但是这难不倒我,不就是个内部类么?我用你的接口不就行了么?于是有了以下的修改,主要看红色部分:
@Autowired
AbstractApplicationContext abstractApplicationContext;
Propagation.Factory sleuthPropagation(SleuthProperties sleuthProperties, SDKTraceAutoConfiguration sdkTraceAutoConfiguration) { BeanPostProcessor processor = (BeanPostProcessor)abstractApplicationContext.getBean("sdkPropagationFactoryPostProcessor"); if (sleuthProperties.getBaggageKeys().isEmpty() && sleuthProperties.getPropagationKeys().isEmpty()) { return (Propagation.Factory) processor.postProcessAfterInitialization(B3Propagation.FACTORY,"sleuthPropagation"); } ExtraFieldPropagation.FactoryBuilder factoryBuilder; if (this.extraFieldPropagationFactoryBuilder != null) { factoryBuilder = this.extraFieldPropagationFactoryBuilder; } else { factoryBuilder = ExtraFieldPropagation .newFactoryBuilder(B3Propagation.FACTORY); } if (!sleuthProperties.getBaggageKeys().isEmpty()) { factoryBuilder = factoryBuilder // for HTTP .addPrefixedFields("baggage-", sleuthProperties.getBaggageKeys()) // for messaging .addPrefixedFields("baggage_", sleuthProperties.getBaggageKeys()); } if (!sleuthProperties.getPropagationKeys().isEmpty()) { for (String key : sleuthProperties.getPropagationKeys()) { factoryBuilder = factoryBuilder.addField(key); } } return (Propagation.Factory) processor.postProcessBeforeInitialization(factoryBuilder.build(),"sleuthPropagation"); }
我采用了getBean的方式获取类实例,使用接口BeanPostProcessor 进行转换,再调用postProcessAfterInitialization接口,并且该bean声明为@Primary,因此不会被覆盖。经测试,解决问题。
以上就是解决问题的过程,其实解决问题的方法相当简单,分析问题比较难,也怪自己学艺不精。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?