【实战】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的。

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

相关文章
|
4天前
|
Java 应用服务中间件 测试技术
深入探索Spring Boot Web应用源码及实战应用
【5月更文挑战第11天】本文将详细解析Spring Boot Web应用的源码架构,并通过一个实际案例,展示如何构建一个基于Spring Boot的Web应用。本文旨在帮助读者更好地理解Spring Boot的内部工作机制,以及如何利用这些机制优化自己的Web应用开发。
30 3
|
4天前
|
安全 Java 开发者
深入理解Spring Boot配置绑定及其实战应用
【4月更文挑战第10天】本文详细探讨了Spring Boot中配置绑定的核心概念,并结合实战示例,展示了如何在项目中有效地使用这些技术来管理和绑定配置属性。
15 1
|
4天前
|
安全 Java 测试技术
Spring Boot集成支付宝支付:概念与实战
【4月更文挑战第29天】在电子商务和在线业务应用中,集成有效且安全的支付解决方案是至关重要的。支付宝作为中国领先的支付服务提供商,其支付功能的集成可以显著提升用户体验。本篇博客将详细介绍如何在Spring Boot应用中集成支付宝支付功能,并提供一个实战示例。
41 2
|
4天前
|
Java 关系型数据库 MySQL
【Java Spring开源项目】新蜂(NeeBee)商城项目运行、分析、总结
【Java Spring开源项目】新蜂(NeeBee)商城项目运行、分析、总结
15 4
|
4天前
|
开发框架 监控 Java
深入探索Spring Boot的监控、管理和测试功能及实战应用
【5月更文挑战第14天】Spring Boot是一个快速开发框架,提供了一系列的功能模块,包括监控、管理和测试等。本文将深入探讨Spring Boot中监控、管理和测试功能的原理与应用,并提供实际应用场景的示例。
16 2
|
4天前
|
Java Spring 容器
深入理解Spring Boot启动流程及其实战应用
【5月更文挑战第9天】本文详细解析了Spring Boot启动流程的概念和关键步骤,并结合实战示例,展示了如何在实际开发中运用这些知识。
19 2
|
4天前
|
JavaScript Java 开发者
Spring Boot中的@Lazy注解:概念及实战应用
【4月更文挑战第7天】在Spring Framework中,@Lazy注解是一个非常有用的特性,它允许开发者控制Spring容器的bean初始化时机。本文将详细介绍@Lazy注解的概念,并通过一个实际的例子展示如何在Spring Boot应用中使用它。
21 2
|
4天前
|
XML Java API
Spring Boot 整合 LiteFlow 规则引擎:概念与实战
【4月更文挑战第30天】在现代软件开发中,规则引擎允许我们以声明式的方式定义业务逻辑和决策路径。LiteFlow 是一个轻量级、易于使用的组件式规则引擎,它可以与 Spring Boot 应用无缝整合。本文将介绍如何在 Spring Boot 项目中引入 LiteFlow,实现灵活的业务流程管理。
32 0
|
4天前
|
安全 Java 测试技术
利用Java反射机制提高Spring Boot的代码质量:概念与实战
【4月更文挑战第29天】Java反射机制提供了一种强大的方法来在运行时检查或修改类和对象的行为。在Spring Boot应用中,合理利用反射可以提高代码的灵活性和可维护性。本篇博客将探讨Java反射的核心概念,并展示如何通过反射提高Spring Boot项目的代码质量。
31 0
|
4天前
|
监控 Java 测试技术
Spring Boot与事务钩子函数:概念与实战
【4月更文挑战第29天】在复杂的业务逻辑中,事务管理是确保数据一致性和完整性的关键。Spring Boot提供了强大的事务管理机制,其中事务钩子函数(Transaction Hooks)允许开发者在事务的不同阶段插入自定义逻辑。本篇博客将详细探讨事务钩子函数的概念及其在Spring Boot中的应用。
37 1