【问题标题】:Delayed Binding to Dagger2 Graph using Annotation Processing使用注释处理延迟绑定到 Dagger2 图形
【发布时间】:2015-04-23 23:36:59
【问题描述】:

在这个问题中,我谈到了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();
}

代码中没有实现MySecondCoolServiceImplMySecondCoolService。相反,我有一个注释@JustForCoolStuff 来标记字段和方法。我创建了一个注释处理器,它收集所有这些注释并生成实现MySecondCoolServiceMySecondCoolServiceImpl

编译器在注解处理器运行之前就知道新接口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 在接口和实现之间应该有一个新的连接,就像我上面描述的那样?


进军: 我知道类似的事情可以在Google GuiceGoogle Gin 中完成。这样做的项目是GWTP。你有一个演示者:

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

它对ProxyPlace 接口有一个@NameToken 注释。在您的AbstractPresenterModule 中,您将视图与演示者和代理连接起来:

public class ApplicationModule extends AbstractPresenterModule {

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

可以看到没有给出MyProxy 接口的实现。由生成器创建的实现(类似于注释处理器,但用于 GWT)。那里Generator生成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() );
    ...
    }
  }

【问题讨论】:

  • 你为什么不能在你的代码中写@Component(modules = {MyModule.class, MyGeneratedModule.class})
  • @TavianBarnes 正如我在文中所描述的,我不知道将被创建的MyGeneratedModule 的名称。
  • 如果您有一个注释处理器生成一个您事先不知道其名称的类,并且没有其他方法可以实例化它,那么您就有问题了。先解决这个问题,其余的将随之而来。是否使用 Dagger 实际上无关紧要。
  • 注解处理器并不神奇,它们会生成您可以自己手动编写的代码。因此,首先手动编写您想要的内容,然后通过注释处理器将其自动化。如果您在 GWT 项目中,您仍然可以利用 GWT.create(),但如果您的代码是由注释处理器生成的,那不会有太大改变。注释处理器生成代码;当 GWT 生成器同时生成代码 并且 告诉 GWT 使用它时。您必须重新考虑您的方法,而不是找到解决方法。
  • @stephan1001,如果您不知道,guice 通过反射而不是代码生成来工作。它可以在运行时做很多匕首不能做的事情。问题是使用反射会影响性能。所以 dagger 在创建对象图时速度更快,但它在生成对象图方面的选择较少。

标签: java dependency-injection guice dagger dagger-2


【解决方案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) { ... }
        }
    }  
    

    【讨论】:

      猜你喜欢
      • 2018-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-09
      • 1970-01-01
      • 2018-10-12
      • 1970-01-01
      相关资源
      最近更新 更多