码迷,mamicode.com
首页 > 编程语言 > 详细

Spring中Bean命名源码分析

时间:2019-10-26 01:32:16      阅读:87      评论:0      收藏:0      [点我收藏+]

标签:mes   inter   uniq   大写   stat   inf   结束   generator   print   

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命名源码分析

标签:mes   inter   uniq   大写   stat   inf   结束   generator   print   

原文地址:https://www.cnblogs.com/heliusKing/p/11741403.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!