Spring源码之:Bean的名称与别名

465 阅读7分钟

我们知道,BeanFactorygetBean(String name)方法可以获取指定名称的Bean
其中,name参数既可以是Bean的名称,也可以是Bean的别名
因此,在解析BeanFactory源码之前,有必要先了解一下Bean的名称和别名
另外,本系列文章中的源码,如果没有特殊说明,都以spring-xxx-5.3.8为准

1. Xml文件配置Bean

1.1. 概述

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                           http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 
         当同时配置了id和name属性时,id值将作为Bean的名称,而name值将作为Bean的别名
         另外,name属性可以配置多个名称(用逗号或分号或空格隔开),此时这些名称都将作为该Bean的别名
    -->
    <bean id="obj1Id" name="obj1Name" class="java.lang.Object" />

    <!-- 
         当只配置了name属性时,name值就是Bean的名称,并且没有别名
         另外,name属性可以配置多个名称(用逗号或分号或空格隔开),此时第一个名称为Bean的名称,之后的都是别名
    -->
    <bean name="obj2Name" class="java.lang.Object" />

    <!--
         当id和name属性都没有配置时,Bean名称的生成规则是:全限定类名 + "#" + 下标
         另外,全限定类名还会作为第一个无id和name的该类型的Bean的别名
    -->
    <bean class="java.lang.Object" />  <!-- Bean名称为"java.lang.Object#0",同时还有一个别名"java.lang.Object" -->
    <bean class="java.lang.Object" />  <!-- Bean名称为"java.lang.Object#1" -->

    <!-- 也可以通过alias标签给Bean名称或Bean别名再起一个别名 -->
    <alias name="obj3Id" alias="obj3IdAlias" />
    <alias name="obj3Name" alias="obj3NameAlias" />
    <alias name="obj3NameAlias" alias="obj3NameAliasAlias" />
    <bean id="obj3Id" name="obj3Name" class="java.lang.Object" />

</beans>

测试程序如下:

public class BootApiApplication {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
        System.out.println(ctx.getBean("obj1Id"));
        System.out.println(ctx.getBean("obj1Name"));
        System.out.println(ctx.getBean("obj2Name"));
        System.out.println(ctx.getBean("obj3Id"));
        System.out.println(ctx.getBean("obj3IdAlias"));
        System.out.println(ctx.getBean("obj3Name"));
        System.out.println(ctx.getBean("obj3NameAlias"));
        System.out.println(ctx.getBean("obj3NameAliasAlias"));
        System.out.println(ctx.getBean("java.lang.Object"));
        System.out.println(ctx.getBean("java.lang.Object#0"));
        System.out.println(ctx.getBean("java.lang.Object#1"));
    }
}

ClassPathXmlApplicationContext底层的BeanFactory的一级缓存中的所有的Bean名称如下(这里省略掉其它无关的Bean名称):

[
  "obj1Id",
  "obj3Id",
  "java.lang.Object#1",
  "java.lang.Object#0",
  "obj2Name"
]

BeanFactoryaliasMap(用于存放别名和原名的对应关系)内容如下:

{
  "obj3IdAlias": "obj3Id",
  "java.lang.Object": "java.lang.Object#0",
  "obj1Name": "obj1Id",
  "obj3NameAliasAlias": "obj3NameAlias",
  "obj3Name": "obj3Id",
  "obj3NameAlias": "obj3Name"
}

1.2. 源码解析

/**
 * 解析Spring配置文件时,底层是通过本类来进行解析的
 */
public class BeanDefinitionParserDelegate {

    /**
     * bean标签的name属性值可以指定多个名称,每个名称可以用逗号、分号或空格隔开
     */
    public static final String MULTI_VALUE_ATTRIBUTE_DELIMITERS = ",; ";

    /**
     * 解析bean标签(即这里的ele参数),并返回解析到的BeanDefinition的Holder
     * 
     * @param containingBean 目前只需将该参数认为是null即可
     */
    @Nullable
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
        
        // 获取到id属性值和name属性值(注意name属性值可以指定多个名称)
        String id = ele.getAttribute(ID_ATTRIBUTE);
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

        // 如果指定了name属性,则对name属性值进行切分,获取到各个名称;这些名称都默认看作是别名
        List<String> aliases = new ArrayList<>();
        if (StringUtils.hasLength(nameAttr)) {
            String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            aliases.addAll(Arrays.asList(nameArr));
        }

        // id值默认看作是Bean名称
        String beanName = id;
        
