ozn*_*mal 0 java spring dependency-injection spring-mvc spring-boot
我正在开发一个 REST API,其中有一个接口定义了由 4 个不同类实现的方法列表,并且将来有可能添加更多类。
当我收到来自客户端的 HTTP 请求时,URL 中包含一些信息,这些信息将确定需要使用哪个实现。
在我的控制器中,我希望端点方法包含一个 switch 语句,用于检查 URL 路径变量,然后使用适当的实现。
我知道我可以定义具体的实现并将其注入到控制器中,然后在 switch 语句中的每种特定情况下插入我想要使用的实现,但这看起来不太优雅或可扩展,原因有两个:
我现在必须实例化所有服务,即使我只需要使用一项服务。
该代码似乎可以更简洁,因为我实际上是使用相同的参数调用接口中定义的相同方法,虽然在示例中这并不是真正的问题,但在实现列表增长的情况下。 .. 案例和冗余代码的数量也是如此。
有没有更好的方案来解决此类情况呢?我正在使用 SpringBoot 2 和 JDK 10,理想情况下,我想实现最现代的解决方案。
我目前的方法
@RequestMapping(Requests.MY_BASE_API_URL)
public class MyController {
//== FIELDS ==
private final ConcreteServiceImpl1 concreteService1;
private final ConcreteServiceImpl2 concreteService2;
private final ConcreteServiceImpl3 concreteService3;
//== CONSTRUCTORS ==
@Autowired
public MyController(ConcreteServiceImpl1 concreteService1, ConcreteServiceImpl2 concreteService2,
ConcreteServiceImpl3 concreteService3){
this.concreteService1 = concreteService1;
this.concreteService2 = concreteService2;
this.concreteService3 = concreteService3;
}
//== REQUEST MAPPINGS ==
@GetMapping(Requests.SPECIFIC_REQUEST)
public ResponseEntity<?> handleSpecificRequest(@PathVariable String source,
@RequestParam String start,
@RequestParam String end){
source = source.toLowerCase();
if(MyConstants.SOURCES.contains(source)){
switch(source){
case("value1"):
concreteService1.doSomething(start, end);
break;
case("value2"):
concreteService2.doSomething(start, end);
break;
case("value3"):
concreteService3.doSomething(start, end);
break;
}
}else{
//An invalid source path variable was recieved
}
//Return something after additional processing
return null;
}
}
Run Code Online (Sandbox Code Playgroud)
T在 Spring 中,您可以通过注入一个List<T>或一个字段来获取接口的所有实现(例如) Map<String, T>。在第二种情况下,bean 的名称将成为映射的键。如果有很多可能的实现或者它们经常更改,您可以考虑这一点。多亏了它,您可以添加或删除实现,而无需更改控制器。
在这种情况下,注入 aList或 aMap都有一些优点和缺点。如果您注入 a,List您可能需要添加一些方法来映射名称和实现。就像是 :
interface MyInterface() {
(...)
String name()
}
Run Code Online (Sandbox Code Playgroud)
通过这种方式,您可以将其转换为Map<String, MyInterface>,例如使用 Streams API。虽然这会更明确,但它会稍微污染你的接口(为什么它应该知道有多个实现?)。
使用时,Map您可能应该显式命名 bean,甚至引入注释以遵循最小惊讶原则。如果您使用配置类的类名或方法名来命名 bean,则可以通过重命名这些 bean(实际上是更改 url)来破坏应用程序,这通常是安全的操作。
Spring Boot 中的简单实现可能如下所示:
@SpringBootApplication
public class DynamicDependencyInjectionForMultipleImplementationsApplication {
public static void main(String[] args) {
SpringApplication.run(DynamicDependencyInjectionForMultipleImplementationsApplication.class, args);
}
interface MyInterface {
Object getStuff();
}
class Implementation1 implements MyInterface {
@Override public Object getStuff() {
return "foo";
}
}
class Implementation2 implements MyInterface {
@Override public Object getStuff() {
return "bar";
}
}
@Configuration
class Config {
@Bean("getFoo")
Implementation1 implementation1() {
return new Implementation1();
}
@Bean("getBar")
Implementation2 implementation2() {
return new Implementation2();
}
}
@RestController
class Controller {
private final Map<String, MyInterface> implementations;
Controller(Map<String, MyInterface> implementations) {
this.implementations = implementations;
}
@GetMapping("/run/{beanName}")
Object runSelectedImplementation(@PathVariable String beanName) {
return Optional.ofNullable(implementations.get(beanName))
.orElseThrow(UnknownImplementation::new)
.getStuff();
}
@ResponseStatus(BAD_REQUEST)
class UnknownImplementation extends RuntimeException {
}
}
}
Run Code Online (Sandbox Code Playgroud)
它通过了以下测试:
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class DynamicDependencyInjectionForMultipleImplementationsApplicationTests {
@Autowired
private MockMvc mockMvc;
@Test
public void shouldCallImplementation1() throws Exception {
mockMvc.perform(get("/run/getFoo"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("foo")));
}
@Test
public void shouldCallImplementation2() throws Exception {
mockMvc.perform(get("/run/getBar"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("bar")));
}
@Test
public void shouldRejectUnknownImplementations() throws Exception {
mockMvc.perform(get("/run/getSomethingElse"))
.andExpect(status().isBadRequest());
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
6713 次 |
| 最近记录: |