当前位置: 首页 > 后端技术 > Java

Spring竟然可以创建“重复”名称的bean?—一次项目中存在多个bean名称重复问题的排查

时间:2023-04-02 10:12:36 Java

Spring可以创建具有“重复”名称的bean吗?—排查项目中多个bean名称重复的问题启动时会报错:但是我在我们的spring项目中发现了两个同名的bean,项目也可以正常启动,对应的bean也可以正常使用。因为项目中会用到多个redis集群,所以配置了多个redis环境,在id上进行区分。但是在配置redis环境的时候,两个环境bean的id是一样的。//创建了一个名为ccProvider的bean//这个也是ccProvider大家都知道标签可以声明一个bean,肯定会被spring解析使用,那为什么呢in有两个相同的bean名称但是不会报错吗?可以看到我们创建的bean是正常的,功能可以使用2.排错过程2.1尝试直接找到创建重复bean的位置首先debug尝试找到创建重复bean时的相关资料,看看有没有思路,然后重启项目,选择debug模式,但是运行后IDEA提示断点已经跳过,查了一些资料和方法都不行,放弃了2.2从其父bean的创建寻找思路放弃了以上思路后,想到可以利用之前学过的spring源码,从代码层面排查这个问题,将断点设置到该处reidsbean创建的地方,果然断点可以来这里那么我们的思路就很简单了。在spring中,组装属性的步骤发生在:populateBean(beanName,mbd,instanceWrapper)过程中,如果发现它的属性也是一个bean,那么会先获取这个bean,如果不存在它的属性会先创建bean,创建完成后将属性bean赋值给待组装的bean。//循环遍历要组装的bean的所有属性for(PropertyValuepv:original){if(pv.isConverted()){deepCopy.add(pv);}else{StringpropertyName=pv.getName();对象原始值=pv。获取值();//获取待组装的beanObjectresolvedValue=valueResolver.resolveValueIfNecessary(pv,originalValue);对象convertedValue=resolvedValue;booleanconvertible=bw.isWritableProperty(propertyName)&&!PropertyAccessorUtils.isNestedOrIndexedProperty}Name)from在debug中也可以看到我们的bean只有一个属性,就是providers,符合我们在上面的xml。我们从实际创建要组装的bean的地方开始,找出何时开始创建bean的私有Object。resolveInnerBean(ObjectargName,StringinnerBeanName,BeanDefinitioninnerBd){RootBeanDefinitionmbd=null;try{...//在实际创建bean的地方ObjectinnerBean=this.beanFactory.createBean(actualInnerBeanName,mbd,null);if(innerBeaninstanceofFactoryBean){布尔合成=mbd.isSynthetic();返回这个.beanFactory.getObjectFromFactoryBean((FactoryBean)innerBean,actualInnerBeanName,!synthetic);}else{返回innerBean;}}catch(BeansExceptionex){thrownewBeanCreationException(this.beanDefinition.getResourceDescription(),this.beanName,"无法创建内部bean'"+innerBeanName+"'"+(mbd!=null&&mbd.getBeanClassName()!=null?"类型["+mbd.getBeanClassName()+"]":"")+"whilesetting"+argName,ex);}}createBean(actualInnerBeanName,mbd,null)这行代码,看过spring源码的人想必都不陌生。通过这个方法,可以得到要创建的bean对象。从debug中也可以看到真正要创建的beanName已经换成了我们要组装的属性ccProvider。至此我们发现,与我们的预期一致,标签确实会创建一个bean对象,无论它在哪里。那么这里的beanName为什么不怕重复呢?2.3为什么这里的bean没有重复的问题?回顾刚才提到的spring,重名的bean是不允许的。其实很好理解,因为在创建bean的过程中,我们会将创建的bean以beanName作为key。在缓存的map中,如果我们有两个同名的bean,那么当有重复的bean时,第二个bean会覆盖第一个bean。在这种情况下,没有唯一性。当其他bean需要依赖重复的bean时,它们可能不会返回同一个bean。那么这里为什么不重复这两个bean呢?其实细心的读者已经发现,这里的变量名是innerBean,说明是一个内部bean,那么innerBean和普通bean有什么区别呢?为什么innerBean没有重名的问题呢?我们重新梳理一下创建普通bean的过程:其实答案已经很明显了:如果我们创建一个普通bean,创建完成后bean会被放到缓存中,如果还有其他bean要使用,它们会直接从缓存中取出来就可以了,beanName不能重复也是基于这个考虑。innerBean的创建是以createBean()的原子操作为前提的。它只会返回创建的bean,不会添加到spring的bean缓存中,所以不存在beanName重复的问题。3.总结3.1为什么spring可以有“重名”的bean。这里我们重新梳理一下bean的创建过程:在spring注入一个普通bean的过程中,会为反射创建的空属性对象赋值。如果发现它所依赖的属性也是一个bean,那么会先获取到bean,如果获取不到,则改为创建bean。这时要创建的bean就变成了innerBean,不会被其他springbean共享,所以名字可以重复。3.2innerBean的用法还是我们刚才的例子,我们可以重写如下:><propertyname="password"value="${r2m.password}"/>在上面的示例中,我们定义了一个普通bean并将其引用到我们想要的属性。这时候ccProviderRef作为一个普通的bean可以被其他bean引用,但是此时bean的名字不能重复。

最新推荐
猜你喜欢