zoukankan      html  css  js  c++  java
  • Spring Bean名称重复ConflictingBeanDefinitionException解决

    冲突分析

    如果我们希望将相同名称的类放入spring中时,如果未指定bean名称,则会抛出异常:

    Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'xxxx' for bean class [xxx] conflicts with existing, non-compatible bean definition of same name and class [xxx]
    

    翻看Spring源码得知,当我们使用注解创建bean时,spring使用了AnnotationBeanNameGenerator来创建bean的名称。

            if (definition instanceof AnnotatedBeanDefinition) {
                String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
                if (StringUtils.hasText(beanName)) {
                    // Explicit bean name found.
                    return beanName;
                }
            }
            // Fallback: generate a unique default bean name.
            return buildDefaultBeanName(definition, registry);
    

    如果我们项目中存在相同名称的类,而在使用注解时(@Service、@Component)指定了不同的name,则不会抛出异常,否则Spring使用类的名称作为bean的名称,则会抛出异常。

    但有些特殊场景,如多版本模块,我们不希望版本号污染类名,而希望以包的形式控制。则此时我们需要替换spring的默认名称生成器。

    - com.v1
        - UserController
    - com.v2
        - UserController
    

    冲突解决

    创建全类名的BeanNameGenerator

    此处代码摘自ConfigurationClassPostProcessor.importBeanNameGenerator。

    public class AnnotationBeanNameGenerator implements BeanNameGenerator {
        @Override
        public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
            if (definition instanceof AnnotatedBeanDefinition) {
                String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
                if (StringUtils.hasText(beanName)) {
                    // Explicit bean name found.
                    return beanName;
                }
            }
            String beanClassName = definition.getBeanClassName();
            Assert.state(beanClassName != null, "No bean class name set");
            return beanClassName;
        }
    }
    

    Spring Boot

    SpringBoot提供了在启动时传入BeanNameGenerator的方式,可以修改Spring扫描注解时使用的名称。

    @SpringBootApplication
    @ComponentScan(nameGenerator = AnnotationBeanNameGenerator.class)
    

    Mybatis

    而Mybatis并不是由Spring直接扫描的,而是由其本身自主扫描到Mapper而注入到Spring中。关键代码如下:

      @Override
      public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    
        AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
        .....
        .....
    }
    

    mybatis创建了spring提供的ClassPathMapperScanner,其默认使用的扔是AnnotationBeanNameGenerator。而mybatis也提供了修改方式。

    @MapperScan(nameGenerator = AnnotationBeanNameGenerator.class)
    

    SpringBoot && Mybatis

    完整代码如下:

    @SpringBootApplication
    @ComponentScan(nameGenerator = Application.SpringBeanNameGenerator.class)
    @MapperScan(value = "**.mapper", markerInterface = BaseMapper.class, nameGenerator = Application.SpringBeanNameGenerator.class)
    public class Application {
        public static class SpringBeanNameGenerator extends AnnotationBeanNameGenerator {
            @Override
            protected String buildDefaultBeanName(BeanDefinition definition) {
            if (definition instanceof AnnotatedBeanDefinition) {
                String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
                if (StringUtils.hasText(beanName)) {
                    // Explicit bean name found.
                    return beanName;
                }
            }
                return definition.getBeanClassName();
            }
        }
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    

    暴力覆盖方案

    上述方式到是一般情况下足够了,但如果此时项目中又存在一个类似mybatis的组件,那么你仍需要配置BeanNameGenerator。我们可以暴力覆盖原本的AnnotationBeanNameGenerator,
    建立与其包名相同,类名相同的文件在项目中:

     

     

    image.png
    public class AnnotationBeanNameGenerator implements BeanNameGenerator {
        private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component";
    
    
        

    此时无须配置任何配置,默认会走此BeanNameGenerator。

    版本控制



    作者:BeRicher
    链接:https://www.jianshu.com/p/74c801fd70f4
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 相关阅读:
    IIS的各种身份验证详细测试
    HTTP Error 401.3 Unauthorized Error While creating IIS 7.0 web site on Windows 7
    C/S and B/S
    WCF ContractFilter mismatch at the EndpointDispatcher exception
    Configure WCF
    Inheritance VS Composition
    Unhandled Error in Silverlight Application, code 2103 when changing the namespace
    Java RMI VS TCP Socket
    Principles Of Object Oriented Design
    Socket处理发送和接收数据包,一个小实例:
  • 原文地址:https://www.cnblogs.com/telwanggs/p/15030358.html
Copyright © 2011-2022 走看看