【问题标题】:How to supply parameters in Guice injections, while not breaking Guice-AOP method interception?如何在 Guice 注入中提供参数,同时不破坏 Guice-AOP 方法拦截?
【发布时间】:2011-09-13 08:57:51
【问题描述】:

我有一种情况,我需要使用 Guice 3.0 来实例化我的对象,但其中一个值可能会随着每个实例而改变。我无法绑定该值的类型,并且在需要创建实例之前我不会知道。

例如:

public class Foo {
    public Foo(Bar bar, Baz baz) {...}
}

我希望 Guice 注入 Bar 参数,但在需要 Foo 之前我不会知道 Baz。该值也不是特定于范围的(例如 RequestScope)。

我希望这个对象被 Guice 完全实例化的全部原因是因为我需要方法拦截。在 Guice 中,“手动构建的实例不参与 AOP”。

我尝试使用 Provider<Foo> 执行此操作,但这只允许我使用 public Foo get() { ... }

必须为Baz 的每个可能值创建一个提供程序将是一场配置噩梦,所以我不能简单地在 FooProvider 的构造函数中定义Baz

我觉得我在这里遗漏了一些基本的东西。也许是因为这是我星期五要做的最后一件事。任何想法将不胜感激。

编辑:以下使用“辅助注射”的答案似乎只有在您能够编辑Foo 的来源时才有效。 Foo 在某些情况下实际上可能超出我的控制范围。如果我自己创建实例(即实现我自己的工厂),那么 Guice-AOP 方法拦截器似乎永远不会知道该对象。

【问题讨论】:

    标签: java aop guice factory-pattern


    【解决方案1】:

    看起来“辅助注射”可能是一个解决方案:

    http://google-guice.googlecode.com/svn/trunk/latest-javadoc/com/google/inject/assistedinject/FactoryModuleBuilder.html

    除非你无权注释Foo的构造函数,否则它不起作用。

    编辑:我发现我能够通过扩展类型并将它们添加到我希望使用的构造函数来添加辅助注入注释:

    public class AssistedFoo extends Foo {
        @AssistedInject
        public AssistedFoo(
            Bar bar,
            @Assisted Baz baz) {
            super(bar, baz);
        }
    }
    

    然后在辅助注入注册中使用这个扩展实现:

    public interface FooFactory {
        Foo create(Baz baz);
    }
    
    //...
    
    install(new FactoryModuleBuilder()
        .implement(Foo.class, AssistedFoo.class)
        .build(FooFactory.class));
    

    仅当您无权更改Foo 时才需要额外的继承类。显然,如果类是 final,则此解决方法将不起作用。

    【讨论】:

    • 如果我无法将@Assisted 注释添加到Baz 参数怎么办?例如当我不控制Foo的来源时。
    • 只需编写自己的FooFactory 类,将Bar 注入其中,然后让它公开一个采用Baz 参数的方法。然后该方法可以只是return new Foo(bar, baz)。所有 Assisted Inject 所做的(或多或少)都是让您不必编写简单的实现。
    • 这实际上回到了我开始的方式,但 Guice 不允许在它没有创建的对象上使用方法拦截器 (Guice-AOP)。似乎我可能遇到两个功能的限制冲突的情况。
    • 是的,听起来你可能有。
    • 我更新了我的答案,如果你真的需要让方法拦截为此工作,你可以尝试一下。
    【解决方案2】:

    如果你不能使用 Assisted Inject 创建工厂,你可以编写自己的工厂实现:

    // same as for Assisted Inject
    public interface FooFactory {
      Foo createFoo(Baz baz);
    }
    
    public class FooFactoryImpl implements FooFactory {
      private final Bar bar;
    
      @Inject
      public FooFactoryImpl(Bar bar) {
        this.bar = bar;
      }
    
      public Foo createFoo(Baz baz) {
        return new Foo(bar, baz);
      }
    }
    

    然后只需 bind(FooFactory.class).to(FooFactoryImpl.class) 并在需要创建 Foos 的任何位置注入 FooFactory

    编辑:

    您可以做一个非常尴尬的解决方法(这将允许在Foo 上拦截方法):

    • bind(Foo.class).toConstructor(...)
    • 定义并绑定SimpleScope 范围描述的here
    • bind(Baz.class).in(BatchScoped.class)
    • Provider<Foo>SimpleScope 注入到要创建Foos 的类中。

    然后:

    public Foo someMethod(Baz baz) {
      simpleScope.enter();
      try {
        simpleScope.seed(Baz.class, baz);
        /*
         * We're in the scope for Baz, so it can be injected normally into Foo
         * right now.
         */
        return fooProvider.get();
      } finally {
        scope.exit();
      }
    }
    

    如果方法不是private...,您甚至可以在此类方法上使用方法拦截来处理范围,尽管这可能不是一个好主意,因为如果您不使用它会令人困惑方法中的参数,但它必须在那里才能工作。

    【讨论】:

    • 范围的有趣使用。对其他人来说似乎有点沉重和/或难以理解。表明 Guice 人可能有机会添加一些机制。
    • @McKAMEY:是的,我会说这是一个非常丑陋的 hack!我认为一个更好的解决方案是忍受不能在Foo 上使用方法拦截。一种选择可能是创建自己的(可拦截和可辅助注入)类来包装 Foo 并在其上公开您需要的方法。
    • 如果您查看其他答案,我可以通过继承 Foo 获得辅助注射工作。这使我可以控制构造函数注释。
    • @McKAMEY:是的,这对于您可以扩展的任何类都是一个很好的解决方案。
    猜你喜欢
    • 2015-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-26
    • 1970-01-01
    相关资源
    最近更新 更多