Java 如何在Spring中使用接口的动态实现?
这个问题是为了回答一个有用的问题 假设我们有一个带有@Controller的Spring应用程序、一个接口和该接口的不同实现 我们希望@Controller根据我们收到的请求使用具有适当实现的接口 这是@Controller:Java 如何在Spring中使用接口的动态实现?,java,spring,spring-mvc,Java,Spring,Spring Mvc,这个问题是为了回答一个有用的问题 假设我们有一个带有@Controller的Spring应用程序、一个接口和该接口的不同实现 我们希望@Controller根据我们收到的请求使用具有适当实现的接口 这是@Controller: @Controller public class SampleController { @RequestMapping(path = "/path/{service}", method = RequestMethod.GET) public void me
@Controller
public class SampleController {
@RequestMapping(path = "/path/{service}", method = RequestMethod.GET)
public void method(@PathVariable("service") String service){
// here we have to use the right implementation of the interface
}
}
以下是界面:
public interface SampleInterface {
public void sampleMethod(); // a sample method
}
以下是一个可能的实现:
public class SampleInterfaceImpl implements SampleInterface {
public void sampleMethod() {
// ...
}
}
public class SampleInterfaceOtherImpl implements SampleInterface {
public void sampleMethod() {
// ...
}
}
还有一个:
以下是一个可能的实现:
public class SampleInterfaceImpl implements SampleInterface {
public void sampleMethod() {
// ...
}
}
public class SampleInterfaceOtherImpl implements SampleInterface {
public void sampleMethod() {
// ...
}
}
下面我将展示我发现的基于请求动态使用其中一个实现的解决方案。我发现的解决方案就是这个 首先,我们必须在@Controller中自动连接ApplicationContext
@Autowired
private ApplicationContext appContext;
其次,我们必须在接口的实现中使用@Service注释。
在这个例子中,我给他们起了“Basic”和“Other”两个名字
接下来,我们必须获得@Controller中的实现。
这里有一种可能的方法:
@Controller
public class SampleController {
@Autowired
private ApplicationContext appContext;
@RequestMapping(path = "/path/{service}", method = RequestMethod.GET)
public void method(@PathVariable("service") String service){
SampleInterface sample = appContext.getBean(service, SampleInterface.class);
sample.sampleMethod();
}
}
通过这种方式,Spring在动态上下文中注入了正确的bean,因此通过正确的实现解决了接口。我解决了这个问题,如下所示:
- 让接口实现
的方法,并将支持(…)
注入控制器列表
- 在控制器中创建一个方法
,在getCurrentImpl(…)
支持的帮助下解决它
- 自Spring 4以来,如果您实现
接口或使用注释ordered
,则自动连线列表将被排序@Order
这样,您就不需要显式地使用ApplicationContext。我不相信您的解决方案,因为HTTP参数值和bean限定符之间存在隐式链接。无辜地更改bean名称将导致一场灾难,调试起来可能很棘手。我将把所有必要的信息封装在一个地方,以确保任何更改只需要在一个bean中完成:
@Controller
public class SampleController {
@Autowired
private SampleInterfaceImpl basic;
@Autowired
private SampleInterfaceOtherImpl other;
Map<String, SampleInterface> services;
@PostConstruct
void init() {
services = new HashMap()<>;
services.put("Basic", basic);
services.put("Other", other);
}
@RequestMapping(path = "/path/{service}", method = RequestMethod.GET)
public void method(@PathVariable("service") String service){
SampleInterface sample = services.get(service);
// remember to handle the case where there's no corresponding service
sample.sampleMethod();
}
}
老实说,我不认为仅仅为了避免编写几行代码而在URL中公开内部实现细节的想法是好的。 @kriger提出的解决方案至少使用键/值方法添加了一个间接步骤 我更愿意创建一个工厂Bean(更面向企业,甚至是一个抽象的工厂模式),它将选择使用哪个具体实现。 通过这种方式,您将能够使用任何自定义逻辑在单独的位置(工厂方法)选择接口。 您将能够将服务URL与具体实现分离(这不是很安全)
如果您正在创建一个非常简单的服务,那么您的解决方案将起作用,但在企业环境中,模式的使用对于确保可维护性和可伸缩性至关重要。方法的
字符串服务
参数在运行时是否具有Basic或Other的值?您是说服务的调用者在调用时应该传递Basic或Other?是的,在这种情况下,服务变量具有该值之一。虽然服务的调用者知道这些常量有点奇怪,但整个想法可以被有效地操纵+1。如果HTTP参数与应用程序的任何bean都不匹配,您可以尝试捕获BeansException以控制。