注入无法控制实例化的类的最佳方法?

发布于 2024-10-23 11:49:22 字数 1098 浏览 5 评论 0原文

我对 Guice 相当陌生,所以希望这不是一个明显的问题...

我正在处理一个框架,它使用反射动态实例化我的应用程序类之一(我们称之为 C),所以我无法控制它的实例化。此外,在框架实例化新创建的 C 后,我没有简单的方法来访问它。这是 C 的样子:

    public class C implements I {

        public C() {
            // this ctor is invoked by the framework so can't use DI here?
        }        

        public void initialize() {
            // this is a post instantiation hook which the f/w will invoke
        }

        private void m() {
            Dependency d = Dependency.getInstance();
            d.doSmth();
        }
    }

我希望 C 使用 DI 获取 Dependency。这里的Dependency显然是一个单例,但一般情况下不必如此。

我想出了两种可能的解决方案,但都感觉不太整洁,所以我想我应该询问专家:

  1. 静态注入。不过,我很难看出这对服务定位器反模式有何显着改进 - 我最终得到了可以在外部操作的非最终静态字段......嗯。

  2. 该框架支持 C 的钩子在实例化后初始化自身(上面显示的 initialize() 方法)。此方法可以使用 setter 注入自行注入新创建的实例。

对于 2,我想这意味着更多的外部可更改性,但至少更明确地暴露了单元测试之类的依赖关系。问题是:在这种情况下我如何获得 Guice 注射器(不依赖另一个服务定位器)?我还读到(并且倾向于同意)在整个应用程序代码中明确引用注入器是可疑的做法。

我们将非常感谢您的见解。

非常感谢,
基督教

I'm fairly new to Guice so hopefully this isn't an obvious question...

I'm dealing with a framework which dynamically instantiates one of my application classes (let's call it C) using reflection, so I have no control over its instantiation. Furthermore, there is no easy way for me to access a newly created C after the framework has instantitated it. HEre's what C would look like:

    public class C implements I {

        public C() {
            // this ctor is invoked by the framework so can't use DI here?
        }        

        public void initialize() {
            // this is a post instantiation hook which the f/w will invoke
        }

        private void m() {
            Dependency d = Dependency.getInstance();
            d.doSmth();
        }
    }

I would like C to acquire Dependency using DI. Here Dependency is clearly a singleton but this needn't be the case generally.

I have come up with two possible solutions, neither of which feels very tidy, so I thought I'd ask the experts:

  1. Static injection. I struggle to see how this improves drastically over the service locator anti-pattern though - I end up with non final static fields which could be manipulated externally... hmmm.

  2. The framework supports a hook for C to initialize itself post instantiation (the initialize() method shown above). This method could self-inject the newly created instance using setter injection.

With 2, I guess this means more external alterability but at least exposes the dependency a little more explicitly for things like unit tests. Trouble is: how do I get my hands on the Guice Injector in that case (short of relying on yet another service locator)? I've also read (and tend to agree with) that explicitly referring to the Injector throughout app code is dubious practice.

Your insight would be really appreciated.

Many thanks,
Christian

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

少女七分熟 2024-10-30 11:49:22

您可以尝试将提供程序静态注入到 C 中。静态注入更难以测试。但这些提供程序使 Guice 能够在延迟创建依赖项的同时急切地验证您的依赖项。

public class C implements I {
  @Inject static Provider<Dep1> dep1Provider;
  @Inject static Provider<Dep2> dep2Provider;

  ...

  public void initialize() {
    Dep1 dep1 = dep1Provider.get();
    Dep2 dep2 = dep2Provider.get();
    ...
  }
}

You could try statically injecting providers into C. Static injection more awkward to test. But the providers make it possible for Guice to validate your dependencies eagerly, while creating them lazily.

public class C implements I {
  @Inject static Provider<Dep1> dep1Provider;
  @Inject static Provider<Dep2> dep2Provider;

  ...

  public void initialize() {
    Dep1 dep1 = dep1Provider.get();
    Dep2 dep2 = dep2Provider.get();
    ...
  }
}
留一抹残留的笑 2024-10-30 11:49:22

如果您通过静态变量和静态访问器方法使 Guice 注入器可用,您可以像这样解决它:

public class C implements I {

    public C() {
        // this ctor is invoked by the framework, you can't do injection here.
    }        

    public void initialize() {
        MyGuice.getInjector().inject(this);
    }

    @Inject
    protected void init(Dep1 dep1, Dep2 dep2) {
        this.dep1 = dep1;
        this.dep2 = dep2;
    }

    private void m() {
        dept1.doSmth();
    }
}

静态调用有点反模式,您可以使用一个简单的注释来删除该依赖关系,该注释负责构造函数后注入如果您愿意使用执行代码编织或后编译步骤的解决方案。这样你就可以注入所有对象,即使它们没有初始化方法。您可以在此处找到相关示例:如何使用 AspectJ 注入模型对象

@Configurable
public class C implements I {

    public C() {
        // this ctor is invoked by the framework, you can't do injection here.
    }        

    @Inject
    protected void init(Dep1 dep1, Dep2 dep2) {
        this.dep1 = dep1;
        this.dep2 = dep2;
    }

    private void m() {
        dept1.doSmth();
    }
}

If you make your Guice injector available by static variable and static accessor methods you can solve it like this:

public class C implements I {

    public C() {
        // this ctor is invoked by the framework, you can't do injection here.
    }        

    public void initialize() {
        MyGuice.getInjector().inject(this);
    }

    @Inject
    protected void init(Dep1 dep1, Dep2 dep2) {
        this.dep1 = dep1;
        this.dep2 = dep2;
    }

    private void m() {
        dept1.doSmth();
    }
}

The static call is a bit of an anti-pattern, you could remove that dependency with a simple annotation that takes care of the post constructor injection if you are willing to use a solution that does code weaving or a post compile step. This way you can inject all objects even if they don't have an initialize method. You can find an example of this here: How to inject model object with AspectJ.

@Configurable
public class C implements I {

    public C() {
        // this ctor is invoked by the framework, you can't do injection here.
    }        

    @Inject
    protected void init(Dep1 dep1, Dep2 dep2) {
        this.dep1 = dep1;
        this.dep2 = dep2;
    }

    private void m() {
        dept1.doSmth();
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文