preface

After years of working with Spring, you’ve always assumed that spring’s default beanName should be the first letter of a class name, such as HelloService, whose beanName is HelloService. Until one day it interfaced with the vendor’s interface, which had a class like ABService, so it used

GetBean (" aBService ")Copy the code

ABservice is not injected. ABservice is not injected

GetBean (ABService. Class)Copy the code

If the bean is successfully obtained, it indicates that ABService is injected into the IOC container. Why can’t ABService be used to obtain the bean? Print the beanName of the corresponding ABService using the following code snippet

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

The printed result is as follows

ABService:com.github.lybgeek.ABService@245b6b85
Copy the code

BeanName turns out to bean ABService, contrary to what was assumed. So have to look at the source

The source code to view

There are two ways to view the source code, and the example in this article is the SpringBoot project

Method one: Debug breakpoints directly from the main method

As can be seen from the figure, if it is in the form of scan annotation injection, its beanName generation rule is defined by

org.springframework.context.annotation.AnnotationBeanNameGenerator#generateBeanName
Copy the code

Decision.

Ps: This directly from the main start class debugging, more suitable for more time, or the investigation has no clue

Method two: look at the question with a guess and verify method

Using the find Usage of idea to find a reference, such as the @service annotation of ABService, we can directly see which reference to @service and guess the generation rule of beanNameBy guessing, we can basically locate an approach that better fits our needs

Source validation

From the above analysis, we can know that if the scan bean annotation injection is the way, which generates beanName rules, is in

org.springframework.context.annotation.AnnotationBeanNameGenerator
Copy the code

The code of its generation rule is as follows

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

Copy the code

From the code snippet, we can see that the annotation has a name, such as @service (” abService “), then beanName is abService, if no name, then look

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);
	}
Copy the code
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);
    }

Copy the code

If the first two or more letters of the class name are uppercase, then beanName and the class name are the same and will not be converted to lowercase.

The decapitalize method is also clearly commented as follows

/** * 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.
     */
Copy the code

conclusion

When injected into IOC by scanning bean annotations, the default rule for not specifying a bean name is lowercase for the first letter of the class name, and if the first two or more letters of the class name are uppercase, then the bean name is the same as the class name.

In fact, this detail may understand all understand, this article is mainly to share the usual view of the source code of a little experience, ha ha