Spring Framework. Внедрить бин во время выполнения на основе параметра запроса - PullRequest
2 голосов
/ 25 апреля 2020

Скажем, у нас есть интерфейс 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);
  }
}

Как я могу ввести права FileLoader во время выполнения для каждого запроса, основанного на countryCode? В Spring я нашел что-то под названием FactoryBean , которое, очевидно, может работать, но теперь я уверен, что это правильный инструмент или это правильный способ решения этой проблемы. Кроме того, я не знаю, как инъекция будет вести себя при одновременной обработке запросов.

Ответы [ 2 ]

1 голос
/ 26 апреля 2020

Лучшее, что вы можете здесь сделать, используя полиморфизм времени выполнения, добавить еще один abstract метод в интерфейсе FileLoader для кода страны

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 в List и вызвать 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
  }
} 
1 голос
/ 25 апреля 2020

Вы можете получить bean-компонент другим способом во время выполнения, используя ApplicationContext::getBean:

@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);
}

Однако я бы порекомендовал создать сервисный слой, который агрегирует специфику страны c реализации и использует фабричный шаблон. В такой реализации нет ничего плохого.

...