felix ipojo入门教程

Felix iPOJO介绍

在OSGI框架中,Felix iPOJO意在简化面向服务编程。iPOJO的全称是inject POJO。Felix iPOJO提供了一种新的开发OSGI服务组件的方式,主要目标是简化服务组件的实现,使环境的动态变化对服务组件的实现透明。Felix iPOJO可以让开发者更好的分离功能代码(比如:POJO)和非功能代码(比如:依赖管理、服务的提供、配置等)。

Felix iPOJO服务组件简介

服务组件可以提供和(或者)依赖一个或者多个服务,服务(service)是一个实现了某个java接口的对象。另外Felix iPOJO提供了一个回调的机制,把各种状态变化通知给组件。

组件是Felix iPOJO的核心概念,一个组件描述了服务的依赖、提供哪些服务以及哪些回调功能;这些信息配置在组件描述中(metadata)。Felix iPOJO中的第二个重要的概念是组件的实例,一个组件实例是一个特殊版本的组件。通过合并组件描述和组件实例的配置,Felix iPOJO可以在运行时管理组件。比如:管理其生命周期、服务的依赖注入、开放服务、发现需要的服务。

Felix iPOJO Hello World(简单的示例)

下面的示例将阐述怎样使用Felix iPOJO的核心功能。示例中包含两个组件,一个提供Hello服务,一个需要任意数量的Hello服务。这些组件通过maven打包,被放在三个不同的bundle中。

  • hello.service包含服务接口Hello的定义
  • hello.impl包含一个组件,它实现了Hello服务接口,具有了提供Hello服务的能力
  • hello.client包含了一个组件的消费者,它依赖一个或者多个Hello服务

这个示例的代码可以在这里下载

1. 准备工作

开始前,先下载并安装配置maven

