springboot项目中出现同名bean异常报错的解决方法

 更新时间:2024年01月09日 10:35:02   作者:linyb极客之路  
这篇文章给大家聊聊springboot项目出现同名bean异常报错如何修复,文中通过代码示例给大家介绍解决方法非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下

前言

最近业务部门接手供方的项目过来二开,其中有个认证实现因为业务需要,需要替换原有供方实现的逻辑。大概伪代码如下。供方提供的接口以及默认实现形如下

public interface AuthCodeService {
    default Boolean check() {
        return true;
    }

}

@Service("authCodeService")
public class AuthCodeImpl implements AuthCodeService {

    public Boolean check() {
	    // doBiz
        return true;
    }
}

业务的替换实现如下

@Service
public class BizAuthCodeImpl implements AuthCodeService {

    public Boolean check() {
	    // doOtherBiz
        return true;
    }
}

然而项目运行的时候,发现走的认证逻辑始终是供方的逻辑,而非业务重写后的逻辑。

平时因为跟业务的技术负责人走得比较近,他就私下找我交流一下思路。一开始我给他提的建议是说在你定制的业务类上加@Primary试下,他说他加了但没效果。

于是我就跟他说不然你直接改供方源码的默认实现,他给的答复供方没提供源码,只提供jar。我就跟他说,这也可以改,你项目创建一个和供方实现一模一样的类,就是包名和类名一模一样,利用类的加载顺序实现。技术负责人又觉得这样不好。

后面那个技术负责人想了一个方式,就是他将业务定制bean名称和供方提供的bean名称一样,形如下

@Service("authCodeService")
public class BizAuthCodeImpl implements AuthCodeService {

    public Boolean check() {
	    // doOtherBiz
        return true;
    }
}

然后项目启动,直接报了如下错

org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'authCodeService' for bean class 

他就跟我说这个异常怎么修复,铺垫了这么久,引来了今天要聊的话题,同名bean异常报错如何修复

解决思路

首先抛出一个观点,在同个spring容器中,是不能出现同名的bean,因此解决的思路要么搞成不同的spring容器,要么就是排除多个同名的bean,只保留自己想要的那个。要么就是将bean改个名字。今天介绍的思路就是排除同名bean,只保留自己想要的bean

实现方法

1、方法一:通过@ComponentScan进行排除

示例配置

在springboot的启动类上加上形如下内容

@ComponentScan(basePackages = {"com.github.lybgeek"},excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = AuthCodeImpl.class)})

这边有个注意点是当你启动类上同时存在@SpringBootApplication和@ComponentScan注解时,@ComponentScan注解指定的扫描包路径会覆盖@SpringBootApplication的包路径。

我将第一种方案告诉业务技术负责人后,他试了一下,果然没报错,但是后面出现一个问题,他说@SpringBootApplication的属性exclude()失效了,导致他项目要排除的自动装配类失效了。于是就有了第二种方案

2、方法二:自定义TypeExcludeFilter

我们点开@SpringBootApplication,可以看到如下内容

@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

既然@SpringBootApplication和@ComponentScan同时标注在启动类上会有一定冲突,我们就遵循@SpringBootApplication提供的扩展方案就好了,自己写一个TypeExcludeFilter进行排除

实现步骤

1、自定义TypeExcludeFilter

public class CustomTypeExcludeFilter extends TypeExcludeFilter {

    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
         String className = metadataReader.getClassMetadata().getClassName();
         return AuthCodeImpl.class.getName().equals(className);

    }
}

2、将自定义TypeExcludeFilter注入到spring容器 中

这边有个特别需要注意的细节点,因为TypeExcludeFilter是要排除bean的,因此他注入的时机至少要在其他bean注入之前,具体来说就是在容器上下文refresh执行之前,就得完成注入。在refresh之前执行的扩展点有很多,我们就挑一个,我们以实现ApplicationContextInitializer为例

