Spring中的beanName
在Spring中每一个注册到容器中的Bean都有自己的名字(至少一个),可能不止一个(别名)。对于未明确指定name的Bean,Spring会自动为其生成一个名字。而对于在xml中配置的Bean和使用诸如Service、Component等注解标识的Bean,Spring为其生成名字的方式并不相同,下面我们一一分析。
核心接口
核心接口.pngBeanNameGenerator接口定义如下
public interface BeanNameGenerator {
/**
* Generate a bean name for the given bean definition.
* @param definition the bean definition to generate a name for
* @param registry the bean definition registry that the given definition
* is supposed to be registered with
* @return the generated bean name
*/
String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry);
}
BeanNameGenerator是生成beanName的顶级接口,而它有两个实现类,图中左侧的DefaultBeanNameGenerator是给XML配置中Bean使用的,图中右侧的AnnotationBeanNameGenerator则是给通过注解定义的Bean使用的。
XML配置
在此不赘述XML文件中Bean的解析过程,直接来看DefaultBeanNameGenerator,其调用链路为
DefaultBeanNameGenerator#generateBeanName—>BeanDefinitionReaderUtils#generateBeanName
最后这个方法的定义如下
public static String generateBeanName(
BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)
throws BeanDefinitionStoreException {
// 先拿类名赋值
String generatedBeanName = definition.getBeanClassName();
if (generatedBeanName == null) {
if (definition.getParentName() != null) {
generatedBeanName = definition.getParentName() + "$child";
}
else if (definition.getFactoryBeanName() != null) {
generatedBeanName = definition.getFactoryBeanName() + "$created";
}
}
if (!StringUtils.hasText(generatedBeanName)) {
throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " +
"'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
}
String id = generatedBeanName;
if (isInnerBean) {
// 内部bean,在少数情况下走该分支,例如使用key-ref等标签时
// Inner bean: generate identity hashcode suffix.
id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
}
else {
// Top-level bean: use plain class name.
// Increase counter until the id is unique.
// 为了保证id唯一,在其后加数字
int counter = -1;
while (counter == -1 || registry.containsBeanDefinition(id)) {
counter++;
id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + counter;
}
}
return id;
}
注释都写在了上面,逻辑很简单:类名+“#”+数字
注解配置
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
if (definition instanceof AnnotatedBeanDefinition) {
// 如果注解的value指定了beanName,则使用该值
String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
if (StringUtils.hasText(beanName)) {
// Explicit bean name found.
return beanName;
}
}
// Fallback: generate a unique default bean name.
// 如果没有指定value,则为其生成beanName
return buildDefaultBeanName(definition, registry);
}
继续追踪
protected String buildDefaultBeanName(BeanDefinition definition) {
String shortClassName = ClassUtils.getShortName(definition.getBeanClassName());
return Introspector.decapitalize(shortClassName);
}
Introspector.decapitalize的代码如下
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);
}
通过上面两段代码可以看出逻辑如下
- 取短类名,即不包含包路径的类名,例如com.test.Student的短类名为Student,这点跟XML配置中取全类名不一样
- 如果短类名长度大于1,且第一个和第二个字符为大写,则直接返回短类名,也就是说假设类为com.test.STudent,则beanName为STudent
- 其他情况下将短类名首字符小写后返回,假设类为com.test.Student,则beanName为student
验证
由于只为了验证beanName,简单起见,Bean类中都为空
People类
@Component
public class Pepole {
}
TNtt类
@Service
public class TNttt {
}
TestPepole类
public class TestPepole {
}
TNTt类
public class TNTt {
}
其中TestPepole和TNTt通过XML配置
<bean class="com.hust.TestPepole"></bean>
<bean class="com.hust.TNTt"></bean>
测试主类
public class App {
public static void main(String[] args) throws IOException {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:application.xml");
System.out.println(new Gson().toJson(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Pepole.class)));
System.out.println(new Gson().toJson(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext,
TNttt.class)));
System.out.println(new Gson().toJson(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext,
TestPepole.class)));
System.out.println(new Gson().toJson(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext,
TNTt.class)));
}
}
输出结果
["pepole"]
["TNttt"]
["com.hust.TestPepole#0"]
["com.hust.TNTt#0"]
总结
- 在不指定beanName的情况下,Spring会自动为注册的Bean生成一个唯一的beanName
- 通过注解注册的Bean和XML注册的Bean,Spring为其生成默认beanName的机制不一样
- 不要盲目觉得通过注解注册的Bean,Spring为其生成beanName就是将短类名的首字母小写,当短类名的首字符和第二个字符均大写时,beanName就是短类名
网友评论