Отложенный привязку к кинжалу2 с использованием обработки аннотаций

В этом вопросе я расскажу о Dagger2. Dagger2 состоит в основном из компонентов и модулей. Вот пример:

Предположим, у меня есть интерфейс:

public interface MyCoolService {
  void run();
}

и возможная реализация:

public class MyCoolServiceImpl {
   @Override
   void run() {}
}

Я мог бы связать реализацию с интерфейсом с помощью создания Dagger2:

@Component(modules = {MyModule.class})
@Singleton
public interface Component {    
    MyCoolService getMyCoolService();       
}

и

@Module
public class MyModule {

    @Provides @Singleton
    MyCoolService provideMyCoolService() {
        return new MyCoolServiceImpl();
    }
}

Это было краткое введение в Dagger2. Теперь предположим, что у меня есть следующий интерфейс:

public interface MySecondCoolService {
  void doCoolStuff();
}

В коде отсутствует реализация MySecondCoolServiceImpl of MySecondCoolService. Вместо этого у меня есть аннотации @JustForCoolStuff для обозначения полей и методов. Я создал обработчик аннотации, который собирает все эти аннотации и генерирует MySecondCoolServiceImpl, который реализует MySecondCoolService.

I компилятор знает новый интерфейс MySecondCoolService до запуска обработчика аннотаций. Поэтому я мог бы изменить свой Компонент как:

@Component(modules = {MyModule.class})
@Singleton
public interface Component {    
    MyCoolService getMyCoolService();   
    MySecondCoolService getMySecondCoolService();    
}    

Проблема заключается в том, что у меня еще нет реализации в коде, и я не знаю имя реализации MySecondCoolService, которое будет создано обработчиком аннотаций. Поэтому я не могу подключить интерфейс с правильной реализацией в MyModule. Что я могу сделать - это изменить мой обработчик аннотации, чтобы он создавал для меня новый модуль. Мой обработчик аннотации может генерировать модуль (MyGeneratedModule) следующим образом:

@Module
public class MyGeneratedModule {

    @Provides @Singleton
    MySecondCoolService provide MySecondCoolService() {
        return new MySecondCoolServiceImpl();
    }
}  

Снова MyGeneratedModule генерируется обработчиком аннотации. У меня нет доступа к нему, прежде чем запускать обработчик аннотации, также я не знаю этого имени.

Вот проблема: Обработчик аннотации каким-то образом должен сказать Dagger2, что есть новый модуль, который должен учитывать Dagger2. Поскольку обработчики аннотаций не могут изменять файлы, они не могут расширять аннотацию @Component(modules = {MyModule.class}) и изменить ее на что-то вроде этого: @Component(modules = {MyModule.class, MyGeneratedModule.class})

Есть ли способ добавить MyGeneratedModule программно к графику зависимости dagger2? Как мой обработчик аннотации сообщает Dagger2, что должна быть новая проводка между интерфейсом и реализацией, как я описал выше?


Foray: Я знаю, что что-то подобное можно сделать в Google Guice и Google Gin. Проект, который делает это GWTP. Там у вас есть Ведущий:

public class StartPagePresenter extends ... {
    @NameToken("start")
    public interface MyProxy extends ProxyPlace<StartPagePresenter> {
    }
    ...
}

который имеет аннотацию @NameToken к интерфейсу ProxyPlace. В вашем AbstractPresenterModule вы подключите представление с презентатором и прокси:

public class ApplicationModule extends AbstractPresenterModule {

        bindPresenter(StartPagePresenter.class,
                StartPagePresenter.MyView.class, StartPageView.class,
                StartPagePresenter.MyProxy.class);
       ...
}

Как видно, реализация интерфейса MyProxy не указана. Реализация, созданная генератором (аналогично процессору аннотации, но для GWT). Генератор генерирует реализацию StartPagePresenter.MyProxy и добавляет его в систему guide/gin:

public class StartPagePresenterMyProxyImpl extends com.gwtplatform.mvp.client.proxy.ProxyPlaceImpl<StartPagePresenter> implements buddyis.mobile.client.app.start.StartPagePresenter.MyProxy, com.gwtplatform.mvp.client.DelayedBind {

  private com.gwtplatform.mvp.client.ClientGinjector ginjector;
    @Override
    public void delayedBind(Ginjector baseGinjector) {
      ginjector = (com.gwtplatform.mvp.client.ClientGinjector)baseGinjector;
      bind(ginjector.getPlaceManager(),
          ginjector.getEventBus());
      presenter = new CodeSplitProvider<StartPagePresenter>( ginjector.getbuddyismobileclientappstartStartPagePresenter() );
    ...
    }
  }

Ответ 1

Есть ли способ добавить MyGeneratedModule программно к графику зависимости dagger2?

Да. Используйте аргумент конструктора в вашем модуле.

@Module
public class MyModule {
  private final MyCoolService serviceImpl;

  public MyModule(MyCoolService serviceImpl) {
    this.serviceImpl = serviceImpl;
  }

  @Provides @Singleton
  MyCoolService provideMyCoolService() {
    return new MyCoolServiceImpl();
  }
}

Инициализация serviceImpl выполняется при построении графика:

DaggerComponent.builder().myModule(new MyModule(serviceImpl)).build();

Ответ 2

Ну, похоже, вам придётся прибегнуть к размышлению для этого...

@Module
public class MyGeneratedModule {

    @Provides @Singleton
    MySecondCoolService provide MySecondCoolService() {
        try {
            return (MySecondCoolService) Class.forName("package.MySecondCoolServiceImpl").newInstance(); 
        } catch (Exception ex) { ... }
    }
}