【实战】Spring生成beanName冲突的解决之道:附源码分析

简介: 【实战】Spring生成beanName冲突的解决之道:附源码分析

 image.gif编辑

 

一、问题描述

最近公司项目打算模块化,其实一个原因也是为了能够整合公司多个业务的代码,比如一个资源xxx,两个业务中都有对这个资源的管理,虽然是一个资源,但是是完全不同的定义、完全不同的表、不同的处理逻辑。所以打算把类名弄成一样的,但是包名不一样。

但这样会产生问题,按照Spring的默认beanName生成规则,会直接将类名首字母小写作为bean的名字,如两个模块里的这个资源都叫xxxJob,这样在Spring启动的时候就会报错。错误如下conflicts with existing, non-compatible bean definition of same name and class [xxxxJob],意思就是说两个bean同名了,这样启动就报错了。

二、源码分析

解决方法我们可以手动修改bean名称的生成策略,这里直接就是用实现类的全限定名称(com.abc.job.xxxJob)作为bean的名称。

翻翻源码,我们先来看默认生成规则:

1.判断bean上的注解中是否指定了名字,如果指定直接返回,否则去构造bean的名称。

public class AnnotationBeanNameGenerator implements BeanNameGenerator {
    @Override
  public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
    if (definition instanceof AnnotatedBeanDefinition) {
                //看bean上的注解中是否指定了bean的名字
      String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
      if (StringUtils.hasText(beanName)) {
        // 如果指定了名字,直接返回
        return beanName;
      }
    }
    // 没有指定名字,去构造一个bean的名称
    return buildDefaultBeanName(definition, registry);
  }
    。。。
}

image.gif

2.bean构造逻辑

protected String buildDefaultBeanName(BeanDefinition definition) {
    String beanClassName = definition.getBeanClassName();
    Assert.state(beanClassName != null, "No bean class name set");
    String shortClassName = ClassUtils.getShortName(beanClassName);
    return Introspector.decapitalize(shortClassName);
  }

image.gif

这里definition.getBeanClassName()是获取全限定名称的,ClassUtils.getShortName是获取类名的,下面的Introspector.decapitalize实际上就是把首字母变小写的。

public static String getShortName(String className) {
    Assert.hasLength(className, "Class name must not be empty");
    int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR);
    int nameEndIndex = className.indexOf(CGLIB_CLASS_SEPARATOR);
    if (nameEndIndex == -1) {
      nameEndIndex = className.length();
    }
    String shortName = className.substring(lastDotIndex + 1, nameEndIndex);
    shortName = shortName.replace(INNER_CLASS_SEPARATOR, PACKAGE_SEPARATOR);
    return shortName;
  }

image.gif

核心是getShortName方法,其实就是个字符串截取,将包和后缀都去掉,生成一个短bean的名称。

三、解决方案

到这里我们应该看得很清楚了,不同包名但相同类名的类,Spring的默认生成beanName规则生成出来的名称是一样的,难怪Spring在启动会报错了,那我们要做的就是修改beanName的生成规则,做法如下:

我们这里要设置为全限定名称,我们可以新写一个类,假设叫MyBeanNameGenerator

,然后继承AnnotationBeanNameGenerator之后重写buildDefaultBeanName方法,返回definition.getBeanClassName(),这样我们这个生成策略就写好了。代码如下:

public class BeanNameGenerator extends AnnotationBeanNameGenerator{
  @Override
  public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
        //这里也也可以加自己的逻辑,比如只有特定包下面的bean需要使用这种生成规则,其他包下面的bean还采用默认生成规则,各位看官自己去发挥即可
    return  definition.getBeanClassName();
}

image.gif

接下来对@ComponentScan注解添加一个nameGenerator属性就好了,指定为我们自定义的bean生成策略。

@ComponentScan(basePackages = "com.xxx.job.**",nameGenerator = MyBeanNameGenerator.class)

image.gif

这样就完美了,这时候所有bean的默认名称就是我们设置的全限定名了,不过如果我们在类上显式的写了bean的id的话,还是会用我们自定义的bean的name的。

最后:感谢我司可少大力支持!

相关文章
|
1月前
|
监控 Java 应用服务中间件
Spring Boot整合Tomcat底层源码分析
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置和起步依赖等特性,大大简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是其与Tomcat的整合。
61 1
|
3天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
25天前
|
XML Java 数据格式
Spring Core核心类库的功能与应用实践分析
【12月更文挑战第1天】大家好,今天我们来聊聊Spring Core这个强大的核心类库。Spring Core作为Spring框架的基础,提供了控制反转(IOC)和依赖注入(DI)等核心功能,以及企业级功能,如JNDI和定时任务等。通过本文,我们将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring Core,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
53 14
|
1月前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
38 1
|
1月前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
35 1
|
2月前
|
自然语言处理 Java API
Spring Boot 接入大模型实战:通义千问赋能智能应用快速构建
【10月更文挑战第23天】在人工智能(AI)技术飞速发展的今天,大模型如通义千问(阿里云推出的生成式对话引擎)等已成为推动智能应用创新的重要力量。然而,对于许多开发者而言,如何高效、便捷地接入这些大模型并构建出功能丰富的智能应用仍是一个挑战。
284 6
|
2月前
|
缓存 JavaScript Java
Spring之FactoryBean的处理底层源码分析
本文介绍了Spring框架中FactoryBean的重要作用及其使用方法。通过一个简单的示例展示了如何通过FactoryBean返回一个User对象,并解释了在调用`getBean()`方法时,传入名称前添加`&`符号会改变返回对象类型的原因。进一步深入源码分析,详细说明了`getBean()`方法内部对FactoryBean的处理逻辑,解释了为何添加`&`符号会导致不同的行为。最后,通过具体代码片段展示了这一过程的关键步骤。
Spring之FactoryBean的处理底层源码分析
|
2月前
|
缓存 NoSQL Java
Spring Boot与Redis:整合与实战
【10月更文挑战第15天】本文介绍了如何在Spring Boot项目中整合Redis,通过一个电商商品推荐系统的案例,详细展示了从添加依赖、配置连接信息到创建配置类的具体步骤。实战部分演示了如何利用Redis缓存提高系统响应速度,减少数据库访问压力,从而提升用户体验。
153 2
|
2月前
|
Java BI API
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
这篇文章介绍了如何在Spring Boot项目中整合iTextPDF库来导出PDF文件,包括写入大文本和HTML代码,并分析了几种常用的Java PDF导出工具。
650 0
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
|
1月前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
30 0