如何基于Enum类型创建和自动装配bean?

时间:2016-08-18 16:16:39

标签: java spring autowired

如果我的术语混乱,我很抱歉,我对Spring很新。

我继承了一个具有相当不吸引人的方法的应用程序,如下所示:

  • 该应用处理3种类型的数据(T1, T2, T3)。类型位于枚举类DataType

  • 有一个数据服务类层次结构,带有抽象的DataService类,还有3个派生类(每个DataType一个)T1DataService,T2DataService ....

  • 配置为数据服务创建3个bean:

    @Bean(name = "T1DataService")
    public DataService getDataService() throws Exception {
        return new T1DataService();
    }
    
    @Bean(name = "T2DataService")
    public DataService getDataService() throws Exception {
        return new T2DataService();
    }
    
    @Bean(name = "T3DataService")
    public DataService getDataService() throws Exception {
        return new T3DataService();
    }
    
  • 类似地,有一个计算服务,只有1个类,但有3个bean实例,其中数据类型被传递给每个实例的构造函数:

    @Bean(name = "T1ComputeService")
    public ComputeService getComputeService() throws Exception {
        return new ComputeService (T1);
    }
    
    @Bean(name = "T2ComputeService")
    public ComputeService getComputeService() throws Exception {
        return new ComputeService (T2);
    }
    
    @Bean(name = "T3ComputeService")
    public ComputeService getComputeService() throws Exception {
        return new ComputeService (T3);
    }
    
  • 有一个请求处理器类,它需要来自DataService的数据,以及来自ComputeService的计算,基于请求中指定的数据类型。

    它的编码方式是Autowire所有3个数据和3个计算服务bean:

    @Autowired @Qualifier("T1DataService")
    protected DataService T1DataService;
    
    @Autowired @Qualifier("T2DataService")
    protected DataService T2DataService;
    
    @Autowired @Qualifier("T3DataService")
    protected DataService T3DataService;
    
    @Autowired @Qualifier("T1ComputeService")
    protected ComputeService T1ComputeService;
    
    @Autowired @Qualifier("T2ComputeService")
    protected ComputeService T2ComputeService;
    
    @Autowired @Qualifier("T3ComputeService")
    protected ComputeService T3ComputeService;
    

    然后,在处理器类的代码中:

    DataService dataService = null;
    ComputeService computeService = null;
    
    switch (request.dataType) {
    case T1: 
        dataService = T1DataService;
        computeService = T1ComputeService;
        break;
    case T2: 
        dataService = T2DataService;
        computeService = T2ComputeService;
        break;
    case T3: 
        dataService = T3DataService;
        computeService = T3ComputeService;
        break;
    

这似乎是一种非常糟糕的选择正确对象的方式。

如果这些不是自动装配的Spring bean,而是常规的POJO,我会简单地将它们隐藏在HashMap中

static HashMap<DataType, DataService> dataServices = new HashMap<>();
dataServices.put(T1, new T1DataService());
dataServices.put(T2, new T2DataService());
dataServices.put(T3, new T3DataService());

static HashMap<DataType, ComputeService> computeServices = new HashMap<>();
for (DataType dataType : DataType.allDataTypes()) {
    computeServices.put(dataType , new ComputeService(dataType));
}

...然后在处理器类中,通过密钥从HashMap获取它来访问正确的服务对象:

DataService dataService = dataServices.get(request.dataType);
ComputeService computeService = computeServices.get(request.dataType);

有没有什么方法可以使用Spring实现同样干净简洁的代码?

2 个答案:

答案 0 :(得分:0)

我会将bean名称或类放在枚举中并使用ApplicationContext#getBean(使用lombok annotations):

@RequiredArgsConstructor
enum DataType {
   T1("T1DataService", "T1ComputeService"),
   T2("T2DataService", "T2ComputeService"),
   T3("T3DataService", "T3ComputeService");

   @Getter
   private final String dataServiceName;

   @Getter
   private final String computeServiceName;   
}

然后

DataService dataService = applicationContext.getBean(DataType.T1.getDataServiceName());
ComputeService computeService = applicationContext.getBean(DataType.T1.getComputeServiceName());

(类似的情况,请注意您也可以假设bean名称已规范化并执行DataType.T1.name() + "DataService"

答案 1 :(得分:0)

这里有一些事情需要发生:

  • 您需要在某处注入各种服务。
  • 您需要根据枚举值
  • 恢复正确的服务

你可以从applicationContext中获取东西,但imo会向后退一步,远离基于注释的配置。

说到ComputeService,是否真的有必要将枚举常量作为状态引入?如果这可以推广,而是检查类'方法的哪个枚举常量参数成立,那么你可以只使用这个类的一个实例。

然后,你会有这样的事情:

public class Something {

    @Inject
    private ComputeService computeService;

    @Inject
    @Named("T1DataService")
    private DataService T1DataService;

    @Inject
    @Named("T2DataService")
    private DataService T2DataService;

    @Inject
    @Named("T3DataService")
    private DataService T3DataService;

    //...
}

此外,没有什么可以阻止您使用注入的服务填充地图,以便于查找:

private Map<DataType, DataService> serviceMap;

@PostConstruct
public void createMap() {
    serviceMap = new HashMap<>();
    serviceMap.put(T1, T1DataService);
    serviceMap.put(T2, T2DataService);
    serviceMap.put(T3, T3DataService);
}

public void doStuff(final Request request) {
    serviceMap.get(request.datatype).doStuff();
}