        // 如果没有指定id属性,但指定了name属性,则将name属性值中第一个名称作为Bean名称,剩下的作为别名
        if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
            beanName = aliases.remove(0);
        }

        // 先不管这段代码
        if (containingBean == null) {
            checkNameUniqueness(beanName, aliases, ele);
        }

        // 解析bean标签的其它属性,并获取到相应的BeanDefinition;具体的解析过程不用去管
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
        
        if (beanDefinition != null) {
            
            // 如果此时beanName没有数据,说明没有指定id属性和name属性
            if (!StringUtils.hasText(beanName)) {
                try {
                    
                    // 这个分支不用管
                    if (containingBean != null) {
                        beanName = BeanDefinitionReaderUtils.generateBeanName(
                                beanDefinition, this.readerContext.getRegistry(), true);
                    
                    // 直接看这个分支
                    } else {
                        
                        // 重新生成Bean名称(如"java.lang.Object#0")
                        beanName = this.readerContext.generateBeanName(beanDefinition);
                        
                        // 如果当前Bean名称以全限定类名作为前缀,且全限定类名还没有作为其它Bean的名称或别名
                        // 则将全限定类名作为当前Bean的别名添加到aliases集合中
                        String beanClassName = beanDefinition.getBeanClassName();
                        if (beanClassName != null &&
                                beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                                !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                            aliases.add(beanClassName);
                        }
                    }
                } catch (Exception ex) {
                    error(ex.getMessage(), ele);
                    return null;
                }
            }
            
            // 根据Bean名称和别名创建并返回相应的BeanDefinitionHolder
            String[] aliasesArray = StringUtils.toStringArray(aliases);
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }

        return null;
    }

    /**
     * BeanDefinitionReaderUtils类中的方法
     * this.readerContext.generateBeanName(beanDefinition)最终会执行到这里
     * 
     * @param beanName 这里是Bean所属类的全限定名称
     */
    public static String uniqueBeanName(String beanName, BeanDefinitionRegistry registry) {
        String id = beanName;
        int counter = -1;

        // Bean名称的前缀为:所属类全限定名称 + "#"
        String prefix = beanName + GENERATED_BEAN_NAME_SEPARATOR;
        
        // 后缀数字从0开始;如果Bean名称(前缀+后缀)没被注册,则返回该Bean名称;否则后缀数字加一
        while (counter == -1 || registry.containsBeanDefinition(id)) {
            counter++;
            id = prefix + counter;
        }
        return id;
    }
}

2. @Bean注解配置Bean

2.1. 概述

@Configuration
public class Config {

    /**
     * 当@Bean注解没有指定Bean名称时,方法名将作为Bean的名称
     */
    @Bean
    public Object obj1() {
        return new Object();
    }

    /**
     * 当@Bean注解指定了一个Bean名称时,该名称将作为Bean的名称
     */
    @Bean("obj2")
    public Object anyMethodName1() {
        return new Object();
    }

    /**
     * 当@Bean注解指定了多个Bean名称时,第一个名称将作为Bean的名称,剩下的名称将作为第一个名称的别名
     */
    @Bean({"obj3", "obj3Alias1", "obj3Alias2"})
    public Object anyMethodName2() {
        return new Object();
    }
}

根据该配置类创建AnnotationConfigApplicationContext组件,其底层的BeanFactory的一级缓存中的Bean名称和aliasMap如下:

[
  "obj2",
  "obj1",
  "obj3",
  "config"
]
{
  "obj3Alias2": "obj3",
  "obj3Alias1": "obj3"
}

2.2. 源码解析

class ConfigurationClassBeanDefinitionReader {
    
    /**
     * 解析@Bean方法(即这里的beanMethod参数)
     */
    private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
        ConfigurationClass configClass = beanMethod.getConfigurationClass();
        MethodMetadata metadata = beanMethod.getMetadata();
        String methodName = metadata.getMethodName();

        // 和@Conditional相关的判断,确保当前Bean确实需要注册;具体的逻辑不用管
        if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
            configClass.skippedBeanMethods.add(methodName);
            return;
        }
        if (configClass.skippedBeanMethods.contains(methodName)) {
            return;
        }

        // 获取@Bean注解中的所有属性
        AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
        Assert.state(bean != null, "No @Bean annotation attributes");

        // 获取name属性的值(该值可以包含多个名称)
        // 注意,@Bean注解的name属性和value属性互为别名,因此可以通过name属性获取到value属性的值
        // 另外还要注意,name和value两个属性不能同时配置;如果同时配置,则值必须相同,否则会抛异常
        List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));
        
        // 如果指定了name属性值,则第一个名称即为Bean名称(并将第一个名称从names集合中移除);否则Bean名称为方法名称
        String beanName = (!names.isEmpty() ? names.remove(0) : methodName);

        // names中保存的是该Bean的别名,因此注册这些别名
        for (String alias : names) {
            this.registry.registerAlias(beanName, alias);
        }
        
        // 之后的代码省略
    }
}

