Preface
After using spring for many years, I have always assumed that the default beanName of spring is the lowercase of the first letter of the class name. For example, the beanName of HelloService is helloService. Until one day when the supplier’s interface was docked, he had a class like ABService, so he used
getBean(“aBService”)
The way to get the bean, the result is null, at first I thought it was that ABservice was not injected, and then used
getBean(ABService.class)
The bean can be successfully obtained, indicating that ABService is injected into the IOC container, but why can’t I get the bean with aBService? So use the following code snippet to print out the beanName corresponding to the corresponding ABService
applicationContext.getBeansOfType(ABService.class).forEach((beanName,bean)->{
System.out.println(beanName + ":" + bean);
});
The printed result is as follows
ABService:com.github.lybgeek.ABService@245b6b85
The beanName turned out to be ABService, which is different from the previous assumption. So I had to view the source code
Source View
There are two ways to view the source code, the example in this article is the springboot project
Method 1: Debug the breakpoint directly from the main method
It can be seen from the figure that if it is in the form of scanning annotation injection, the generation rule of its beanName is determined by
org.springframework.context.annotation.AnnotationBeanNameGenerator#generateBeanName
Decided.
ps: starts directly from the main startup type debugging, which is more suitable for more time, or the investigation has no clue
Method 2: Check with questions and rely on guessing and verifying
Use the find Usage of idea to find references, such as the annotation @service of ABService, we can directly check which references to @Service, and then guess the generation rule of beanName
By guessing, we can basically locate a method that meets our needs
Source code verification
From the above analysis, we can know that if the method of scanning bean annotation injection is to generate the beanName rule, it is in
org.springframework.context.annotation.AnnotationBeanNameGenerator
The generation rule code 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.
return beanName;
}
}
// Fallback: generate a unique default bean name.
return buildDefaultBeanName(definition, registry);
}
From the code snippet, we can see that the annotation has a name, such as @Service ("abService"), the beanName is abService, if there is 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);
}
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);
}
In fact, we can easily see the answer from the code, If the first two or more letters of the class name are uppercase, the beanName and the class name are the same, and the first letter will not be converted to lowercase.
The comment of the decapitalize method is also very clearly written, 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.
*/
to sum up
When injecting IOC by scanning bean annotations, if you do not specify the bean name, the default rule is to lowercase the first letter of the class name. 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, you may understand all the details. The easter eggs in this article are mainly to share some experience of viewing the source code, haha