Например
азработчику Java, мне часто приходится выбирать между различными реализациями моих интерфейсов. Иногда этот выбор можно сделатьодин разв то время как в некоторых других случаях мне нужны разные реализации в ответ на различные входные данные, которые получает моя программа. Другими словами, мне нужно уметьменять реализация во время выполнения. Это легко достижимо с помощью вспомогательного объекта, который преобразует некоторый ключ (на основе пользовательского ввода) в ссылку на подходящую реализацию интерфейса.
С помощью Spring я могу спроектировать такой объект как боб и внедрить его везде, где мне нужно:
public class MyClass {
@Autowired
private MyHelper helper;
public void someMethod(String someKey) {
AnInterface i = helper.giveMeTheRightImplementation(someKey);
i.doYourjob();
}
}
Теперь, как я должен реализовать помощник? Давайте начнем с этого:
@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 ...
}
}
Такое решение имеет несколько недостатков. Одним из худших является тот факт, что экземпляры, возвращаемые помощником, неизвестны контейнеру и поэтому не могут извлечь выгоду из внедрения зависимости. Другими словами, даже если я определюFoo
класс как это:
@Service
public class Foo {
@Autowired
private VeryCoolService coolService;
...
}
... случаи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 ...
}
}
Но я не большой поклонник таких решений. Я нахожу более элегантным и ремонтопригодным что-то вроде этого:
@Service
public class MyHelper {
@Autowired
private ApplicationContext app;
public AnInterface giveMeTheRightImplementation(String key) {
return (AnInterface) app.getBean(key);
}
}
Это основано на весне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;
}
Но так как я не эксперт по Spring, мне интересно, есть ли еще лучшие подходы