public class CustomTypeExcludeFilterApplicationContextInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory();
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(CustomTypeExcludeFilter.class);
        AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();

        defaultListableBeanFactory.registerBeanDefinition("customTypeExcludeFilter",beanDefinition);
    }
}

3、将ApplicationContextInitializer 的实现类放在/META-INF/spring.factories

org.springframework.context.ApplicationContextInitializer=\
com.github.lybgeek.context.CustomTypeExcludeFilterApplicationContextInitializer

按照上面三步执行,就可以排除自己想排除的bean

总结

当项目中出现同名bean冲突时,如果可以的话,就尽量换个其他bean名称来解决

后面业务负责人并没有采用我上述的方案,我们回归业务负责人他们项目诉求,他们的需求是要他们自定义认证的逻辑能生效,而非解决同名bean冲突。

业务负责人他们最后的方案是通过加@Primary注解解决,他之前加了觉得没生效,是因为他们项目引的自定义认证逻辑的旧包,那个旧包没加@Primary注解,后面把包升级就解决了

以上就是springboot项目中出现同名bean异常报错的解决方法的详细内容,更多关于springboot出现同名bean的资料请关注脚本之家其它相关文章!

相关文章

  • Spring高阶用法之自定义业务对象组件化

    Spring高阶用法之自定义业务对象组件化

    这篇文章主要介绍了Spring高阶用法之自定义业务对象组件化,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • SpringBoot+thymeleaf+Echarts+Mysql 实现数据可视化读取的示例

    SpringBoot+thymeleaf+Echarts+Mysql 实现数据可视化读取的示例

    本文主要介绍了SpringBoot+thymeleaf+Echarts+Mysql 实现数据可视化读取的示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-04-04
  • springcloud使用Hystrix进行微服务降级管理

    springcloud使用Hystrix进行微服务降级管理

    这篇文章主要介绍了springcloud使用Hystrix进行微服务降级管理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • 8种常见的接口请求重试方法总结

    8种常见的接口请求重试方法总结

    在跨境业务中,可能第三方的服务器分布在世界的各个角落,所以请求三方接口的时候,难免会遇到一些网络问题,这时候需要加入重试机制了,下面小编就给大家分享几个接口重试的写法吧
    2023-11-11
  • 一文搞懂Mybatis中Mapper配置文件获取参数的五种方式

    一文搞懂Mybatis中Mapper配置文件获取参数的五种方式

    这篇文章主要介绍了Mybatis中Mapper配置文件获取参数的五种方式,文中通过代码示例讲解的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-03-03
  • java 微信随机红包算法代码实例

    java 微信随机红包算法代码实例

    这篇文章主要介绍了java 微信随机红包算法的相关资料,并附实例代码,需要的朋友可以参考下
    2016-10-10
  • 详解Java网络编程

    详解Java网络编程

    网络编程是指编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来。本文介绍了一些网络编程基础的概念,并用Java来实现TCP和UDP的Socket的编程,来让读者更好的了解其原理
    2021-06-06
  • 关于maven配置项目一直提示程序包不存在以及scope的坑

    关于maven配置项目一直提示程序包不存在以及scope的坑

    这篇文章主要介绍了关于maven配置项目一直提示程序包不存在以及scope的坑,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • 使用JAVA实现邮件发送功能的图文教程

    使用JAVA实现邮件发送功能的图文教程

    邮件发送其实是一个非常常见的需求,用户注册,找回密码等地方,都会用到,下面这篇文章主要给大家介绍了关于使用JAVA实现邮件发送功能的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-06-06
  • 深入了解Java File分隔符和Path分隔符的使用

    深入了解Java File分隔符和Path分隔符的使用

    不同的操作系统使用不同的字符作为文件和路径分隔符。当我们的应用程序需要在多个平台上运行时,我们需要正确处理这些问题。Java帮助我们选择一个合适的分隔符,本文就来聊聊Java中File分隔符和 Path分隔符的使用
    2022-07-07

最新评论