spring alias别名使用

今天,有个新来的同事问了我一个关于spring中bean的别名问题,他说如果一个bean的别名跟其他bean的名字一样,那根据名字获取的对象到底是哪一个?碰到这种问题,我们最好就是自己写个demo测试一下,demo程序如下:

@Configuration
public class AppleConfig {
    @Bean
    public Apple redApple() {
        return new Apple("红苹果");
    }
    @Bean(name = "blackApple")
    public Apple whiteApple() {
        return new Apple("白苹果");
    }
    @Bean
    public Apple blackApple() {
        return new Apple("黑苹果");
    }
}

按照上述demo的配置,黑苹果对象名字应该叫blackApple,虽然白苹果对象的名字叫whiteApple,但是它有一个blackApple的别名,那如果我通过容器获取名字是blackApplede对象,拿出来的是白苹果还是黑苹果呢?

答案是白苹果。

首先我们要知道,容器中可以通过applicationContext.getBean(beanName)方法获取bean,那我们首先看一下这个方法,这个方法定义在AbstractApplicationContext类中

@Override
public Object getBean(String name) throws BeansException {
	assertBeanFactoryActive();
	return getBeanFactory().getBean(name);
}

通过该方法可以看出,这个方法最终是委托给AbstractBeanFactory的getBean(beanName)方法执行

@Override
public Object getBean(String name) throws BeansException {
	return doGetBean(name, null, null, false);
}

getBean方法又会调用doGetBean方法,进入doGetBean方法就可以看到获取bean的核心逻辑

protected <T> T doGetBean(
			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {

		String beanName = transformedBeanName(name);
		Object bean;

		// Eagerly check singleton cache for manually registered singletons.
		Object sharedInstance = getSingleton(beanName);

今天我们不是研究容器获取bean的完整过程,只是看看别名对于获取对象的影响,那主要的逻辑就是在tranformBeanName方法中(我将方法的注释稍作翻译)

/**
	 * Return the bean name, stripping out the factory dereference prefix if necessary,
	 * and resolving aliases to canonical names.
	 * @param name the user-specified name
	 * @return the transformed bean name
	 * 返回bean的名字,如果名字中含有工厂对象的前缀,去除前缀,并且如果是别名,转换成真正的bean名字
	 */
	protected String transformedBeanName(String name) {
		return canonicalName(BeanFactoryUtils.transformedBeanName(name));
	}

BeanFactoryUtils.transformedBeanName(name)方法的作用,如果传入的名字带有"&"前缀,去除该前缀,在此我们不过多赘述。
进入canonicalName(beanName)方法,可知该方法是在SimpleAliasRegistry类中定义,看类的名字可知这是别名注册中心管理类,所有的别名都是由这个类管理。

/**
 * Determine the raw name, resolving aliases to canonical names.
 * @param name the user-specified name
 * @return the transformed name
 * 方法作用:解析别名转换成真正的bean名字
 */
public String canonicalName(String name) {
	String canonicalName = name;
	// Handle aliasing...
	String resolvedName;
	do {
		resolvedName = this.aliasMap.get(canonicalName);
		if (resolvedName != null) {
			canonicalName = resolvedName;
		}
	}
	while (resolvedName != null);
	return canonicalName;
}

所有的别名是放在aliasMap中,这个map以别名作为key,bean真正的名字作为value,通过这种数据结构可以推测出一个bean可以有多个别名,这与我们平时了解的是一致的。
现在分析我们的demo例子,如果要获取的bean名字是blackApple,即这边的name=blackApple,因为whiteApple的别名是blackApple,在容器启动的时候已经注册到了这个SimpleAliasRegistry中,所以aliasMap中有blackApple=whiteApple的键值对,那么经过第一轮do循环,resolvedName=whiteApple,canonicalName=whiteApple,由于此时while条件成立,会进入第二次do循环,aliasMap并没有键为whiteApple的键值对存在,所以resolvedName=null,canonicalName=whiteApple保持不变,while条件判断不成立跳出循环,返回canonicalName的值为whiteApple。

最终会根据名字whiteApple去单例对象池中取出对象并返回,具体的获取步骤这边不做赘述。

经过以上分析,可以知道最终返回的是白苹果对象,我们可以测试一下

private ApplicationContext ac;

@GetMapping("/testAppleName")
public String testAppleName() {
    return ((Apple) ac.getBean("blackApple")).getName();
}

调用该接口,页面展示白苹果,说明我们的分析正确
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值