在不编写太多模块配置代码的情况下注入生成的类

时间:2011-04-19 03:45:42

标签: dependency-injection code-generation guice

以下是这种情况:我有一个带有构造函数的抽象类,它接受一个布尔值(它控制一些缓存行为):

abstract class BaseFoo { protected BaseFoo(boolean cache) {...} }

这些实现都是生成的源代码(很多都是)。我想自动为所有这些创建绑定,即不对每个被绑定的类型进行显式手动编码。我希望注入站点能够指定缓存或非缓存(true / false ctor param)。例如,我可能会进行两次注射:

DependsOnSomeFoos(@Inject @NonCaching AFoo aFoo, @Inject @Caching BFoo bFoo) {...}

(可以说这是一件坏事,因为决定是否缓存可能更好地在一个模块中。但考虑到我正在使用它,它似乎很有用。)

接下来的问题是 :配置绑定以统一方式生成 set 生成类型的最佳方法是什么,支持一个绑定注释以及具体类的构造函数参数?

以前我只在实现类上有一个默认构造函数,只需在每个生成的接口上放置一个@ImplementedBy。 E.g:

// This is all generated source...
@ImplementedBy(AFooImpl.class)
interface AFoo { ... }

class AFooImpl extends BaseFoo implements AFoo {  AFooImpl() { super(true); } }

但是,现在我想允许各个注入点来判断是否将true或false传递给BaseFoo,而不是总是默认为true。我尝试设置一个注入监听器(偷偷摸摸)改变构造后的真/假值,但是我看不出如何“监听”用某个注释注入的类型的范围

我一直回到的问题是绑定需要针对特定​​类型,但我不想集中枚举所有类型。

我也考虑过:

  1. 编写某种扫描程序以发现所有生成的类,并为每个类添加一对绑定,可能使用Google Reflections。
  2. 创建额外的,简单的“非缓存”类型(例如AFoo.NoCache扩展AFoo),这将允许我回到@ImplementedBy。
  3. 在生成时将每种特定类型硬连接为缓存/非缓存。
  4. 我对这些想法都没有感觉很好。还有更好的方法吗?


    更新:感谢您的评论和回答。我认为在每个类型旁边生成一个小模块并写出要在运行时通过getResources提取的模块列表是赢家。

    那就是说,在与一位同事交谈之后,我们可能会在提出问题时回避问题,而是在每个生成的类中使用类似boolean shouldCache(Class<? extends BaseFoo> c)的方法注入一个策略对象。该策略可以在应用程序配置之上实现,并提供粗粒度和细粒度控制。这放弃了通过注射部位改变行为的要求。从好的方面来说,我们不需要额外的模块。

1 个答案:

答案 0 :(得分:1)

除了你提到的内容之外,还有两种方法可供选择:

  1. 注入工厂类而不是真正的类;也就是说,你的手工编码的东西最终会说:

    @Inject
    DependsOnSomeFoos(AFoo.Factory aFooFactory, BFoo.Factory bFooFactory) {
      AFoo aFoo = aFooFactory.caching();
      BFoo bFoo = bFooFactory.nonCaching();
      ...
    }
    

    并且您生成的代码会说:

    // In AFoo.java
    interface AFoo {
      @ImplementedBy(AFooImpl.Factory.class)
      interface Factory extends FooFactory<AFoo> {}
      // ...
    }
    
    // In AFooImpl.java
    class AFooImpl extends BaseFoo implements AFoo {
      AFooImpl(boolean caching, StuffNeededByAFIConstructor otherStuff) {
        super(caching);
        // use otherStuff
      }
      // ...
      class Factory implements AFoo.Factory {
        @Inject Provider<StuffNeededByAFIConstructor> provider;
        public AFoo caching() {
          return new AFooImpl(true, provider.get());
        }
        // ...
      }
    }
    

    当然这取决于界面FooFactory:

    interface FooFactory<T> {
      T caching();
      T nonCaching();
    }
    
  2. 修改生成代码的过程,以生成随后在应用程序设置中使用的Guice模块。我不知道你的代码生成当前是如何构建的,但如果你有一些方法可以在代码生成时知道完整的类集,你可以直接执行此操作,也可以附加到某个文件,然后可以加载{{1作为Guice模块的一部分,它可以自动发现要绑定的类。