3

I am having some problems understanding the concept of component instantiation in iPOJO. I read this guide and I get the analogy to classes and objects but I still have some concrete problems and some conceptual problems that I hope someone can clarify

I thought I needed to create instances via iPOJO (@Instantiate or factories) only for service providers since they never use new because the impl is always hidden. However, I have some consumers @Component that I instantiate myself (say in a main() method where I call new on them directly). I made them @Component because they need to have things injected. I was assuming that the ipojo bytecode manipulation would make it so that when the objects were constructed, they would have their dependencies injected (I'm using mostly method injection with @Bind) but it seems that is not the case. Can someone clarify this to me please. Now it seems to me that for iPOJO to do any injection at all I need to always use one of the iPOJO instantiation techniques. The problem I have is that then the constructors I made in the consumer classes are not called.

This is a simplified example to illustrate my confusion

@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);
    }
}

So, as can be seen here, I need to have all the providers of interface External, but I also need a Bar object that I pass when I construct the object using new MyFoo(someBar)

My problem is that if I need to pass a Bar to the constructor then I need to use new; but If i use new, iPojo never invokes my injection method. On the other hand, if I use iPOJOs instantiation (say I add @Instantiate) then the injection does happen but the constructor is not invoked, so the bind throws a NPE because the list has not been created yet + bar will not be set. I know I can create the list inside the bind method, but my question is more conceptual.

  1. How are you supposed to accomplish this (framework injection + argument passing in the constructor)?
  2. How can iPOJO be calling addExternal (which means the object has been created) without calling my one and only constructor that creates the object? this is very counter-intuitive in standard java
  3. Are you just not supposed to use constructors when using iPOJO components maybe?
4

1 回答 1

3

iPojo 的工作方式类似于其他 DI(依赖注入)框架,例如 Blueprint (OSGi)、Spring、Guice 等。也就是说,为了让 DI 完成它的工作,您必须让容器 (iPojo) 管理您正在与之交互的对象。因此,您的倾向是正确的:您必须使用 iPojo 的一种实例化技术。如果您选择在对象上使用 new,那么您的代码将管理生命周期(因此您将需要手动“注入”所有参数)。

在示例中,您的构造函数没有被调用,因为开箱即用的 iPojo 将支持两种主要情况:默认构造函数 (MyFoo()) 或接受 BundleContext 的构造函数 (MyFoo(BundleContext c))。如果您在版本 1.7.0 或更高版本上使用@Requires/@Property分别在您的构造函数变量(或元数据中的等效项)上,iPojo 还支持构造函数服务和属性注入。

当 iPojo字节码操作启动时,它会操作字节码以使其可由 iPojo管理,而不是iPojo 管理。它通过添加 MyClass(InstanceManager) 构造函数以及 iPojo 在实例化对象时在内部使用的其他内容来做到这一点。

所以,回答你的问题:

  1. 您可以通过将注入变量定义为属性或服务要求并委托给 iPojo 来创建它们来完成此操作。iPojo 有多种与实例创建交互的方法,但您可能感兴趣的两种方法是通过 OSGi ConfigurationAdmin 或 iPojo 工厂(假设您的 publicFactory 设置为 true @Component,这是默认设置)。作为使用配置管理员的示例并假设 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注释中覆盖它。然后将您的 somebar 添加到属性并更新配置。iPojo 工厂与此类似,尽管我相信它使用构建器模式来创建实例。如果您不想添加对 OSGi 配置管理员的依赖项,则最好使用 iPojo 工厂。

  2. 我不是 iPojo 内部的专家,我在这里根据我的经历和我在他们的文档中读到的内容得出一个结论。如前所述,iPojo 进行字节码操作以增强您的类,使其可由 iPojo 管理。它为您的类添加的一个特征是带有InstanceManager. 由于您添加的构造函数没有绑定到它的元数据(即注释 - 我假设清单或 xml 文件中没有手动元数据)它或多或少完全忽略该构造函数,而是选择使用一个由字节码操作过程动态生成。完成后,它最终会调用您的@Bind方法来添加外部服务(因为该方法已标记),您会发现自己处于您所描述的状态。
  3. 这里的关键是让 iPojo 管理对象的生命周期,如上所述。通过调用构造函数,您实际上是在管理该实例的生命周期,因此 iPojo 脱离了循环。因此,您可以使用构造函数并手动将参数“注入”到其中,或者依靠 iPojo 为您执行此操作。
于 2014-04-07T21:44:19.070 回答