Предположим, что у нас есть класс, называемый Bean
, который зависит от другого класса, называемого Service
, используя Fields Injection, и мы не можем изменить код этих классов. Другой класс (App
) использует Provider
для создания новых экземпляров Bean, когда захочет, и мы можем изменять код в этих классах.
Поскольку в методе Provider
get()
новый экземпляр создается с использованием new
оператора, невозможно вставить Service
в Bean
, поэтому экземпляр Bean
будет неполным (поле Service будет равно null
).
Одним из способов обойти эту проблему было бы Injector
в Провайдере и вызвать метод injector.injectMembers()
в методе get()
в новом экземпляре.
Пример кода следующий (я избегал использования интерфейсов, чтобы сделать код короче).
class Bean {
@Inject private Service service;
public Bean() {
}
public void bar() {
this.service.foo();
}
}
class Service {
public void foo() {
System.out.println("foo");
}
}
class App {
@Inject private Provider<Bean> beanProvider;
public App() {
}
public void run() {
this.beanProvider.get().bar();
}
}
class BeanProvider implements Provider<Bean> {
@Inject Injector injector;
public Bean get() {
Bean mybean = new Bean();
this.injector.injectMembers(mybean);
return mybean;
}
}
// A simple module to bind the classes
class MyModule extends AbstractModule {
@Override
protected void configure() {
bind(Service.class);
bind(Bean.class).toProvider(BeanProvider.class);
bind(App.class);
}
}
public static void main(String[] args) {
Injector injector = Guice.createInjector(new MyModule());
injector.getInstance(App.class).run();
}
Другой способ состоял бы в том, чтобы изменить Bean на использование Construction Injection, но, как я сказал, допустим, что мы не можем этого сделать.
Я знаю, что большинство людей утверждают, что следует избегать инъекции Injector
, поскольку он нарушает всю философию DI, поскольку инжектор должен использоваться только в точке входа приложения и нигде больше, но поскольку Guice предлагает различные способы использования DI в приложении, например, "Инъекция поля", могут возникать ситуации, подобные описанным. Кроме того, я видел, что Field Injection используется довольно часто в нескольких проектах, поэтому такие случаи часто бывают.
Итак, существует ли "более чистый" способ избежать инъекции Injector
в описанной проблеме?
Похоже, что это не должно быть проблемой для вашего модуля. Не похоже, что вам необходимо создать провайдера вообще.
Связывания, которые вы установили в своем модуле, должны привести к тому, что Service будет введен в Bean, где вам это нужно. Таким образом, вы должны просто заменить декларацию поставщика в приложении с помощью инъекции Bean.
class App {
@Inject private Bean beanObject;
public App() {
}
public void run() {
this.beanObject.bar();
}
}
// A simple module to bind the classes
class MyModule extends AbstractModule {
@Override
protected void configure() {
//bind this to whatever you want to inject into Bean...
bind(Service.class);
bind(Bean.class);
bind(App.class);
}
}
Если это не сработает для вас, можете ли вы уточнить, что вы пытаетесь сделать и почему вам нужен провайдер?