先看代码演示
项目先定义一个User类
public class User {
private String name;
@Override
public String toString() {
return "User{" + "name='" + name + '\'' + '}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
接着配置文件定义bean,注意这里的bean标签都没有设置name
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<bean class="org.example.User">
<property name="name" value="u1" />
</bean>
<bean class="org.example.User">
<property name="name" value="u2" />
</bean>
<bean class="org.example.User">
<property name="name" value="u3" />
</bean>
</beans>
在代码中获取bean
public class App {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
User u1 = ctx.getBean("org.example.User", User.class);
User u2 = ctx.getBean("org.example.User#1", User.class);
User u3 = ctx.getBean("org.example.User#2", User.class);
System.out.println("u1=" + u1);
System.out.println("u2=" + u2);
System.out.println("u3=" + u3);
}
}
输出
u1=User{name='u1'}
u2=User{name='u2'}
u3=User{name='u3'}
问题:ctx.getBean
第一个参数是bean的name,但是beans.xml中并没有设置bean标签的name或者id属性,为什么可以顺利拿到呢?
源码分析
在 Spring 中,提供了 BeanNameGenerator 用来生成 BeanName:
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 regist){}
}
- DefaultBeanNameGenerator:XML 配置中,默认的 BeanName 就是在这个中自动生成的。
- AnnotationBeanNameGenerator:Java 配置中,如果使用了 @Component 等注解标记的 Bean,没有设置默认的名称,则通过这个来生成默认的 BeanName。
public class DefaultBeanNameGenerator implements BeanNameGenerator {
public static final DefaultBeanNameGenerator INSTANCE = new DefaultBeanNameGenerator();
public DefaultBeanNameGenerator() {
}
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
return BeanDefinitionReaderUtils.generateBeanName(definition, registry);
}
}
可以看到,generateBeanName 这个方法实际上代理了 BeanDefinitionReaderUtils.generateBeanName
方法的执行,真正的 BeanName 的生成是在这个方法中完成的。
public static String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean) throws BeanDefinitionStoreException {
// 这里就是获取到 XML 中 bean 标签里边配置的 class 属性的值
String generatedBeanName = definition.getBeanClassName();
// 判断是否有 class 这个属性值,如果没有的话,则在 parnetName 存在的情况下,
// 使用 parentName+$child 来作为 生成的 beanName
if (generatedBeanName == null) {
if (definition.getParentName() != null) {
generatedBeanName = definition.getParentName() + "$child";
// 如果没有 parentName,则尝试使用 factoryBeanName
} else if (definition.getFactoryBeanName() != null) {
generatedBeanName = definition.getFactoryBeanName() + "$created";
}
}
// 如果经过上面的处理,还是没有 generatedBeanName,那么就要抛异常了
if (!StringUtils.hasText(generatedBeanName)) {
throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither 'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
} else {
// 我们的默认 BeanName,实际上是在uniqueBeanName这个方法中生成的
return isInnerBean ? generatedBeanName + "#" + ObjectUtils.getIdentityHexString(definition) : uniqueBeanName(generatedBeanName, registry);
}
}
public static String uniqueBeanName(String beanName, BeanDefinitionRegistry registry) {
String id = beanName;
int counter = -1;
// 所有这里是把类的全路径和 # 拼在一起
for(String prefix = beanName + "#"; counter == -1 || registry.containsBeanDefinition(id); id = prefix + counter) {
++counter;
}
//最终生成的 id 就是 org.example.User#0
return id;
}
由此可以看到,默认的 BeanName 就是类的全路径+#+序列号,如 org.example.User#0 、 org.example.User#1 。
一个新的问题
对于序列号为 0 的 BeanName,似乎还有一个默认的名称,就是类的全路径,不加任何序列号?
上面这个生成 BeanName 的方法是在 BeanDefinitionParserDelegate#parseBeanDefinitionElement
方法中执行的,具体的逻辑如下:
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
。。。。
if (beanDefinition != null) {
// 当前没有配置 BeanName,即 bean 标签中没有 id 或者 name 属性
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
} else {
//这个地方,最终会调用到上面的逻辑去生成 BeanName
beanName = this.readerContext.generateBeanName(beanDefinition);
// 获取一个类的全路径 org.javaboy.demo.User
String beanClassName = beanDefinition.getBeanClassName();
//!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)表示beanClassName还没有作为一个beanName注册到Spring容器中
if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (this.logger.isTraceEnabled()) {
this.logger.trace("Neither XML 'id' nor 'name' specified - using generated bean name [" + beanName + "]");
}
} catch (Exception var9) {
this.error(var9.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
} else {
return null;
}
}
这就是为什么默认生成的 BeanName 中, #0 可有可无的原因。