2. 第一个工程:服务接口(hello.service

这是一个maven工程,工程仅有一个Hello接口类,类的路径为:src/main/java/ipojo/example/hello/Hello.java

在工程目录中,pom.xml文件必须包含了maven-bundle-plugin插件,因为这个工程最终mvn install生成的jar包必须是符合osgi规范的bundle包,同时因为Hello这个类是接口类,需要提供给其他的bundle import,所以在pom.xml中需要export这个接口类所在的包ipojo.example.hello。如下所示:

在这个工程中使用mvn clean install命令执行后,如果结果是SUCCESS,那么maven repository里面应该有这个jar包了,另外工程的target目录下应该也有打包好的jar包。这个包将在接下来的两个工程中被依赖上。

3. 第二个工程:服务提供者(hello.impl)

这个maven工程(hello.impl)里面有一个实现了Hello接口的类,这个工程依赖了第一个工程hello.service,这个类路径为:src/main/java/ipojo/example/hello/impl/HelloImpl.java

为了管理这个组件,iPOJO需要一些描述信息来描述这个组件提供了Hello这个接口的服务。iPOJO的描述文件(metadata.xml)在hello.impl工程的根目录下,这个描述文件的内容如下:

‘component’元素的‘classname’属性是为了告诉iPOJO这个组件的实现类是哪个;‘name’属性是给这个组件取了一个名字,如果没有属性,那么它的值就是属性‘classname’的值;这个示例中‘component’元素下有‘provides’元素,这是为了告诉iPOJO需要管理这个组件怎样公布到osgi容器中,在上面的示例中,‘provides’元素没有属性,那么iPOJO会为这个组件实现的所有的服务接口都注册上这个组件,当然‘provides’元素可以指定一个或者多个服务接口。

‘instance’元素是告诉iPOJO,在这个bundle在osgi中启动的时候创建一个组件的实例。

这个maven的工程的pom.xml文件包含以下的内容:

其中<Private-Package>ipojo.example.hello.impl</Private-Package>是指HelloImpl类所在包不被export出去,其他bundle不能import到这个包下面的类。配置好后,使用mvn clean install命令打包。

4. 第三个工程:服务消费者(hello.client)

这个工程中有一个类HelloClient类,这个类中有一个Hello数组类型的field,这个field需要让iPOJO自动的注入,当有实现了Hello服务接口的组件实例被公布后,iPOJO会自动的把实例添加到field中。

在上面的代码中,服务消费者HelloClient创建了一个线程间歇地调用可用的Hello类型的服务实例,在至少一个Hello类型的服务实例出现时,这个线程会由iPOJO的回调机制启动。代码中组件的实现对象m_hello被直接使用,且m_hello是一个数组。在iPOJO中服务数组代表了一种依赖聚合或者多个合适的依赖,当然iPOJO也支持单一的简单的对象注入,只需要把代表数组的中括号去掉即可。在一个服务的组件类中声明了一个属性,那么这个组件类的其他部分代码就可以直接使用这个属性,因为这个属性会被自动初始化,比如:m_hello[i].sayHello(“world”)。

iPOJO也是同步管理服务的。服务的调用不需要使用同步语句块。这种同步机制是以线程为单位的,每个访问服务的方法都在线程上附带了一个给定的服务实例,这样线程就能看见相同服务实例,甚至是嵌套的方法调用。线程看不到不同的服务实例,除非它从开始进入的方法中完全退出。

组件提供了两个回调方法,用来激活和钝化服务实例,比如starting()和stopping()。当相关的某个组件的状态有变更时会通知组件来回调这些回调方法。在iPOJO中,组件状态要么是INVALID(比如:不是所有的组件约束都满足时),要么是VALID(比如:所有的组件约束都满足了)。在这个例子中,starting回调方法创建和启动了一个线程,stopping回调方法停止了这个线程。在这个组件的metadata(描述信息)会告诉iPOJO,当组件的状态变为VALID或者INVALID的时候,要回调starting或者stopping方法。

iPOJO描述文件命名为metadata.xml,包含如下内容:

上面的xml配置中,组件component元素有classname属性,用来标明这个组件的实现类是哪个;requires元素描述了这个组件的m_hello字段依赖了Hello服务;callback元素描述了当组件的状态改变时哪个方法会被回调;instance元素会让iPOJO创建一个组件的实例(注意:这里没有配置instance元素的name属性,iPOJO会自动给这个instance设置一个默认的name)。instance元素下面有子元素property,它配置了组件HelloClient的m_name字段的值为字符串“world”。

最后,pom.xml的配置如下:

在dependencies中有服务接口的依赖,因为HelloClient组件中依赖了这个接口Hello类。pom.xml配置完成后,即可执行下面的命令对服务消费者进行打包:

mvn clean install

如果打包成功,在target目录下回看一个jar包,这个jar包既可以当做普通的jar包,也可以当做bundle包,因为它与普通jar包唯一的不同就是MENIFEST.MF文件中多了一些OSGI的描述信息。

5. 运行实例

要运行这个实例,首先需要启动Felix。可以先下载Felix Framework Distribution,然后通过下面的命令来启动Felix:

java -jar bin/felix.jar

你可以在控制台中使用”ps”命令来检查已经安装的bundle:

-> ps
START LEVEL 1
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (2.0.5)
[ 1] [Active ] [ 1] Apache Felix Bundle Repository (1.4.3)
[ 2] [Active ] [ 1] Apache Felix iPOJO (1.6.0)
[ 3] [Active ] [ 1] Apache Felix iPOJO Arch Command (1.6.0)
[ 4] [Active ] [ 1] Apache Felix Shell Service (1.4.2)
[ 5] [Active ] [ 1] Apache Felix Shell TUI (1.4.1)

接下来安装服务接口(hello.service)、服务提供者(hello.impl)、服务消费者(hello.client):

start file:../hello.service/target/hello.service-1.0.0.jar
start file:../hello.impl/target/hello.impl-1.0.0.jar
start file:../hello.client/target/hello.client-1.0.0.jar

以上的指令输入后,hello.service-1.0.0.jar的id为6,hello.impl-1.0.0.jar的id为7,hello.client-1.0.0.jar的id为8。在启动了服务提供者(hello.impl)后,服务消费者(hello.client)会被自动激活,由于服务消费者的instance实例在创建时就注入了m_name字段的值为“world”,因此控制台会循环输出“hello world”:

-> hello world
hello world
hello world
hello world

如果此时在控制台执行“stop 7”的指令,则服务消费者hello.client会自动钝化,并停止输出“hello world”,因为它依赖的服务实例对象(hello.impl的HelloService)变得不可用了。如果此时再重新“start 7“(或者重新安装部署实现了Hello接口的其他bundle),那么服务消费者又会变得可用。这两个步骤的效果如下:

-> stop 7
-> arch
Instance ArchCommand -> valid
Instance ipojo.example.hello.client.HelloClient-0 -> invalid
-> arch -instance ipojo.example.hello.client.HelloClient-0
instance name=”ipojo.example.hello.client.HelloClient-0″
 component.type=”ipojo.example.hello.client.HelloClient”
 state=”invalid” bundle=”8″
        object name=”ipojo.example.hello.client.HelloClient@137c60d”
        handler name=”org.apache.felix.ipojo.handlers.dependency.DependencyHandler” state=”invalid”
               requires aggregate=”true” optional=”false” state=”resolved” specification=”ipojo.example.hello.Hello”
        handler name=”org.apache.felix.ipojo.handlers.lifecycle.callback.LifecycleCallbackHandler” state=”valid”
        handler name=”org.apache.felix.ipojo.handlers.architecture.ArchitectureHandler” state=”valid”
-> start 7
hello world
-> arch
Instance ArchCommand -> valid
Instance ipojo.example.hello.client.HelloClient-0 -> valid
Instance HelloService -> valid
-> arch -instance ipojo.example.hello.client.HelloClient-0
instance name=”ipojo.example.hello.client.HelloClient-0″
 component.type=”ipojo.example.hello.client.HelloClient”
  state=”valid” bundle=”8″
        object name=”ipojo.example.hello.client.HelloClient@137c60d”
        handler name=”org.apache.felix.ipojo.handlers.dependency.DependencyHandler” state=”valid”
               requires aggregate=”true” optional=”false” state=”resolved” specification=”ipojo.example.hello.Hello”
                        uses service.id=”38″ instance.name=”HelloService”
        handler name=”org.apache.felix.ipojo.handlers.lifecycle.callback.LifecycleCallbackHandler” state=”valid”
        handler name=”org.apache.felix.ipojo.handlers.architecture.ArchitectureHandler” state=”valid”

总结

felix ipojo配置和spring的配置的目的差不多,都是为了解耦和简化依赖注入。但是felix ipojo比spring的配置要复杂一些,因为spring的bean都是创建后就不怎么变了,而且felix ipojo的instance可以随组件的任意时候的安装而产生,又随组件的任意时候卸载而悄悄的消失,所以ipojo的配置除了基本的spring功能外,主要是为了很好的解决组件中service来去匆匆的问题。

进入正题,felix ipojo配置主要是围绕着类、component和instance三个元素。以下是我对三者的理解:

  • 类:平时写的普通的ipojo类(暂时先不考虑ipojo annotation),类有field,有构造函数,有method,但是没有描述这些field会被怎么注入,注入何种依赖,也没有描述这个类中的一些方法是如何按照ipojo生命周期的流程有机的结合起来。它只是一个类而已。
  • component:一个类可以对应一至多个component(如果component是用annotation的方式来配置的话,那么一个类就只跟一个component对应了),component描述了一个类提供何种功能、其field将怎样被什么样的instance注入、其method何时被调用、在ipojo生命周期中调用顺序是怎样的。
  • instance:每个instance都是根据component产生的一个具体的实例对象,它和spring中的bean的含义很接近。一个component可以有多个不同的instance(spring中一个类也可以配置多个不同id的bean),由于component描述了一个instance的整体结构(甚至可以给field设置默认实现或者默认值),但是instance自己还是可以给field设置符合一定条件的instance。

在使用felix ipojo之前首先需要在maven的pom.xml里面添加ipojo插件配置:maven-ipojo-plugin使用指南,然后在ipojo配置文件目录或者metadata.xml文件中添加component和instance配置。

Reference

原创文章:felix ipojo入门教程,转载请注明:转载自戎码一生

Post Footer automatically generated by wp-posturl plugin for wordpress.