laineysgl  
          日歷
          <2015年6月>
          31123456
          78910111213
          14151617181920
          21222324252627
          2829301234
          567891011
          統計
          • 隨筆 - 0
          • 文章 - 2
          • 評論 - 1
          • 引用 - 0

          導航

          留言簿

          文章檔案

          搜索

          •  

          最新評論

           

          OSGI(Open Services Gateway Initiative),或者通俗點說JAVA動態模塊系統,定義了一套模塊應用開發的框架。OSGI容器實現方案如Knopflerfish, Equinox, and Apache Felix允許你把你的應用分成多個功能模塊,這樣通過依賴管理這些功能會更加方便。

          和Servlet和EJB規范類似,OSGI規范包含兩大塊:一個OSGI容器需要實現的服務集合;一種OSGI容器和應用之間通信的機制。開發OSGI平臺意味著你需要使用OSGI API編寫你的應用,然后將其部署到OSGI容器中。從開發者的視角來看,OSGI提供以下優勢:

           

          1. 你可以動態地安裝、卸載、啟動、停止不同的應用模塊,而不需要重啟容器。
          2. 你的應用可以在同一時刻跑多個同一個模塊的實例。
          3. OSGI在SOA領域提供成熟的解決方案,包括嵌入式,移動設備和富客戶端應用等。

           

          OK,你已經有個Servlet容器來做web 應用,有了EJB容器來做事務處理,你可能在想為什么你還需要一個新的容器?簡單點說,OSGI容器被設計專門用來開發可分解為功能模塊的復雜的Java應用。

           

          企業應用領域的OSGI

           

          OSGI規范最初是由OSGI聯盟在1999年3月發起。它的主要目的是成為向網絡設備傳輸服務管理的開放規范。核心思想是一旦你向網絡設備中添加了一個OSGI服務平臺,你可以在網絡中的任意位置管理該設備上的服務組件。這些服務組件可以任意安裝,更新或移除而不會對設備產生影響。

          多年來,OSGI技術只出現在嵌入式系統和網絡設備市場。現在,Eclipse使OSGI在企業開發領域煥發出新的光彩。

           

          OSGI受到越來越廣泛的支持

           

          2003年,Eclipse開發團隊開始尋找一種使eclipse成為一種功能更動態、工具更模塊化的富客戶端平臺。最終,他們的目光鎖定在OSGI框架上。Eclipse3.0,2004年6月發布,是基于OSGI技術搭建的首個Eclipse版本。

           

          幾乎所有企業應用服務提供商支持或計劃支持OSGI。Spring框架同樣支持OSGI,通過Spring DM(Spring Dynamic Modules for OSGI Service Platforms)項目,可以讓我們在Spring上更方便的應用OSGI。

           

           

          開源OSGI容器

           

          從企業應用開發者的角度看,OSGI容器侵入性非常小,你可以方便地將其嵌入一個企業應用。舉個例子來說,假設你在開發一個復雜的web應用。你希望將這個應用分解成多個功能模塊。一個View層模塊,一個Model層模塊,一個DAO模塊。使用嵌入式OSGI容器來跨依賴地管理這些模塊可以讓你隨時更新你的DAO模塊卻不需要重啟你的服務器。

          只要你的應用完全符合OSGI規范,它就可以在所有符合OSGI規范的容器內運行。現在,有三種流行的開源OSGI容器:

           

          1. Equinox是OSGI Service Platform Release 4的一個實現。是Eclipse 模塊化運行時的核心。
          2. Knopflerfish另一個選擇。
          3. Apache Felix是Apache軟件基金會贊助的一個OSGI容器

           

          在這篇文章里我們使用Equinox作為我們的OSGI容器。

           

           

          嘗試開發一個Hello World bundle

           

          在OSGI的領域,發布的軟件是以bundle的形式出現。bundle由java class類和資源文件組成,向設備所有者提供功能,同時可以為其他的bundles提供服務。Eclipse對開發bundles提供了強大的支持。Eclipse不僅僅提供創建bundles的功能,它還集成了Equinox這個OSGI容器,你可以在其上開發和調試OSGI組件。其實所有的Eclipse插件都是使用Eclipse規范代碼寫的OSGI bundle。接下來,你將可以學到如何使用Eclipse IDE開發一個Hello world osgi bundle。

           

          開始開發bundle

           

          我們一步步的開始:

           

           

          1. 啟動Eclipse,依次點 File --> New --> Project。
          2. 選擇Plug-in Project,next。
          3. 輸入Project Name項目名稱,比如com.howard.sample.HelloWorld,Target Platform(目標平臺)里的an OSGI framework,選擇standard。
          4. 剩下的保持默認,next。
          5. 下個對話框也默認,next。
          6. 然后選擇Hello OSGI Bundle作為模版。Finish。

          Eclipse會飛快的為你創建Hello world bundle的模版代碼。主要包含兩個文件:Activator.java和MANIFEST.MF。

           

          Activator.java的代碼如下所示:

           

          Java代碼  收藏代碼
          1. import org.osgi.framework.BundleActivator;  
          2. import org.osgi.framework.BundleContext;  
          3. public class Activator implements BundleActivator {  
          4.     public void start(BundleContext context) throws Exception {  
          5.         System.out.println("Hello world");  
          6.     }  
          7.     public void stop(BundleContext context) throws Exception {  
          8.         System.out.println("Goodbye World");  
          9.     }  
          10. }  
           

          如果你的bundle在啟動和關閉的時候需要被通知,你可以考慮實現BundleActivator接口。以下是定義Activator的一些注意點:

           

          1. 你的Activator類需要一個公有的無參數構造函數。OSGI框架會通過類反射的方式來實例化一個Activator類。
          2. 容器啟動bundle過程中負責調用你的Activator類的start方法。bundle可以在此初始化資源比如說初始化數據庫連接。start方法需要一個參數,BundleContext對象。這個對象允許bundles以取得OSGI容器相關信息的方式和框架交互。如果某一個bundle有異常拋出,容器將對該bundle標記為stopped并不將其納入service列表。
          3. 容器關閉的時候會調用你的Activator類方法stop(),你可以利用這個機會做一些清理的操作。

           

          MANIFEST.MF

           

          這個文件是你的bundle的部署描述文件。格式和Jar里的MANIFEST.MF是一樣的。包含的不少名值對,就像如下:

           

          Xml代碼  收藏代碼
          1. Manifest-Version: 1.0  
          2. Bundle-ManifestVersion: 2  
          3. Bundle-Name: HelloWorld Plug-in  
          4. Bundle-SymbolicName: com.howard.sample.HelloWorld  
          5. Bundle-Version: 1.0.0  
          6. Bundle-Activator: com.howard.sample.helloworld.Activator  
          7. Bundle-Vendor: HOWARD  
          8. Bundle-RequiredExecutionEnvironment: JavaSE-1.6  
          9. Import-Package: org.osgi.framework;version="1.3.0"  
           

          分別來看下:

           

          Bundle-ManifestVersion

           數值為2意味著本bundle支持OSGI規范第四版;如果是1那就是支持OSGI規范第三版。

          Bundle-Name

           給bundle定義一個短名,方便人員閱讀

          Bundle-SymbolicName

           給bundle定義一個唯一的非局部名。方便分辨。

          Bundle-Activator

           聲明在start和stop事件發生時會被通知的監聽類的名字。

          Import-Package

           定義bundle的導入包。

          Hello World bundle完成了,接下來我們運行一下。

           

          執行bundle

           

           

          1. 點擊Run --> Run Configuration
          2. 在左邊的OSGI Framework選項里右鍵 new ,創建一個新的OSGI Run Configuration
          3. 名字隨便取好了,我們取個OSGi hello world。
          4. 你會注意到中間的窗口里Workspace項目里有一子項 com.howard.sample.HelloWorld,將其勾選上,其他的不用管。這時的狀態應該如下圖。
          5. 點擊Run按鈕。在控制臺你應該可以看見點東西了。那是叫做OSGI控制臺的東東。與子相伴,還有一個"Hello world"。


          OSGI控制臺

           

          OSGI控制臺是一個OSGI容器的命令行界面。你可以利用它做些諸如啟動,關閉,安裝bundles,更新和刪除bundles等操作。現在,點擊OSGI控制臺所在的位置,回車,你就會發現可以輸入命令了。這時的OSGI控制臺應該如下圖:


          下面列出一些常用的OSGI命令,你可以試著和OSGI容器交互。

          ss 顯示已安裝的bundles的狀態信息,信息包括bundle ID,短名,狀態等等。

          start 啟動一個bundle

          stop  關閉一個bundle

          update  載入一個新的JAR文件更新一個bundle

          install  安裝一個新的bundle到容器中

          uninstall  卸載一個已在容器中的bundle

           

           

          依賴管理

           

          OSGI規范允許你把你的應用分解成多個模塊然后管理各個模塊間的依賴關系。

          這需要通過bundle scope來完成。默認情況下,一個bundle內的class對其他bundle來說是不可見的。那么,如果要讓一個bundle訪問另一個bundle里的class要怎么做?解決的方案就是從源bundle導出包,然后在目標bundle里導入。

          接下來我們對此做一個例子。

           

          首先,我們需要先創建一個com.howard.sample.HelloService bundle,我們將通過它導出一個包。

          然后,我們在com.howard.sample.HelloWorld 這個bundle里導入包。

           

           

          導出包

           

          1、創建名為com.howard.sample.HelloService的bundle,創建步驟和前面一樣。

          2、在這個bundle內,添加一個com.howard.sample.service.HelloService.java 接口,代碼如下:

           

          Java代碼  收藏代碼
          1. public interface HelloService {  
          2.     public String sayHello();  
          3. }  
           

          3、創建一個com.howard.sample.service.impl.HelloServiceImpl.java類實現剛才的接口:

           

          Java代碼  收藏代碼
          1. public class HelloServiceImpl implements HelloService{  
          2.     public String sayHello() {  
          3.         System.out.println("Inside HelloServiceImple.sayHello()");  
          4.         return "Say Hello";  
          5.     }  
          6. }  
           

          4、打開MANIFEST.MF,選擇Runtime標簽項,在Exported Packages選項欄,點擊Add并且選擇com.howard.sample.service這個包。然后MANIFEST.MF的代碼應該如下:

           

          Xml代碼  收藏代碼
          1. Manifest-Version: 1.0  
          2. Bundle-ManifestVersion: 2  
          3. Bundle-Name: HelloService Plug-in  
          4. Bundle-SymbolicName: com.howard.sample.HelloService  
          5. Bundle-Version: 1.0.0  
          6. Bundle-Activator: com.howard.sample.helloservice.Activator  
          7. Bundle-Vendor: HOWARD  
          8. Bundle-RequiredExecutionEnvironment: JavaSE-1.6  
          9. Import-Package: org.osgi.framework;version="1.3.0"  
          10. Export-Package: com.howard.sample.service  
           

           

          你可以看到,MANIFEST.MF文件和剛才的HelloWorld的那份很類似。唯一的區別就是這個多了Export-Package這個標記,對應的值就是我們剛才選擇的com.howard.sample.service。

          Export-Package標記告訴OSGI容器在com.howard.sample.service包內的classes可以被外部訪問。

          注意,我們僅僅暴露了HelloService接口,而不是直接暴露HelloServiceImpl實現。

           

          導入包

           

          接下來我們要更新原來的HelloWorld bundle以導入com.howard.sample.service包。步驟如下:

           

          1、進入HelloWorld bundle,打開MANIFEST.MF,進入Dependencies標簽頁,在Imported Packages里添加com.howard.sample.service。MANIFEST.MF文件應該如下所示:

           

          Xml代碼  收藏代碼
          1. Manifest-Version: 1.0  
          2. Bundle-ManifestVersion: 2  
          3. Bundle-Name: HelloWorld Plug-in  
          4. Bundle-SymbolicName: com.howard.sample.HelloWorld  
          5. Bundle-Version: 1.0.0  
          6. Bundle-Activator: com.howard.sample.helloworld.Activator  
          7. Bundle-Vendor: HOWARD  
          8. Bundle-RequiredExecutionEnvironment: JavaSE-1.6  
          9. Import-Package: com.howard.sample.service,  
          10.  org.osgi.framework;version="1.3.0"  
           

          沒錯,Import-package標記的值也就是導入的包名之間是用逗號隔開的。在這里導入了兩個包om.howard.sample.service和org.osgi.framework。后者是使用Activator類時必須導入的包。

           

          2、接下來,打開HelloWorld項目下的Activator.java文件,這時候你會發現可以使用HelloService這個接口了。但還是不能使用HelloServiceImpl實現類。Eclipse會告訴你:Access restriction(立入禁止)。

           

          Class級別可見域

           

          為什么OSGI容器可以做到讓jar包中的一些classes可見而另一些又不可見呢。

          答案其實就是OSGI容器自定義了java class loader來有選擇的加載類。OSGI容器為每一個bundle都創建了不同的class loader。因此,bundle可以訪問的classes包括

           

          • Boot classpath:所有的java基礎類。
          • Framework classpath:OSGI框架級別的classloader加載的類
          • Bundle classpath:Bundle本身引用的關系緊密的JAR的路徑
          • Imported packages:就是在MANIFEST.MF里聲明的導入包,一旦聲明,在bundle內就可見了。

           

          bundle級別的可見域允許你可以隨時放心的更改HelloServiceImpl實現類而不需要去擔心依賴關系會被破壞。

           

           

          OSGI服務

           

          OSGI框架是實現SOA的絕佳土壤。通過它可以實現bundles暴露服務接口給其他bundles消費而不需要讓細節暴露。消費bundles甚至可以完全不知道提供服務的bundles。憑著可以良好的隱藏具體實現的能力,OSGI當之無愧是SOA的一種較完美的實現方案。

           

          OSGI中,提供服務的bundle在OSGI容器上將一個POJO注冊成一個service。消費者bundle請求OSGI容器中基于某個特殊接口的注冊service。一旦找到,消費者bundle就會綁定它,然后就可以調用service中的方法了。舉個例子會更容易說明。

           

           

          導出services

           

          1、確保com.howard.sample.HelloService里的MANIFEST.MF導入org.osgi.framework包

          2、創建com.howard.sample.service.impl.HelloServiceActivator.java,代碼如下:

           

          Java代碼  收藏代碼
          1. public class HelloServiceActivator implements BundleActivator {  
          2.     ServiceRegistration helloServiceRegistration;  
          3.     @Override  
          4.     public void start(BundleContext context) throws Exception {  
          5.         HelloService helloService = new HelloServiceImpl();  
          6.         helloServiceRegistration = context.registerService(HelloService.class  
          7.                 .getName(), helloService, null);  
          8.     }  
          9.   
          10.     @Override  
          11.     public void stop(BundleContext context) throws Exception {  
          12.         helloServiceRegistration.unregister();  
          13.     }  
          14.   
          15. }  
           

           

          OK,我們就是用BundleContext的registerService方法注冊service的。這個方法需要三個參數。

           

          • service的接口名。如果service實現了多個接口,那樣你需要傳入一個包含所有接口名的String數組。在這里我們傳入的是HelloService這個接口。
          • 真正的service實現。在例子中我們傳了一個HelloServiceImpl實現。
          • service屬性。這個參數可以在有多個service實現同一個接口的情況下,消費者用來區分真正感興趣的service。

          3、最后一步就是修改HelloService的MANIFEST.MF文件,將Bundle-Activator改成com.howard.sample.service.impl.HelloServiceActivator

           

          現在HelloService bundle已經隨時準備將HelloServiceImpl服務發布了。OSGI容器啟動HelloServie bundle的時候會讓HelloServiceActivator運作,在那個時候將HelloServiceImpl注冊到容器中,接下來就是創建消費者的問題了。

           

          導入service

           

          我們的消費者就是HelloWorld bundle,主要修改的就是其中的Activator.java,修改代碼如下:

           

          Java代碼  收藏代碼
          1. public class Activator implements BundleActivator {  
          2. ServiceReference helloServiceReference;  
          3.     public void start(BundleContext context) throws Exception {  
          4.         System.out.println("Hello World!!");  
          5.         helloServiceReference=context.getServiceReference(HelloService.class.getName());  
          6.         HelloService helloService=(HelloService)context.getService(helloServiceReference);  
          7.         System.out.println(helloService.sayHello());  
          8.     }  
          9.     public void stop(BundleContext context) throws Exception {  
          10.         System.out.println("Goodbye World!!");  
          11.         context.ungetService(helloServiceReference);  
          12.     }  
          13. }  
           

          代碼很簡單,就不多說了。

           

          在運行之前我們在Run-->Run Configurations對話框里,把HelloWorld和HelloService這兩個bundle前面的鉤都打上。然后運行時你會發現HelloServiceImpl.sayHello()方法已經被調用了。

           

          在OSGI控制臺輸入ss并回車,所有容器內的bundle狀態一目了然。其中id為0的bundle是OSGI框架基礎bundle,另兩個就是HelloService和HelloWorld了,它倆的id是隨機的,狀態是ACTIVE也就是已啟動狀態。假設HelloService的id為7,HelloWorld為8。

           

          輸入stop 8就可以暫停bundle的運行,容器內這個bundle還是存在的,只是狀態變成了RESOLVED。再次啟動使用start 8,然后就會看見HelloWorld bundle消費了HelloService的服務。

           

           

          創建服務工廠

           

          剛才例子所示,我們會在HelloService bundle啟動時初始化并注冊service。然后不管存不存在消費端,這個service都會存在,而且消費端取得的service 實例其實都是同一個。OK,某些servie是比較耗費資源的主,我們不希望它一直占用資源,最好是在真正用它的時候創建不用的時候銷毀就最好了。

           

          解決如上問題的方案就是使用ServiceFactory接口的實現來代替原先service具體的實現到OSGI容器去注冊。這樣,以后只有當其他bundle請求該服務時,才會由ServiceFactory實現類來處理請求并返回一個新的service實例。

           

          實例步驟如下:

          1、在HelloService bundle創建一個實現ServiceFactory接口的類HelloServiceFactory類,代碼如下:

           

          Java代碼  收藏代碼
          1. public class HelloServiceFactory implements ServiceFactory {  
          2.     private int usageCounter = 0;  
          3.     @Override  
          4.     public Object getService(Bundle bundle, ServiceRegistration registration) {  
          5.         System.out.println("Create object of HelloService for " + bundle.getSymbolicName());  
          6.         usageCounter++;  
          7.         System.out.println("Number of bundles using service " + usageCounter);  
          8.         HelloService helloService = new HelloServiceImpl();  
          9.         return helloService;  
          10.     }  
          11.     @Override  
          12.     public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) {  
          13.         System.out.println("Release object of HelloService for " + bundle.getSymbolicName());  
          14.         usageCounter--;  
          15.         System.out.println("Number of bundles using service " + usageCounter);  
          16.     }  
          17. }  
           

           

          ServiceFactory接口定義了兩個方法:

           

          • getService方法:特定的bundle在第一次調用BundleContext的getService方法時由OSGI框架調用,在實例代碼中,我們用這個方法來返回一個新的HelloService的實現。OSGI框架會緩存這個返回的對象,如果同一個bundle在未來再次調用BundleContext的getService方法的話,會直接返回這個緩存中的對象。
          • ungetService方法:bundle釋放service的時候由OSGI容器調用。

          2、修改HelloServiceActivator.java的start方法,將ServiceFactory作為服務注冊,代碼如下:

           

          Java代碼  收藏代碼
          1. public class HelloServiceActivator implements BundleActivator {  
          2.     ServiceRegistration helloServiceRegistration;  
          3.     @Override  
          4.     public void start(BundleContext context) throws Exception {  
          5.         HelloServiceFactory helloServiceFactory = new HelloServiceFactory();  
          6.         helloServiceRegistration = context.registerService(HelloService.class  
          7.                 .getName(), helloServiceFactory, null);  
          8.     }  
          9.   
          10.     @Override  
          11.     public void stop(BundleContext context) throws Exception {  
          12.         helloServiceRegistration.unregister();  
          13.     }  
          14. }  
           

           

          現在運行下試試看,你會發現HelloWorld bundle啟動時才會初始化HelloService,控制臺會打印出"Number of bundles using service 1",當HelloWorld bundle暫停時會打印出"Number of bundles using service 0"。

           

           

          services跟蹤

           

          某種情形下,我們可能需要在某個特殊的接口有新的服務注冊或取消注冊時通知消費端。這時我們可以使用ServiceTracker類。如下步驟所示:

          1、在HelloWorld bundle里的MANIFEST.MF導入org.util.tracker包。

          2、創建HelloServiceTracker類,代碼如下:

           

          Java代碼  收藏代碼
          1. public class HelloServiceTracker extends ServiceTracker {  
          2.     public HelloServiceTracker(BundleContext context) {  
          3.         super(context, HelloService.class.getName(),null);  
          4.     }  
          5.     public Object addingService(ServiceReference reference) {  
          6.         System.out.println("Inside HelloServiceTracker.addingService " + reference.getBundle());  
          7.         return super.addingService(reference);  
          8.     }  
          9.     public void removedService(ServiceReference reference, Object service) {  
          10.         System.out.println("Inside HelloServiceTracker.removedService " + reference.getBundle());  
          11.         super.removedService(reference, service);  
          12.     }  
          13. }  
           

          我們在HelloServiceTracker的構造函數里將HelloService接口名傳進去,ServiceTracker會跟蹤實現這個接口的所有的注冊services。ServiceTracker主要有兩個重要方法:

           

          • addingService方法:bundle注冊一個基于給定接口的service時調用。
          • removeService方法:bundle取消注冊一個基于給定接口的service時調用。

           

          3、修改Activator類,使用剛剛創建的HelloServiceTracker來獲取service:

           

          Java代碼  收藏代碼
          1. public class Activator implements BundleActivator {  
          2.     HelloServiceTracker helloServiceTracker;  
          3.     public void start(BundleContext context) throws Exception {  
          4.         System.out.println("Hello World!!");  
          5.         helloServiceTracker= new HelloServiceTracker(context);  
          6.         helloServiceTracker.open();  
          7.         HelloService helloService=(HelloService)helloServiceTracker.getService();  
          8.         System.out.println(helloService.sayHello());  
          9.     }  
          10.       
          11.     public void stop(BundleContext context) throws Exception {  
          12.         System.out.println("Goodbye World!!");  
          13.         helloServiceTracker.close();  
          14.     }  
          15. }  
           

          現在運行一下,可以發現只要HelloService bundle啟動或是暫停都會導致HelloServiceTracker的對addingService或removedService方法的調用。

           

          ServiceTracker不僅僅能跟蹤Service的動向,它還能通過getService方法取得Service實例并返回。但是如果同一個接口下有多個service注冊,這時返回哪個service呢?這時候就需要看service的等級哪個高了。這個等級是service注冊時的property屬性里的一項:SERVICE_RANKING。誰的SERVICE_RANKING高,就返回誰。

          如果有兩個一樣高的呢?這時再看這兩個service誰的PID更低了。

           

          如果對OSGI的ClassLoader機制有疑問,可以看看這篇解釋ClassLoader機制和自定義ClassLoader相關的文章:

          http://longdick.iteye.com/blog/442213

           

          OSGI的基本原理和入門開發就到這里了。希望同學們能對OSGI開發有個簡單而清晰的認識。

          posted on 2012-09-25 17:11 lainey 閱讀(269) 評論(1)  編輯  收藏
          評論:
          • # re: 實例講解OSGI開發[未登錄]  John Posted @ 2015-06-29 19:38
            JxADF(http://osgi.jxtech.net)是一個基于B/S的企業級快速插件開發平臺,主要功能、特點如下:
            ? 基于J2EE技術。
            ? 從需求、設計到開發、實施、上線、運維提供一套完整的方法論。
            ? 移動設備、桌面設備自適應UI風格,真正做到開發一次,隨處運行。
            ? 插件式開發,支持插件的熱部署,可做到應用7X24小時不間斷運行、維護。
            ? 表單(單表、主從表)增、刪、查、改的快速開發,開發人員只需要關注業務邏輯,零JS、零CSS,大大地提升了開發效率、降低開發成本。
            ? 插件的安裝、配置、啟用、停用等都是一鍵式完成,大大地提升了實施效率。
            ? 插件可以支持符合健新快速開發平臺的插件、也支持標準的OSGi插件。
            ? 健新插件市場,提供豐富的行業業務插件,平臺基礎插件,使用戶真正做到按需選擇、開箱即用。
            ? 基礎組件:Layout、appbar、pushbutton、textbox、lable、section、table、tree、treetable、panel、checkbox、code、link、form等50多個組件,目前還在不斷新增中。
            ? 基礎功能:國際化、皮膚、Dashboard、組織、用戶、權限管理、單點登錄等功能。
            ? 通用插件:插件管理、Oracle OSB插件、Oracle BPM插件、健新工作流插件、任務調度插件、郵件發送插件、消息服務插件、LDAP插件、日程管理、BIRT報表插件等。
              回復  更多評論   


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
           
          Copyright © lainey Powered by: 博客園 模板提供:滬江博客
          主站蜘蛛池模板: 西安市| 紫阳县| 仁寿县| 罗甸县| 乌拉特前旗| 青州市| 名山县| 仲巴县| 西林县| 鲁山县| 镇巴县| 绥德县| 巴东县| 包头市| 贞丰县| 榆树市| 敦煌市| 阜南县| 抚宁县| 洪泽县| 罗平县| 来安县| 六枝特区| 东宁县| 益阳市| 东至县| 枣强县| 黄石市| 泸溪县| 太白县| 岐山县| 斗六市| 札达县| 左贡县| 常宁市| 溧阳市| 宜阳县| 宁安市| 舟曲县| 广丰县| 长春市|