Spring中Bean命名源码分析

Posted HeliusKing

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring中Bean命名源码分析相关的知识,希望对你有一定的参考价值。

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

以上是关于Spring中Bean命名源码分析的主要内容,如果未能解决你的问题,请参考以下文章

Spring源码分析(十三)缓存中获取单例bean

Spring源码分析Bean加载流程概览

Spring源码分析Bean加载流程概览

Spring源码分析@Autowired依赖注入源码分析

Spring源码分析bean的加载

Spring源码分析bean标签的解析及注册