Spring Bean别名之坑:注入map别名不会放入

2023-06-19 14:31:37 浏览数 (1)


现象


使用@Bean注解,给出bean多个名字时如

@Bean(name = {"aliasTestService", "aliasTestServiceDemo"})时,只有第一个名字才是bean的名字,其余的是别名

而当我们以map<String,类接口名>注入时别名不会存在与map的keyset之中。

现象场景


在动态切数据源情形下,用org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource实现。有时候我们提供给其他组的jar包,包含一些业务逻辑,需要注入接入方一个数据源datasource实例。

代码语言:javascript复制
org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource#setTargetDataSources
代码语言:javascript复制
public void setTargetDataSources(Map<Object, Object> targetDataSources) {
    this.targetDataSources = targetDataSources;
  }

AbstractRoutingDataSource需要注入所有数据源的实例:

代码语言:javascript复制
Map<Object, Object> targetDataSources

为了减少数据库的连接,业务方的数据源名称已定性,需要别名我们jar包中需要的数据源名称,但别名加上之后,我们jar包中的数据源路由时就会找不到。为了临时解决,只能实例化一个重复连接,bean名字是jar包需要的名字。

原因


看下spring的几个比较重要的处理点。

  • 获取bean名称,放入容器时
代码语言:javascript复制
org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod
代码语言:javascript复制
// Consider name and any aliases
    List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));
    String beanName = (!names.isEmpty() ? names.remove(0) : methodName);

    // Register aliases even when overridden
    for (String alias : names) {
      this.registry.registerAlias(beanName, alias);
    }
代码语言:javascript复制
    this.registry.registerBeanDefinition(beanName, beanDefToRegister);

@Bean name数组的第一个为beanName,其他的为aliases name,注入容器以beanName为准。

  • 以类型获取bean实例过程
代码语言:javascript复制
org.springframework.beans.factory.support.DefaultListableBeanFactory#doGetBeanNamesForType
代码语言:javascript复制
// Check all bean definitions.
    for (String beanName : this.beanDefinitionNames) {
      // Only consider bean as eligible if the bean name is not defined as alias for some other bean.
      if (!isAlias(beanName)) {
        .....
      }
         
    }
代码语言:javascript复制
org.springframework.core.SimpleAliasRegistry#isAlias
代码语言:javascript复制
@Override
  public boolean isAlias(String name) {
    return this.aliasMap.containsKey(name);
  }

当已类型注入时,!isAlias(beanName)即别名不会被处理。以map<String,Object>注入,map的keys不会存在别名。

结论


bean有别名时,以map<String,Object>注入,map的keys不会存在别名。

如何解决


若我们必须以map<String,Object>注入,别名也要存在其中,动态数据源切换才能成功,我们必须怎么做?

示例:

代码语言:javascript复制
@Configuration
public class AliasTestConfiguration {


    @Bean(name = {"aliasTestService", "aliasTestServiceDemo"})
    public AliasTestService aliasTestService1() {
        return new AliasTestServiceImpl();
    }

    @Bean
    public AliasTestService aliasTestService2() {
        return new AliasTestServiceImpl2();
    }

    @Bean
    public AutowireTest autowireTest(Map<String, AliasTestService> aliasTestServiceMap, ApplicationContext context) {
        Map<String, AliasTestService> serviceHashMap = new HashMap<>(aliasTestServiceMap);

        for (Map.Entry<String, AliasTestService> serviceEntry : aliasTestServiceMap.entrySet()) {
            String beanName = serviceEntry.getKey();
            AliasTestService service = serviceEntry.getValue();
            String[] aliases = context.getAliases(beanName);
            for (String alias : aliases) {
                serviceHashMap.put(alias, service);
            }
        }
        return new AutowireTest(serviceHashMap);
    }
}

重新把丢掉的别名找回来,放入map中

博文涉及到的Spring版本

代码语言:javascript复制
<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

0 人点赞