在OSGI框架中,Felix iPOJO意在簡化面向服務編程。iPOJO的全稱是inject POJO。Felix iPOJO提供了一種新的開發OSGI服務組件的方式,主要目標是簡化服務組件的實現,使環境的動態變化對服務組件的實現透明。Felix iPOJO可讓開發者更好的分離功能代碼(好比:POJO)和非功能代碼(好比:依賴管理、服務的提供、配置等)。 html
服務組件能夠提供和(或者)依賴一個或者多個服務,服務(service)是一個實現了某個java接口的對象。另外Felix iPOJO提供了一個回調的機制,把各類狀態變化通知給組件。 java
組件是Felix iPOJO的核心概念,一個組件描述了服務的依賴、提供哪些服務以及哪些回調功能;這些信息配置在組件描述中(metadata)。Felix iPOJO中的第二個重要的概念是組件的實例,一個組件實例是一個特殊版本的組件。經過合併組件描述和組件實例的配置,Felix iPOJO能夠在運行時管理組件。好比:管理其生命週期、服務的依賴注入、開放服務、發現須要的服務。 spring
下面的示例將闡述怎樣使用Felix iPOJO的核心功能。示例中包含兩個組件,一個提供Hello服務,一個須要任意數量的Hello服務。這些組件經過maven打包,被放在三個不一樣的bundle中。 apache
這個示例的代碼能夠在這裏下載 編程
開始前,先下載並安裝配置maven。
數組
這是一個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。以下所示: maven
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
<project>
<modelVersion>
4.0.0
</modelVersion>
<packaging>
bundle
</packaging>
<groupId>
ipojo.example
</groupId>
<artifactId>
hello.service
</artifactId>
<version>
1.0.0
</version>
<name>
Hello Service
</name>
<build>
<plugins>
<plugin>
<groupId>
org.apache.felix
</groupId>
<artifactId>
maven-bundle-plugin
</artifactId>
<version>
2.0.1
</version>
<extensions>
true
</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>
${pom.artifactId}
</Bundle-SymbolicName>
<Export-Package>
ipojo.example.hello
</Export-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
|
在這個工程中使用mvn clean install命令執行後,若是結果是SUCCESS,那麼maven repository裏面應該有這個jar包了,另外工程的target目錄下應該也有打包好的jar包。這個包將在接下來的兩個工程中被依賴上。 ide
這個maven工程(hello.impl)裏面有一個實現了Hello接口的類,這個工程依賴了第一個工程hello.service,這個類路徑爲:src/main/java/ipojo/example/hello/impl/HelloImpl.java 函數
爲了管理這個組件,iPOJO須要一些描述信息來描述這個組件提供了Hello這個接口的服務。iPOJO的描述文件(metadata.xml)在hello.impl工程的根目錄下,這個描述文件的內容以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<ipojo
xmlns
:
xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
xsi
:
schemaLocation
=
"org.apache.felix.ipojo http://felix.apache.org/ipojo/schemas/CURRENT/core.xsd"
xmlns
=
"org.apache.felix.ipojo"
>
<component
classname
=
"ipojo.example.hello.impl.HelloImpl"
name
=
"HelloProvider"
>
<provides
/>
</component>
<instance
component
=
"HelloProvider"
name
=
"HelloService"
/>
</ipojo>
|
‘component’元素的‘classname’屬性是爲了告訴iPOJO這個組件的實現類是哪一個;‘name’屬性是給這個組件取了一個名字,若是沒有屬性,那麼它的值就是屬性‘classname’的值;這個示例中‘component’元素下有‘provides’元素,這是爲了告訴iPOJO須要管理這個組件怎樣公佈到osgi容器中,在上面的示例中,‘provides’元素沒有屬性,那麼iPOJO會爲這個組件實現的全部的服務接口都註冊上這個組件,固然‘provides’元素能夠指定一個或者多個服務接口。
‘instance’元素是告訴iPOJO,在這個bundle在osgi中啓動的時候建立一個組件的實例。
這個maven的工程的pom.xml文件包含如下的內容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
<project>
<modelVersion>
4.0.0
</modelVersion>
<packaging>
bundle
</packaging>
<groupId>
ipojo.example
</groupId>
<artifactId>
hello.impl
</artifactId>
<version>
1.0.0
</version>
<name>
Hello Service Provider
</name>
<dependencies>
<dependency>
<!--Compilation (i.e. class) dependency on the service interface -->
<groupId>
ipojo.example
</groupId>
<artifactId>
hello.service
</artifactId>
<version>
1.0.0
</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>
org.apache.felix
</groupId>
<artifactId>
maven-bundle-plugin
</artifactId>
<version>
2.0.1
</version>
<extensions>
true
</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>
${pom.artifactId}
</Bundle-SymbolicName>
<Private-Package>
ipojo.example.hello.impl
</Private-Package>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>
org.apache.felix
</groupId>
<artifactId>
maven-ipojo-plugin
</artifactId>
<version>
1.6.0
</version>
<executions>
<execution>
<goals>
<goal>
ipojo-bundle
</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
|
其中<Private-Package>ipojo.example.hello.impl</Private-Package>是指HelloImpl類所在包不被export出去,其餘bundle不能import到這個包下面的類。配置好後,使用mvn clean install命令打包。
這個工程中有一個類HelloClient類,這個類中有一個Hello數組類型的field,這個field須要讓iPOJO自動的注入,當有實現了Hello服務接口的組件實例被公佈後,iPOJO會自動的把實例添加到field中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
package
ipojo
.
example
.
hello
.
client
;
import
ipojo
.
example
.
hello
.
Hello
;
public
class
HelloClient
implements
Runnable
{
/**
* Delay between two invocations.
*/
private
static
final
int
DELAY
=
10000
;
/**
* Hello services.
* Injected by the container.
* */
private
Hello
[
]
m_hello
;
/**
* End flag.
* */
private
boolean
m_end
;
/**
* m_name field.
* */
private
String
m_name
;
/**
* Run method.
* @see java.lang.Runnable#run()
*/
public
void
run
(
)
{
while
(
!
m_end
)
{
try
{
invokeHelloServices
(
)
;
Thread
.
sleep
(
DELAY
)
;
}
catch
(
InterruptedException
ie
)
{
/* will recheck end */
}
}
}
/**
* Invoke hello services.
*/
public
void
invokeHelloServices
(
)
{
for
(
int
i
=
0
;
i
<
m_hello
.
length
;
i
++
)
{
System
.
out
.
println
(
m_hello
[
i
]
(
)
.
sayHello
(
m_name
)
)
;
}
}
/**
* Starting.
*/
public
void
starting
(
)
{
Thread
thread
=
new
Thread
(
this
)
;
m_end
=
false
;
thread
.
start
(
)
;
}
/**
* Stopping.
*/
public
void
stopping
(
)
{
m_end
=
true
;
}
}
|
在上面的代碼中,服務消費者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,包含以下內容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<ipojo
xmlns
:
xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
xsi
:
schemaLocation
=
"org.apache.felix.ipojo http://felix.apache.org/ipojo/schemas/CURRENT/core.xsd"
xmlns
=
"org.apache.felix.ipojo"
>
<component
classname
=
"ipojo.example.hello.client.HelloClient"
>
<requires
field
=
"m_hello"
/>
<callback
transition
=
"validate"
method
=
"starting"
/>
<callback
transition
=
"invalidate"
method
=
"stopping"
/>
<properties>
<property
field
=
"m_name"
name
=
"hello.name"
/>
</properties>
</component>
<instance
component
=
"ipojo.example.hello.client.HelloClient"
>
<property
name
=
"hello.name"
value
=
"world"
/>
</instance>
</ipojo>
|
上面的xml配置中,組件component元素有classname屬性,用來標明這個組件的實現類是哪一個;requires元素描述了這個組件的m_hello字段依賴了Hello服務;callback元素描述了當組件的狀態改變時哪一個方法會被回調;instance元素會讓iPOJO建立一個組件的實例(注意:這裏沒有配置instance元素的name屬性,iPOJO會自動給這個instance設置一個默認的name)。instance元素下面有子元素property,它配置了組件HelloClient的m_name字段的值爲字符串「world」。
最後,pom.xml的配置以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
<project>
<modelVersion>
4.0.0
</modelVersion>
<packaging>
bundle
</packaging>
<groupId>
ipojo.example
</groupId>
<artifactId>
hello.client
</artifactId>
<version>
1.0.0
</version>
<name>
Hello Client
</name>
<dependencies>
<dependency>
<!-- 編譯時依賴了服務接口 -->
<groupId>
ipojo.example
</groupId>
<artifactId>
hello.service
</artifactId>
<version>
1.0.0
</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>
org.apache.felix
</groupId>
<artifactId>
maven-bundle-plugin
</artifactId>
<version>
2.0.1
</version>
<extensions>
true
</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>
${pom.artifactId}
</Bundle-SymbolicName>
<Private-Package>
ipojo.example.hello.client
</Private-Package>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>
org.apache.felix
</groupId>
<artifactId>
maven-ipojo-plugin
</artifactId>
<version>
1.6.0
</version>
<executions>
<execution>
<goals>
<goal>
ipojo-bundle
</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
|
在dependencies中有服務接口的依賴,由於HelloClient組件中依賴了這個接口Hello類。pom.xml配置完成後,便可執行下面的命令對服務消費者進行打包:
mvn clean install
若是打包成功,在target目錄下回看一個jar包,這個jar包既能夠當作普通的jar包,也能夠當作bundle包,由於它與普通jar包惟一的不一樣就是MENIFEST.MF文件中多了一些OSGI的描述信息。
要運行這個實例,首先須要啓動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),那麼服務消費者又會變得可用。這兩個步驟的效果以下:
felix ipojo配置和spring的配置的目的差很少,都是爲了解耦和簡化依賴注入。可是felix ipojo比spring的配置要複雜一些,由於spring的bean都是建立後就不怎麼變了,並且felix ipojo的instance能夠隨組件的任意時候的安裝而產生,又隨組件的任意時候卸載而悄悄的消失,因此ipojo的配置除了基本的spring功能外,主要是爲了很好的解決組件中service來去匆匆的問題。
進入正題,felix ipojo配置主要是圍繞着類、component和instance三個元素。如下是我對三者的理解:
在使用felix ipojo以前首先須要在maven的pom.xml裏面添加ipojo插件配置:maven-ipojo-plugin使用指南,而後在ipojo配置文件目錄或者metadata.xml文件中添加component和instance配置。