spring transformedBeanName

doGetBean 方法中有有这样一个方法 transformedBeanName,这个方法做了两件事情:

  • 处理 FactoryBean 类型的 bean 名称
  • 处理别名
处理 FactoryBean 类型的 bean 名称

我们来看下代码:

protected String transformedBeanName(String name) {
		return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}

其中 BeanFactoryUtils.transformedBeanName(name) 就是来处理 FactoryBean 类型的 bean 的。
这里需要解释一下,为什么要对 FactoryBean 做特殊的处理?
FactoryBean 类型的 bean 在获取的时候,会有两种需求:

  • 获取真正的 bean,也就是 FactoryBean 生产的 bean
  • 获取 FactoryBean 本身

FactoryBean 其实还是个 bean 工厂,跟 BeanFactory 的区别在于它可以通过代码去定制化的生成一些 bean,但是 BeanFactory 只能通过 xml 配置的方式去生产 bean。当 bean 的定义信息很复杂的时候,通过 xml 来配置显然是不合适的,这时候就会用到 FactoryBean。
获取 FactoryBean 生成的 bean 可以直接用 bean 的名称获取就行,但是如果想获取 FactoryBean 本身的话,就需要在名称前面加上 &,这个是 spring 约定写死的。这跟普通的 bean 的获取是有差异的,所以 spring 需要在这里处理下这个传入的名称。
我们来看下代码:

String FACTORY_BEAN_PREFIX = "&";

public static String transformedBeanName(String name) {
	Assert.notNull(name, "'name' must not be null");
	if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
		return name;
	}
	return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
		do {
			beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
		}
		while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
		return beanName;
	});
}

如果这个 name 不是以 & 开头的,直接就返回了,不做任何处理。如果是的话,就需要将 & 截取掉返回。这里之所以会使用 do while 循环,是因为其支持输入多个 &。这里的 transformedBeanNameCache 就是用来缓存的,这种去除 & 的操作对于一个 name 来说只会处理一次。spring 中有很多这种类似的缓存。

处理别名

现在我们来看下别名是怎么处理的。
首先我们来看下 spring 中别名是怎么定义的。

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
       default-autowire="byName">
    <bean id="message" name="ms,mi" class="com.test.springtest.MessageServiceImpl">
    </bean>
    <alias name="ms" alias="mm"></alias>
</beans>

别名的定义有两种方式:

  • 在 bean 定义中增加 name 属性,同时支持定义多个别名,用 , 分隔
  • 通过 <alias></alias> 标签来定义

<alias></alias> 标签里面 name 既可以是 bean 的 id,也可以是 bean 的 name。也就是说我通过别名获取到的可能还是别名。这样我就需要通过获取到的别名再去获取,直到我拿到 bean 的 id,否则我在获取 bean 的定义信息的时候就会报错。
如果我们把 xml 修改成:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
       default-autowire="byName">
    <bean id="message" class="com.test.springtest.MessageServiceImpl">
    </bean>
    <alias name="ms" alias="mm"></alias>
</beans>

如果我通过 mm 这个去获取 bean 的话(xxx.getBean(“mm”))就会报如下的错:

/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/bin/java -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:49941,suspend=y,server=n -javaagent:/Users/qianrongli/Library/Caches/IntelliJIdea2018.3/captureAgent/debugger-agent.jar -Dfile.encoding=UTF-8 -classpath "/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/lib/ant-javafx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/lib/javafx-mx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/lib/packager.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/lib/tools.jar:/Users/qianrongli/workspace/spring-test/target/classes:/Users/qianrongli/.m2/repository/org/springframework/spring-context/5.1.6.RELEASE/spring-context-5.1.6.RELEASE.jar:/Users/qianrongli/.m2/repository/org/springframework/spring-aop/5.1.6.RELEASE/spring-aop-5.1.6.RELEASE.jar:/Users/qianrongli/.m2/repository/org/springframework/spring-beans/5.1.6.RELEASE/spring-beans-5.1.6.RELEASE.jar:/Users/qianrongli/.m2/repository/org/springframework/spring-core/5.1.6.RELEASE/spring-core-5.1.6.RELEASE.jar:/Users/qianrongli/.m2/repository/org/springframework/spring-jcl/5.1.6.RELEASE/spring-jcl-5.1.6.RELEASE.jar:/Users/qianrongli/.m2/repository/org/springframework/spring-expression/5.1.6.RELEASE/spring-expression-5.1.6.RELEASE.jar:/Users/qianrongli/.m2/repository/org/springframework/spring-test/5.1.6.RELEASE/spring-test-5.1.6.RELEASE.jar:/Users/qianrongli/.m2/repository/junit/junit/4.12/junit-4.12.jar:/Users/qianrongli/.m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar" com.test.springtest.App
Connected to the target VM, address: '127.0.0.1:49941', transport: 'socket'
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'ms' available
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:775)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1221)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:294)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
	at com.test.springtest.App.main(App.java:25)
Disconnected from the target VM, address: '127.0.0.1:49941', transport: 'socket'

这个报错是因为通过 mm 可以匹配到 ms,但是通过 ms 匹配不到其它的名称呢,那么 spring 就会认为 ms 就是 bean 的 id,就会通过 ms 去获取 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 存入的就是 alias 跟 name 的映射,这个 name 可以是某个 bean 的 alias。
这个 map 的 key 是别名,value 是名称。我们传入的 name,程序默认都是将其当做别名来处理的。通过 aliasMap 获取这个别名对应的 bean 的名称,如果没有获取到,spring 就会认为传入的 name 就是 bean 的 id,然后就会用这个 name 去获取 bean 的信息,获取不到就报错。如果通过 aliasMap 获取到的 resolvedName 不为空,那么就会在通过这个值去获取,这就是上面提到的 <alias></alias> 标签的定义所产生的效果。这样大家应该就能理解这边为什么要通过 do while 去获取了吧。

以上就是 transformedBeanName 方法的所有的信息,希望对你有所帮助,谢谢。

QQ:
在这里插入图片描述

QQ 群:
在这里插入图片描述
微信:TY_3268407924
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值