掉进了Spring @Configuration 坑里,导致阿里云 RocketMq 消费者状态(同一个进程出了两client)异常

42 篇文章 0 订阅
6 篇文章 0 订阅

掉进了Spring @Configuration 坑里,导致阿里云 RocketMq 消费者状态(同一个进程出了两client)异常

1 案发现场

复现代码

@Configuration
public class BeanConflict {

    @Bean("MyAtomic1")
    public MyAtomic getMyAtomic(@Value("${OONN}") String oonn){
        System.out.println(oonn);
        System.out.println("--------first--------");
        return new MyAtomic();
    }

    @Bean("MyAtomic")
    public MyAtomic getMyAtomic(){
        System.out.println("---------------------");
        System.out.println("--------second--------");
        return new MyAtomic();
    }
}

2 源码分析 基于 spring-context 5.1.6

refresh

----->org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors // 解析Spring Bean(定义)

// 遍历工厂后处理器 org.springframework.beans.factory.config.BeanFactoryPostProcessor 接口实现类 目的是修改 内部 bean factory

// 此时 configuration class

----->org.springframework.context.annotation.ConfigurationClassPostProcessor // Spring Bean ConfigurationClass 后处理器

// 里面执行了所有的Parse each @Configuration class 注解的类 最简单的项目也有80-90个 @Configuration class 放入一个名为configClasses 的 LinkedHashSet中

// 逐一解析,生成Spring Bean====>//class级别

---->org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass

// 加载Spring Bean 依据 BeanMethod

// 内部逐一解析 ,生成Spring Bean====>//method级别

----->org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod

// 如果beanName相同会进行重载判断(本文不关心)

---->org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#isOverriddenByExistingDefinition

// 通常会产生 org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.ConfigurationClassBeanDefinition 类型的 Spring Bean 并放到 org.springframework.beans.factory.support.DefaultListableBeanFactory#beanDefinitionMap 中,此时会有

---->// Cannot modify startup-time collection elements anymore (for stable iteration)

---->org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition

---->synchronized (this.beanDefinitionMap) { ...}同步执行

// 如果一个类多个 BeanMethod重复

// 循环--->内部逐一解析 ,生成Spring Bean====>//method级别

----->org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod

---->org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass

// 循环---> 逐一解析,生成Spring Bean====>//class级别

如下图

不同方法

相同方法名

// 经过refresh的其他处理终于来到了 bean实例化,属性设置,初始化

----->org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons // 实例化所有Spring Bean

---->org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance // 1159行 会执行一个工厂方法的获取

// 同理MyAtomic2 Spring Bean的创建

// 确定了使用工厂方法来实例化bean

---->org.springframework.beans.factory.support.ConstructorResolver#instantiateUsingFactoryMethod // 匹配具体的方法

// 怎么匹配的呢?Choose this factory method if it represents the closest match.

// 算法解释 不是宽大模式也不是严格模式下,找距离最近(最小)的方法

// 而上面说的方法列表是org.springframework.beans.factory.support.ConstructorResolver#getCandidateMethods决定的 Arraylist

// beanName MyAtomic2 MyAtomic1 执行的反射方法一模一样!!!

// 结果就是调用相同逻辑来实例化bean----->同一个妈生的!!!

// 线上情况就是

-----> Object result = factoryMethod.invoke(factoryBean, args); // 反射获取 // 注意第一个参数 factoryBean 是CGLIB代理类

----->callback 做织入(参数依赖等获取如第一个参数@Value获取)--->org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#intercept

----->重复执行客户端构建逻辑,于是两个相同订阅的com.aliyun.openservices.ons.api.order.OrderConsumer 就出来了!两个客户端走了相同的订阅!topic

3 小结

对于 @Configuration + @Bean 注解一定要区分方法名,因为Spring Bean实例化使用的工厂方法,反射(+AOP)来生是生成bean实例,如果方法区分度很低那就有问题了!!

所以像配置rocketMq 客户端之类的Bean,一定要区分方法名,copy code 必须重新命名方法名!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

自驱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值