在我们日常工作中,Bean的别名使用较少,因为大部分场景下,我们的Bean都是自己定义自己使用,比如:
@Component
public class UserServiceImpl1 implements UserService {
public void test() {
System.out.println("userServiceImpl1:test");
}
}
@Component
public class OrderService {
@Autowired()
@Qualifier("userServiceImpl1")
private UserService userService;
public void test() {
userService.test();
}
}
在上述场景下,定义别名确实多此一举。但是如果在大型项目中,我们无法修改某些Bean的ID,在开发时又无法预先知道我们最终所注入的Bean的ID时,别名功能就很有用处了。下面是Spring文档中的例子:
假设在项目中有两个子模块A和B,两者分开开发。开发时子模块A和B定义它们的数据源DataSource的名字分别叫subsystemA-dataSource和subsystemB-dataSource。在项目组合运行时引入了C模块中的数据源myApp-dataSource,在不改动A、B模块代码的情况下,要让A、B模块都引用myApp-dataSource作为数据源,就需要为myApp-dataSource增加别名。使用XML的方式配置如下:
<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>
当然也有使用JavaConfig的配置方式:
@Configuration
public class AppConfig {
@Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
public DataSource dataSource() {
// instantiate, configure and return DataSource bean...
}
}
PS:在前文中同时提到了Bean的ID和Bean的名字的概念,我们明确一下:
- Spring的所有单例Bean都有一个唯一的标识符,叫做Bean的ID;同时这个Bean又可以关联多个别名(alias),或者说名字(name)。
- Spring所维护的单例池在概念上是这样一个集合:Map<ID,单例对象>
- 别名池(aliasMap)的结构则是这样的:Map<alias, ID>
我们知道Spring从IOC容器中获取对象的方法叫做getBean(String name)
,Spring在获取对象时,由于不知道传入的参数到底是Bean的ID还是name,就会统一将其视为别名。Spring会先根据传入的name参数从aliasMap中获取Bean的ID,再从单例池中获取单例对象。下面是Spring获取Bean ID的源码:
类名:SimpleAliasRegistry
/**
* Determine the raw name, resolving aliases to canonical names.
* @param name the user-specified name
* @return the transformed name
*/
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;
}
注意源码里有个细节,就是获取ID的逻辑是在一个do-while循环中,这是因为有可能Bean定义的别名关联的是这个Bean的另一个别名,因此要一直循环,直到拿到真正的ID为止。所以下面的配置别名的方式Spring也是支持的,等于又为myApp-dataSource增加了一个别名subsystemC-dataSource:
<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>
<alias name="subsystemB-dataSource" alias="subsystemC-dataSource"/>