【问题标题】:Spring Framework. Inject bean at runtime based on request parameter春天框架。根据请求参数在运行时注入bean
【发布时间】:2020-04-25 20:32:30
【问题描述】:

假设我们有一个 FileLoader 接口:

public interface FileLoader {

  default String loadFile(String fileId) {
    // Default business logic
    return "Default implementation for FileLoader. Loading file" + fileId;
  }
}

以及针对不同国家/地区的不同实施:

public class USAFileLoader implements FileLoader {

  @Override
  public String loadFile(String fileId) {
    // ... Specific business logic for USA
    return "USA implementation for FileLoader. Loading file" + fileId;
  }
}
public class FRAFileLoader implements  FileLoader {

  @Override
  public String loadFile(String fileId) {
    // ... Specific business logic for France
    return "France implementation for FileLoader. Loading file" + fileId;
  }
}

我们创建一个端点来加载文件:

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class FileUploadController {

  FileLoader fileLoader;

  @PostMapping("/load/{fileId}/{countryCode}")
  public String loadFile(@PathVariable String fileId, @PathVariable String countryCode) {

    fileLoader = ... // Inject the right loader based on countryCode

    return fileLoader.loadFile(fileId);
  }
}

如何根据countryCode 在运行时为每个请求注入正确的FileLoader?我在 Spring 中发现了一个名为 FactoryBean 的东西,它显然可以工作,但我现在确定它是否是正确的工具,或者这是否是解决此问题的正确方法。另外,我不知道在同时处理请求时注入会如何表现。

【问题讨论】:

标签: java spring spring-boot dependency-injection


【解决方案1】:

您可以在这里使用运行时多态性做的最好的事情,在接口FileLoader 中为国家代码添加一个abstract 方法

public interface FileLoader {

 default String loadFile(String fileId) {
   // Default business logic
  return "Default implementation for FileLoader. Loading file" + fileId;
 }

 public abstract String getCountryCode();
}

然后在每个实现类中实现它并返回相应的国家代码

public class USAFileLoader implements FileLoader {

  @Override
  public String loadFile(String fileId) {
    // ... Specific business logic for USA
    return "USA implementation for FileLoader. Loading file" + fileId;
   }

  public String getCountryCode(){
   return "USA";
  }
}

然后您可以将Autowire 类型为FileLoader 的所有bean 转换为List 并在适当的bean 上调用loadFile

@RestController
public class FileUploadController {

 @Autowire
 List<FileLoader> fileLoaders;

  @PostMapping("/load/{fileId}/{countryCode}")
  public String loadFile(@PathVariable String fileId, @PathVariable String countryCode) {

return fileLoaders.stream()
                  .filter(f->f.getCountryCode().equlas(countryCode))
                  .findFirst()
                  .map(loader->loader.loadFile(fileId))
                  .orElse(()-> FileLoader.super.loadFile(fileId));  //calling interface default method
  }
} 

【讨论】:

  • 这会起作用,但我更愿意添加另一层而不是在控制器中添加额外的逻辑。类似FileLoaderService#loadFile(fileId, countryCode)
  • 因此,如果应用程序需要服务于 160 个国家/地区,则所有加载程序都将在运行时注入,并且在流查找后仅使用其中一个。作为另一个想知道解决方案的开发人员,这对于我的 pov 来说没有多大意义。我希望以某种方式使用抽象工厂,您可以传递国家代码并检索相应的加载器,同时牢记弹簧的注入容器。在这个方向上,我会将工厂声明为该控制器的第一类依赖项。
【解决方案2】:

您可以在运行时使用ApplicationContext::getBean 以另一种方式接收bean:

@Autowired
ApplicationContext

@PostMapping("/load/{fileId}/{countryCode}")
public String loadFile(@PathVariable String fileId, @PathVariable String countryCode) {

    FileLoader fileloader = (FileLoader) applicationContext.getBean(countryCode);
    return fileLoader.loadFile(fileId);
}

但是,我建议创建一个服务层来聚合特定国家/地区的实现并使用工厂模式。这样的实现没有什么不好的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-06-04
    • 2018-06-30
    • 1970-01-01
    • 1970-01-01
    • 2017-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多