乐趣区

关于spring:聊聊spring-bean名称命名的那些事儿

前言

用了多年 spring,始终想当然把 spring 默认的 beanName 当成是类名的首字母小写,比方 HelloService 其 beanName 为 helloService。直到有天对接了供方厂商的接口,他有个类形如 ABService,于是用

getBean(“aBService”)

的形式获取 bean,后果取到是 null,一开始认为是 ABservice 没注入,前面采纳

getBean(ABService.class)

能胜利获取到 bean,阐明 ABService 是有注入到 IOC 容器中,然而为啥用 aBService 获取不到 bean?于是就用如下代码段,打印出相应 ABService 对应的 beanName

 applicationContext.getBeansOfType(ABService.class).forEach((beanName,bean)->{System.out.println(beanName + ":" + bean);
        });

打印进去的后果,如下

ABService:com.github.lybgeek.ABService@245b6b85

beanName 居然是 ABService,这就和之前的想当然有出入。于是只好查看源码

源码查看

源码查看有 2 种形式,本文的示例是 springboot 我的项目

办法一:从 main 办法间接调试断点


从图能够看出如果是以扫描注解注入模式,其 beanName 的生成规定是由

org.springframework.context.annotation.AnnotationBeanNameGenerator#generateBeanName

决定。

ps: 这种间接从 main 启动类调试起,比拟实用于工夫比拟多,或者排查毫无脉络

办法二:带着问题查看,靠猜加验证的形式

利用 idea 的 find Usage 查找援用,比方 ABService 的注解 @service,咱们能够间接查看哪个援用到 @Service,再猜想下 beanName 的生成规定

通过猜,咱们基本上就能够定位出比拟合乎咱们需要的办法

源码验证

从下面的剖析,咱们能够晓得如果是扫描 bean 注解注入的形式,其生成 beanName 规定,是在

org.springframework.context.annotation.AnnotationBeanNameGenerator

其生成规定代码如下

@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;
            }
        }
        // Fallback: generate a unique default bean name.
        return buildDefaultBeanName(definition, registry);
    }

从代码段,咱们能够看出,注解上有取名,比方 @Service(“abService”),则 beanName 为 abService,如果没有取名,则看

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);
    }
public static String decapitalize(String name) {if (name == null || name.length() == 0) {return name;}
        if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
                        Character.isUpperCase(name.charAt(0))){return name;}
        char chars[] = name.toCharArray();
        chars[0] = Character.toLowerCase(chars[0]);
        return new String(chars);
    }

其实从代码咱们就很容易看出答案了,如果类名前两个或以上个字母都是大写,则 beanName 和类名就一样了,不会进行首字母小写转换。

decapitalize 这个办法的正文也写得很分明,正文如下

/**
     * Utility method to take a string and convert it to normal Java variable
     * name capitalization.  This normally means converting the first
     * character from upper case to lower case, but in the (unusual) special
     * case when there is more than one character and both the first and
     * second characters are upper case, we leave it alone.
     * <p>
     * Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays
     * as "URL".
     *
     * @param  name The string to be decapitalized.
     * @return  The decapitalized version of the string.
     */

总结

通过扫描 bean 注解注入 IOC 时,如果不指定 bean 名称的默认规定是类名的首字母小写,如果类名前两个或以上个字母都是大写,那么 bean 名称与类名一样。

其实这个细节可能懂的都懂,本文的彩蛋次要是分享一下平时查看源码的一点心得吧,哈哈

退出移动版