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