3. @Component注解配置Bean

3.1. 概述

/**
 * 当@Component注解未指定名称时,Bean名称默认为类名(首字母小写);如:TestBean.class -> "testBean"
 * 特别地,如果类名前两个字符都是大写,则Bean名称是类名本身;如:XMLParser.class -> "XMLParser"
 */
@Component
public class TestBean {
}

/**
 * 当@Component注解指定了名称时,则该名称即为Bean名称
 * 注意,这里的名称不支持多个,如果名称填的是"name,alias",则Bean名称就是"name,alias"
 */
@Component("testBeanName")
public class TestBean {
}

3.2. 源码解析

public class AnnotationBeanNameGenerator implements BeanNameGenerator {

    /**
     * 为Bean生成对应的名称
     */
    @Override
    public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
        if (definition instanceof AnnotatedBeanDefinition) {
            
            // 先尝试从@Componnet等注解中获取value值,如果能获取到,则直接返回该值
            // 注意,如果有多个注解,且注解的value值不相同(如@Component("a")和@Service("b")),则这里会报错
            String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
            if (StringUtils.hasText(beanName)) {
                return beanName;
            }
        }
        
        // 否则,生成一个默认的Bean名称
        return buildDefaultBeanName(definition, registry);
    }

    /**
     * 当没有指定@Component等注解的value值时,调用该方法获取Bean的名称
     */
    protected String buildDefaultBeanName(BeanDefinition definition) {
        String beanClassName = definition.getBeanClassName();
        Assert.state(beanClassName != null, "No bean class name set");
        
        // 获取类名,然后将首字母转成小写并返回
        String shortClassName = ClassUtils.getShortName(beanClassName);
        return Introspector.decapitalize(shortClassName);
    }

    /**
     * Introspector中的方法,负责将name中第一个字符转成小写形式
     */
    public static String decapitalize(String name) {
        if (name == null || name.length() == 0) {
            return name;
        }
        
        // 如果前两个字符都是大写,则直接返回原名称
        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]);
        return new String(chars);
    }
}

4. FeignClient的名称和别名

/**
 * FeignClient的全限定类名将作为Bean的名称,而注解的name/value属性值拼接上"FeignClient"将作为该Bean的别名
 * 比如,该Bean的名称是"com.xxx.ZtxyFeignClient",别名是"ztxy123FeignClient"
 */
@FeignClient(name = "ztxy123", url = "${ztxy.host}", path = "/path")
public interface ZtxyFeignClient {
}

5. FactoryBean相关

5.1. 概述

/**
 * 自定义一个FactoryBean
 */
public class LongFactoryBean implements FactoryBean<Long> {

    @Override
    public Long getObject() throws Exception {
        return 233L;
    }

