felix ipojo入門教程

felix ipojo入門教程


Felix iPOJO介紹

在OSGI框架中,Felix iPOJO意在簡化面向服務編程。iPOJO的全稱是inject POJO。Felix iPOJO提供了一種新的開發OSGI服務組件的方式,主要目標是簡化服務組件的實現,使環境的動態變化對服務組件的實現透明。Felix iPOJO可讓開發者更好的分離功能代碼(好比:POJO)和非功能代碼(好比:依賴管理、服務的提供、配置等)。 html

Felix iPOJO服務組件簡介

服務組件能夠提供和(或者)依賴一個或者多個服務,服務(service)是一個實現了某個java接口的對象。另外Felix iPOJO提供了一個回調的機制,把各類狀態變化通知給組件。 java

組件是Felix iPOJO的核心概念,一個組件描述了服務的依賴、提供哪些服務以及哪些回調功能;這些信息配置在組件描述中(metadata)。Felix iPOJO中的第二個重要的概念是組件的實例,一個組件實例是一個特殊版本的組件。經過合併組件描述和組件實例的配置,Felix iPOJO能夠在運行時管理組件。好比:管理其生命週期、服務的依賴注入、開放服務、發現須要的服務。 spring

Felix iPOJO Hello World(簡單的示例)

下面的示例將闡述怎樣使用Felix iPOJO的核心功能。示例中包含兩個組件,一個提供Hello服務,一個須要任意數量的Hello服務。這些組件經過maven打包,被放在三個不一樣的bundle中。 apache

  • 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 框架

1
2
3
4
5
6
7
8
9
public interface Hello {
 
     /**
     * Returns a message like: "Hello $user_name".
     * @param name the name
     * @return the hello message
     */
     String sayHello ( String name ) ;
}

在工程目錄中,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

3. 第二個工程:服務提供者(hello.impl)

這個maven工程(hello.impl)裏面有一個實現了Hello接口的類,這個工程依賴了第一個工程hello.service,這個類路徑爲:src/main/java/ipojo/example/hello/impl/HelloImpl.java 函數

1
2
3
4
5
6
7
8
9
10
public class HelloImpl implements Hello {
 
     /**
     * Returns an 'Hello' message.
     * @param name : name
     * @return Hello message
     * @see ipojo.example.hello.Hello#sayHello(java.lang.String)
     */
     public String sayHello ( String name ) { return "hello " + name ;    }
}

爲了管理這個組件,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命令打包。

4. 第三個工程:服務消費者(hello.client)

這個工程中有一個類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的描述信息。

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

相關文章
相關標籤/搜索