此问题旨在为有用的问题提供答案。
假设我们有一个带有@Controller的Spring应用程序,一个接口以及该接口的不同实现。
我们希望@Controller根据我们收到的请求使用具有正确实现的接口。
这是@Controller:
@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() {
// ...
}
}
下面我将展示我发现根据请求动态使用其中一个实现的解决方案。
答案 0 :(得分:2)
我找到的解决方案就是这个。
首先,我们必须在@Controller中自动装配ApplicationContext。
@Autowired
private ApplicationContext appContext;
其次,我们必须在接口的实现中使用@Service注释。 在示例中,我给它们命名为“Basic”和“Other”。
@Service("Basic")
public class SampleInterfaceImpl implements SampleInterface {
public void sampleMethod() {
// ...
}
}
@Service("Other")
public class SampleInterfaceOtherImpl implements SampleInterface {
public void sampleMethod() {
// ...
}
}
接下来,我们必须在@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,因此通过正确的实现来解析接口。
答案 1 :(得分:0)
我解决了这个问题:
supports(...)
并将List<SampleInterface>
注入您的控制器。getCurrentImpl(...)
,以便在supports
Ordered
界面或使用注释@Order
,则会订购自动装配列表。这样您就不需要显式使用ApplicationContext。
答案 2 :(得分:0)
我不相信你的解决方案,因为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();
}
}
此外,依赖ApplicationContext
对象会使测试变得更加复杂。
NB。为了使它更健壮,我使用枚举而不是“基本”和“其他”字符串。
但是,如果你知道你只有两种类型的服务可供选择,那就是“保持简单愚蠢”的方式:
@Controller
public class SampleController {
@Autowired
private SampleInterfaceImpl basic;
@Autowired
private SampleInterfaceOtherImpl other;
@RequestMapping(path = "/path/Basic", method = RequestMethod.GET)
public void basic() {
basic.sampleMethod();
}
@RequestMapping(path = "/path/Other", method = RequestMethod.GET)
public void other() {
other.sampleMethod();
}
}
答案 3 :(得分:0)
老实说,我不认为在URL中公开内部实现细节只是为了避免编写一些代码行是好的。 @kriger提出的解决方案至少使用键/值方法添加一个间接步骤。
我更愿意创建一个工厂Bean(更加面向企业,甚至是抽象工厂模式),它将选择使用哪个具体实现。 通过这种方式,您可以使用任何自定义逻辑在单独的位置(工厂方法)中选择接口。 并且您将能够将服务URL与具体实现分离(这不是非常安全)。
如果您正在创建一个非常简单的服务,您的解决方案将起作用,但在企业环境中,使用模式对于确保可维护性和可伸缩性至关重要。