    @Override
    public Class<?> getObjectType() {
        return Long.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 注册该FactoryBean;注意,在一级缓存中,"longFactoryBean"名称对应的是FactoryBean本身,而不是其生产的Long对象 -->
    <bean id="longFactoryBean" name="longFactoryBeanName1;longFactoryBeanName2" class="com.xxx.LongFactoryBean" />

</beans>
public class BootApiApplication {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");

        // 虽然"longFactoryBean"这个名称对应的是FactoryBean
        // 但工厂发现拿到的Bean是FactoryBean类型的,于是会将该FactoryBean生产的Bean返回
        System.out.println(ctx.getBean("longFactoryBean"));
        
        // 如果要想获取FactoryBean本身,可以在Bean名称前面加上"&"前缀(可以加多个,效果是一样的)
        // 工厂在获取Bean时,会先去掉所有的"&"前缀,再进行查找,从而得到FactoryBean本身
        // 然后工厂发现用户指定的Bean名称是以"&"开头的,因此会直接返回FactoryBean,而不是返回其生产的Bean
        System.out.println(ctx.getBean("&longFactoryBean"));
        System.out.println(ctx.getBean("&&longFactoryBeanName1"));
        System.out.println(ctx.getBean("&&&longFactoryBeanName2"));
    }
}

5.2. doGetBean()

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {

    /**
     * BeanFactory.getBean(name)方法最终会调用本方法,此时后面三个参数分别是null、null、false
     */
    protected <T> T doGetBean(
            String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
            throws BeansException {

        // 将用户指定的Bean名称中的"&"前缀去掉,并且将别名转成Bean名称
        String beanName = transformedBeanName(name);

        // 最终要返回的Bean实例
        Object beanInstance;

        // 调用getObjectForBeanInstance()方法处理xxxInstance,该方法的返回值是真正要返回的Bean
        // 这里的xxxInstance代表在容器内部找到的Bean实例;先不用关心xxxInstance是怎么获取到的
        beanInstance = getObjectForBeanInstance(xxxInstance, name, beanName, mbd);

        // getObjectForBeanInstance()方法的主要逻辑:
        // 1. 判断用户是否想获取FactoryBean(name是否以"&"开头)
        // 2. 如果是,则直接返回xxxInstance(此时该实例必须是FactoryBean类型)
        // 3. 否则,如果xxxInstance是FactoryBean,则返回xxxInstance生产的Bean,否则还是返回xxxInstance

        // 进行类型转换并返回;由于这里requiredType是null,因此这里会直接将beanInstance转成返回值的类型
        return adaptBeanInstance(name, beanInstance, requiredType);
    }
}

5.3. transformedBeanName()

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {

    /**
     * 将用户指定的Bean名称转成BeanFactory能识别的Bean名称
     */
    protected String transformedBeanName(String name) {
        return canonicalName(BeanFactoryUtils.transformedBeanName(name));
    }

    /**
     * BeanFactoryUtils中的方法,负责将name中的"&"前缀去掉
     */
    public static String transformedBeanName(String name) {
        Assert.notNull(name, "'name' must not be null");
        if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
            return name;
        }
        
        // 去掉name的"&"前缀,然后将结果返回(这里还会将结果缓存起来)
        return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
            do {
                beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
            } while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
            return beanName;
        });
    }

    /**
     * 继承自SimpleAliasRegistry的方法;将Bean的别名转成Bean的名称
     */
    public String canonicalName(String name) {
        
        // 先假设name就是Bean名称
        String canonicalName = name;
        
        String resolvedName;
        do {
            // 根据canonicalName在aliasMap中查找对应的值
            // 如果找得到,说明canonicalName是别名,因此将canonicalName更新为它所对应的原名称,然后继续循环判断
            // 否则,说明canonicalName就是Bean名称,此时返回该canonicalName
            resolvedName = this.aliasMap.get(canonicalName);
            if (resolvedName != null) {
                canonicalName = resolvedName;
            }
        } while (resolvedName != null);
        return canonicalName;
    }
}

5.4. getObjectForBeanInstance()

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
    
    /**
     * 处理和FactoryBean相关的逻辑
     * 
     * @param name         用户指定的Bean名称
     * @param beanName     真正的Bean名称
     * @param beanInstance beanName对应的Bean
     */
    protected Object getObjectForBeanInstance(
            Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

        // 如果name以"&"为前缀,说明用户想要获取FactoryBean
        // 此时要求beanInstance是FactoryBean类型,然后直接返回beanInstance即可
        if (BeanFactoryUtils.isFactoryDereference(name)) {
            if (beanInstance instanceof NullBean) {
                return beanInstance;
            }
            if (!(beanInstance instanceof FactoryBean)) {
                throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
            }
            if (mbd != null) {
                mbd.isFactoryBean = true;
            }
            return beanInstance;
        }

        // 否则,说明用户想要获取某个具体的Bean
        
        // 如果beanInstance不是FactoryBean类型,则直接返回该实例即可
        if (!(beanInstance instanceof FactoryBean)) {
            return beanInstance;
        }

        // 否则,说明需要将FactoryBean生产的Bean返回
        
        // 先查询factoryBeanObjectCache缓存(该缓存会保存FactoryBean生产的单例Bean)
        Object object = null;
        if (mbd != null) {
            mbd.isFactoryBean = true;
        } else {
            object = getCachedObjectForFactoryBean(beanName);
        }
        
        // 如果缓存没有找到,则调用getObjectFromFactoryBean()方法来获取目标Bean;主要逻辑为:
        // 1. 调用FactoryBean的getObject()方法获取目标Bean
        // 2. 如果发现目标Bean是单例的,则将其放入factoryBeanObjectCache缓存
        if (object == null) {
            // Return bean instance from factory.
            FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
            // Caches object obtained from FactoryBean if it is a singleton.
            if (mbd == null && containsBeanDefinition(beanName)) {
                mbd = getMergedLocalBeanDefinition(beanName);
            }
            boolean synthetic = (mbd != null && mbd.isSynthetic());
            object = getObjectFromFactoryBean(factory, beanName, !synthetic);
        }
        return object;
    }
}