Mar*_*eti 5 java polymorphism spring dependency-injection interface
作为Java开发人员,我经常需要在接口的不同实现之间进行选择.有时候这个选择可以做一次,而有些时候我需要不同的实现来响应我的程序收到的不同输入.换句话说,我需要能够在运行时更改实现.这可以通过一个帮助对象轻松实现,该对象将一些键(基于用户输入)转换为对合适的接口实现的引用.
使用Spring,我可以将这样的对象设计为bean,并将其注入到我需要的任何位置:
public class MyClass {
@Autowired
private MyHelper helper;
public void someMethod(String someKey) {
AnInterface i = helper.giveMeTheRightImplementation(someKey);
i.doYourjob();
}
}
Run Code Online (Sandbox Code Playgroud)
现在,我该如何实现帮助?让我们从这开始:
@Service
public class MyHelper {
public AnInterface giveMeTheRightImplementation(String key) {
if (key.equals("foo")) return new Foo();
else if (key.equals("bar")) return new Bar();
else ...
}
}
Run Code Online (Sandbox Code Playgroud)
这种解决方案有几个缺陷.最糟糕的情况之一是从帮助程序返回的实例对于容器是未知的,因此不能从依赖注入中受益.换句话说,即使我Foo像这样定义类:
@Service
public class Foo {
@Autowired
private VeryCoolService coolService;
...
}
Run Code Online (Sandbox Code Playgroud)
... Foo返回的实例MyHelper不会coolService正确初始化该字段.
为避免这种情况,经常建议的解决方法是在帮助程序中注入每个可能的实现:
@Service
public class MyHelper {
@Autowired
private Foo foo;
@Autowired
private Bar bar;
...
public AnInterface giveMeTheRightImplementation(String key) {
if (key.equals("foo")) return foo;
else if (key.equals("bar")) return bar;
else ...
}
}
Run Code Online (Sandbox Code Playgroud)
但我不是这种解决方案的忠实粉丝.我找到了更优雅和可维护的东西:
@Service
public class MyHelper {
@Autowired
private ApplicationContext app;
public AnInterface giveMeTheRightImplementation(String key) {
return (AnInterface) app.getBean(key);
}
}
Run Code Online (Sandbox Code Playgroud)
这是基于Spring的ApplicationContext.
类似的解决方案是使用ServiceLocatorFactoryBean类:
public interface MyHelper {
AnInterface giveMeTheRightImplementation(String key);
}
// Somewhere else, in Java config
@Bean
ServiceLocatorFactoryBean myHelper() {
ServiceLocatorFactoryBean bean = new ServiceLocatorFactoryBean();
bean.setServiceLocatorInterface(MyHelper.class);
return bean;
}
Run Code Online (Sandbox Code Playgroud)
但由于我不是春天的专家,我想知道是否有更好的方法.
我在项目中遵循这种方法。这不是万无一失的,但是在以很少的配置代码添加新的实现方面,它可以很好地发挥作用。
我用这样的东西创建一个枚举
enum Mapper{
KEY1("key1", "foo"),
KEY2("key2", "bar")
;
private String key;
private String beanName;
public static getBeanNameForKey(String key){
// traverse through enums using values() and return beanName for key
}
}
Run Code Online (Sandbox Code Playgroud)
让我们假设Foo和Bar都是通过命令界面实现的。我们称之为AnInterface
class ImplFactory{
@Autowired
Map<String, AnInterface> implMap; // This will autowire all the implementations of AnInterface with the bean name as the key
public AnInterface getImpl(string beanName){
implMap.get(beanName);
}
}
Run Code Online (Sandbox Code Playgroud)
您的帮助程序类将如下所示
@Service
public class MyHelper {
@Autowired
ImplFactory factory;
public AnInterface giveMeTheRightImplementation(String key) {
String beanName = Mapper.getBeanNameForKey(key);
factory.getImpl(beanName);
}
}
Run Code Online (Sandbox Code Playgroud)
这种方法的一些优点是:
1.在选择正确的实现方案时,它避免了冗长的案例。
2.如果要添加新的实现。您要做的就是在枚举中添加一个Mapper(除了添加新的Impl类之外)。
3.您甚至可以为所需的impl类配置Bean名称(如果您不希望spring提供默认的Bean名称)。该名称将成为工厂类中地图的键。您必须在枚举中使用它。
编辑:如果您想给您的bean自定义名称,则可以使用构造型注释之一的value属性。例如。如果您已将Impl注释为@Component或@Service,则请执行@Component("myBeanName1")或@Service("myBeanName2")
小智 3
做你想做的事情的标准方法应该是这样的:
interface YourInterface {
void doSomething();
}
public class YourClass {
@Inject @Any Instance<YourInterface> anImplementation;
public void yourMethod(String someInput) {
Annotation qualifier = turnInputIntoQualifier(someInput);
anImplementation.select(qualifier).get().doSomething();
}
private Annotation turnInputIntoQualifier(String input) {
...
}
}
Run Code Online (Sandbox Code Playgroud)
但目前 Spring 不支持它(尽管计划在 v5.x 中使用)。它应该可以在应用程序服务器上运行 。
如果您想坚持使用 Spring,那么ServiceLocatorFactoryBean基于 Spring 的解决方案可能是最好的解决方案。
| 归档时间: |
|
| 查看次数: |
2784 次 |
| 最近记录: |