同包同名的配置类Spring是如何处理的?
先看一下测试用例





很遗憾加载了ServiceA,为什么会出现这种情况,我们下面分析一下
为什么会导致这种现象?
扫描的实现类为ClassPathBeanDefinitionScanner,Spring中所有的秒扫方法实现都是委托到这个类来处理的,我们直接看最原始的扫描结果.可以明显看到这里虽然同包同名但是file的路径是不同的,所以在扫描处理这个阶段是可以区分的,但是实际上变为ScannedGenericBeanDefinition这个对象的时候,它的基类AbstractBeanDefinition的equals方法经过自身的重写,并没有去考虑实际Resource的不同.也就是存在同包同名的Class这里也只会注册到容器一个对象(同包同名后面的会替换掉前面的).
可是虽然同包同名,但是我们通过@Profile进行了区分,这里应该是会把那个不满足条件的Class跳过才对,也就是应该加载了对的那个Class.这里确实是加载的对的那一个,那么为什么加载了对了那个BeanDefinition我们的注解却没有生效呢?这里实际上只解析了指定路径扫出来的结果,并没有处理配置类的递归解析,我们继续往下看看配置类的注解如何解析的.

这里看到一旦扫描完成直接判断扫描到的BeanDefiition是否含有配置类@Configuration注解,如果有的话递归解析,但是parse方法并没有直接使用这个BeanDefiition已经解析出的注解而是自己从新加载



可以看到这里加载了错误的元信息回去,但是实际上这里解析不会出问题的,因为processConfigurationClass方法会判断@Conditional注解的条件.

到这里都没有载入错误的信息,那到底这些错误的注解实在哪里解析的?继续往下看,追踪到所有解析的入口在ConfigurationClassPostProcessor的processConfigBeanDefinitions方法.
先看一下主要的相关逻辑.去除了部分其他逻辑细节

在第一次扫描完之后原来外层还会再判断一次配置类是否加载,这里可是从BeanDefiition取出来正确的元信息了,那么会加载正确吗?


下面就找到了最终加载错误的元凶了...



可以看到最后还是通过正确的BeanDefinition跳过验证,后面通过缓存加载了错误的类元信息,导致后面解析类元信息解析错误.
3总结
尽量避免同包,每个项目的package都应该有自己独立的顶层package这样能避免同包同名导致的错误.相同包内如果同名会提示错误的,所以同项目同包目录下不担心同名问题.
4其他
示例源码:github源码地址
网友评论