Spring中Bean命名源码分析
一、案例代码
首先是demo的整体结构
其次是各个部分的代码,代码本身比较简单,不是我们关注的重点
配置类
/**
* @Author Helius
* @Create 2019-10-25-20:16
*/
@Configuration
@ComponentScan(basePackages = {"service"})
public class SpringConfiguration {
}
接口的实现类
public interface UserService {
public void sayHello();
}
@Service(value = "userService")
public class UserServiceImpl implements UserService {
@Override
public void sayHello() {
System.out.println("hello");
}
}
测试类
public class SpringBootConfigurationTest {
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
UserService userService = ac.getBean(UserService.class);
userService.sayHello();
}
}
我们主要探究两个bean:一个是我们的SpringConfiguration类,它是配置类,也是容器中的bean
一个是UserServiceImpl类,这个不同在于我们通过@Service(value = "userService")
手动指定了bean的名字。我们探究两种情况下,spring容器中bean的名字如何生成的。
二、BeanNameGenerator
我们主要着眼于spring中bean的命名如何生成的,这个接口BeanNameGenerator
是用来给容器中的bean进行命名的。类结构如下
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);
}
我们采用的是注解方式,用到的实现类是AnnotationBeanNameGenerator
三、 源码调试
首先debug启动测试类
AnnotationBeanNameGenerator#generateBeanName
AnnotationBeanNameGenerator#determineBeanNameFromAnnotation
AnnotationBeanNameGenerator#determineBeanNameFromAnnotation
AnnotationBeanNameGenerator#buildDefaultBeanName(BeanDefinition)
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
// 判断这个bean是不是注解所标注的bean,显然我们这里是true
if (definition instanceof AnnotatedBeanDefinition) {
//从注解获取beanName,具体见下个方法,
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);
}
protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {
//获取bean定义
AnnotationMetadata amd = annotatedDef.getMetadata();
//获取到类上注解的名字,存于set集合中,
// types: 有两个值
// 1. org.springframework.context.annotation.Configuration
// 2.org.springframework.context.annotation.ComponentScan
Set<String> types = amd.getAnnotationTypes();
String beanName = null;
//遍历上面这两个注解
for (String type : types) {
// 获取注解的属性
// 第一次是我们的@Configuration注解,我们在SpringConfiguration中没有加属性,其
// 只有 默认的属性值value,且为“”.
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(amd, type);
// isStereotypeWithNameValue检查该注解是否有指定bean名称的资格
if (attributes != null && isStereotypeWithNameValue(type, amd.getMetaAnnotationTypes(type), attributes)) {
// 1.value为空
//2.这里其实就是与UserServiceImpl类差异的地方,它只有一个注解@Service,且只有一个属
//性value(我们默认没写),其值为userService。直接就返回了,
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;
}
}
}
}
// 1. beanNamef为null,接下来将进入generateBeanName的最后一句:buildDefaultBeanName()方法
return beanName;
}
// 生命默认的beanName,
//对于我们的springBootConfiuration类,其上的两个注解都没有指定bean名称
protected String buildDefaultBeanName(BeanDefinition definition) {
// 获取这个bean的类名:config.SpringConfiguration
String beanClassName = definition.getBeanClassName();
// 段言
Assert.state(beanClassName != null, "No bean class name set");
// 获取类的简短类名SpringConfiguration
String shortClassName = ClassUtils.getShortName(beanClassName);
// springConfiguration将作为默认的Bean的名称返回,
// 这里其实就是bean默认名称的生成规则,见下文
return Introspector.decapitalize(shortClassName);
}
这个类其实是JDK自带的一个类,
public static String decapitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
// 长度大于1且前两个字母是大写,直接返回,意思就是比如HEllo直接就返回hello
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]);
// 所以SpringBootConfiguration转为了springBootConfiguration返回做为bean名称
return new String(chars);
}
到此SpringbootConfuration的bean命名就结束了,
至于UserServiceimpl实现类,
四、自定义BeanName生成规则
参考上面的AnnotationBeanNameGenerator