BeanName
环境:Spring-framework 5.1.x
构建自己的model javaboy-test
引入依赖spring-context
创建一个类A。并交给Spring容器进行管理。
告诉Spring扫描的目录在哪。使用@ComponentScan注解。
编写测试类,因为使用的是注解进行的配置,所以使用AnnotationConfigApplicationContext进行配置解析。
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
这一行代码实现了初始化Spring容器并完成Bean的创建。
对于Bean的命名,Spring提供了BeanNameGenerator接口完成BeanName的命名。
Spring对于Bean的命名,默认使用的是AnnotationBeanNameGenerator。
AnnotationBeanNameGenerator通过通过使用注解的类型和属性完成Bean的命名。它支持以下几种注解:
@Component、@Repository、@Service、@Controller、@Named
AnnotationBeanNameGenerator通过上面注释中的value值可以直接设置Bean的名字,如果为空,则使用简单类名生成beanName。
其中重点关注:
String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
determineBeanNameFromAnnotation方法将返回生成的BeanName。
protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {
//获取bd注释的元数据
AnnotationMetadata amd = annotatedDef.getMetadata();
//获取bd注释的类型
Set<String> types = amd.getAnnotationTypes();
String beanName = null;
for (String type : types) {
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(amd, type);
if (attributes != null && isStereotypeWithNameValue(type, amd.getMetaAnnotationTypes(type), attributes)) {
Object value = attributes.get("value");
if (value instanceof String) {
String strVal = (String) value;
if (StringUtils.hasLength(strVal)) {
if (beanName != null && !strVal.equals(beanName)) {
throw new IllegalStateException("Stereotype annotations suggest inconsistent " +
"component names: '" + beanName + "' versus '" + strVal + "'");
}
beanName = strVal;
}
}
}
}
return beanName;
}
获取bd的注释类型
这里正是在上面配置类中使用的注解ComponentScans和Configuration,其中我们还使用了Configuration,但是其是作为ComponentScans的value属性而存在。
开始for循环去遍历配置类中使用的注解。从图中可以看到,第一个拿到的是@ComponentScans。
并且通过AnnotationConfigUtils.attributesFor(amd, type);拿到ComponentScans中的属性及其属性值。
从图中可以看到,ComponentScans的属性为value,而value又包含了一个长度为4的AnnotationAttributes的数组。而每一个元素包含了扫码的路径、是否是懒加载、beanName生成器等等信息。
isStereotypeWithNameValue方法是检查给定的注释是否是一个允许通过注释{@code value()}建议组件名称的构造型。
简单说就是检查当前注释是否可以通过value值类指定bean的名称。
因为上面的扫描的第一个注解为ComponentScans,它的value值是不被允许指定为bean的名称的。所以返回false。
第二次for。此时type为configuration。它的value值是可以指定bean的名称的。
但是在MyConfig中,我没有使用value指定值,所以取出来为空字符串。
因为是空字符串,所以beanName没有被复制strVal.
直接返回。此时beanName为null。
StringUtils.hasText返回false。
通过definition获取beanClassName.。此时的definition为MyConfig的bd。
获取到的beanClassName为MyConfig的全限定名。
通过全限定名再获取简单类名。
最后通过Introspector.decapitalize方法生成beanName。
从上图代码逻辑可以看出,命名规则为首字符小写,或者前面两个字符都大写。
最终返回生成的beanName。
验证:将beanName改为前两个字符都大写。beanName直接返回。