我在理解iPOJO中的组件实例化概念时遇到了一些问题。我阅读了本指南我得到了类和对象的类比,但我仍然有一些具体问题和一些概念问题,我希望有人可以澄清
我以为我只需要通过 iPOJO(@Instantiate 或工厂)为服务提供商创建实例,因为他们从不使用 new,因为 impl 总是隐藏的。但是,我有一些使用者@Component我自己实例化(例如在main()方法中,我直接对它们调用new)。我让他们@Component,因为他们需要注射东西。我假设 ipojo 字节码操作将使它在构造对象时注入它们的依赖项(我主要使用带有 @Bind 的方法注入),但似乎事实并非如此。有人可以向我澄清一下吗?现在在我看来,要让 iPOJO 进行任何注入,我需要始终使用 iPOJO 实例化技术之一。我遇到的问题是,我在消费者类中制作的构造函数没有被调用。
这是一个简化的例子来说明我的困惑
@Component(name="test")
public class MyFoo {
private List<External> externals; //injected
private Bar bar; //passed via constructor. Bar is *not* a @Component
public MyFoo(Bar otherBar) {
bar = otherBar;
externals = new ArrayList();
}
@Bind(aggregate=true)
public addExternal(External service) {
externals.add(service);
}
}
因此,从这里可以看出,我需要拥有接口External
的所有提供程序,但我还需要一个Bar
对象,当我使用 new MyFoo(someBar)
构造对象时会传递该对象
我的问题是,如果我需要将Bar
传递给构造函数,那么我需要使用 new;但是如果我使用 new,iPojo 永远不会调用我的注入方法。另一方面,如果我使用 iPOJOs 实例化(比如我添加 @Instantiate),那么注入确实会发生,但构造函数没有被调用,所以绑定会抛出一个 NPE,因为列表尚未创建 + bar 不会设置。我知道我可以在绑定方法中创建列表,但我的问题更具概念性。
- 你应该如何做到这一点(框架注入+构造函数中的参数传递)?
- iPOJO 如何在不调用创建对象的唯一构造函数的情况下调用 addOutside(这意味着对象已创建)? 这在标准Java中是非常违反直觉 的
- 也许在使用 iPOJO 组件时,您不应该使用构造函数吗?
iPojo的工作方式类似于其他DI(依赖注入)框架,如蓝图(OSGi),Spring,Guice等。也就是说,为了让 DI 完成它的工作,你必须让容器 (iPojo) 管理你正在与之交互的对象的生命周期。所以,你的倾向是正确的:你必须使用iPojo的实例化技术之一。如果选择在对象上使用 new,则代码将管理生命周期(因此需要手动"注入"所有参数)。
在示例中,不会调用构造函数,因为开箱即用的 iPojo 将支持两种主要情况:默认构造函数 (MyFoo()) 或接受 BundleContext 的构造函数 (MyFoo(BundleContext c))。 如果您使用的是 1.7.0 或更高版本,则 iPojo 还支持构造函数服务和属性注入,分别在构造函数变量(或元数据中的等效值)上使用 @Requires
/@Property
。
操作启动时,它会操纵字节码,使其可由 iPojo 管理,而不是由 iPojo 管理。它通过添加 MyClass(InstanceManager) 构造函数来实现这一点,该构造函数在实例化对象时由 iPojo 内部使用。
因此,要回答您的问题:
您可以通过将注入的变量定义为属性或服务要求并委托给 iPojo 来创建它们来实现此目的。 iPojo 有多种与实例创建交互的方法,但您可能感兴趣的两种方法是通过 OSGi ConfigurationAdmin 或 iPojo 工厂(假设 publicFactory 在您的
@Component
上设置为 true, 这是默认值)。作为使用配置管理员的示例,假设 Bar 不在 OSGi 服务注册表中,您的 MyFoo 类将如下所示:@Component(name="test") public class MyFoo { private List<External> externals; //injected private Bar bar; //passed via constructor. Bar is *not* a @Component public MyFoo(@Property(name = "somebar") Bar otherBar) { bar = otherBar; externals = new ArrayList(); } @Bind(aggregate=true) public addExternal(External service) { externals.add(service); } }
然后,您将使用配置管理员(或 iPojo 工厂)来创建实例。因此,您的主要方法是从OSGi服务层拉入ConfigAdmin或iPojo工厂(例如,通过将其从BundleContext中提取出来等),创建一个配置,并将"somebar"属性设置为"new Bar()"并保存该配置。然后,为您创建的 iPojo 托管服务工厂将使用您在配置中提供的新 Bar 实例化 MyFoo 版本,并将其注入到 MyFoo 构造函数中:
... Configuration config = configAdmin.createFactoryConfiguration( MyFoo.class.getCanonicalName()); Hashtable<String, String> properties = new Hashtable<>(); properties.put("somebar", new Bar()); // This is where you new config.update(properties); // do something useful with the config if you need to update // the instance or destroy it later. ...
配置管理员
createFactoryConfiguration
的第一个参数指定 pid。在这种情况下,pid 是类的名称,这是 iPojo 默认使用的名称,除非您在@Component
注中覆盖它。然后,将某个栏添加到属性并更新配置。iPojo 工厂与此类似,尽管我相信它使用构建器模式来创建实例。如果您不想添加对 OSGi 配置管理员的依赖,则最好使用 iPojo 工厂。
我- 不是 iPojo 内部结构方面的专家,我在这里根据我的经历和我在他们的文档中读到的内容得出结论。如前所述,iPojo 执行字节码操作以增强您的类,使其可由 iPojo 管理。它添加到您的类中的一个特征是带有
InstanceManager
的构造函数。由于您添加的构造函数没有绑定到它的元数据(即注释 - 我假设清单或 xml 文件中没有手动元数据),它或多或少完全忽略了该构造函数,而是选择使用字节码操作过程动态生成的构造函数。完成后,它最终将调用您的@Bind
方法来添加外部服务(因为该方法已标记),您会发现自己处于您描述的状态。 - 这里的关键是让 iPojo 管理对象的生命周期,如上所述。通过调用构造函数,您实际上是在管理该实例的生命周期,因此 iPojo 不在循环中。因此,您可以使用构造函数并手动将参数"注入"其中,也可以依靠 iPojo 为您执行此操作。