OSGi模块化框架是很早就出来的一个插件化框架,最早Eclipse用它而出名,但这些年也没有大热虽然OSGi已经发布了版本1到版本5。现在用的最多的,也是本文讲述基于的是Equinox的OSGi实现,同时也是Eclipse核心,Spring Dynamic Module也是基于Equinox。OSGi框架为java系统提供了一个通用的容器,该系统中的 bundle,无需停止系统,即可实现 bundle 的安装、卸载。OSGi是Java中目前唯一的一个模块化、动态化的规范。在模块化方面OSGi联盟已经研究了很多年了,因此OSGi规范对于模块的物理隔离、模块的交互、多版本这些方面都有了非常完善的机制,并且也得到了现在几乎所有的App Server厂商或开源社区的认可,但至今没有被JAVA纳入语言级(有待观察)。OSGi的突出特点有:
OSGi运行在JVM之上,其架构图如下图所示:
很多人错误的使用了OSGi, 套用了OSGi架构把系统复杂化。在我看来,OSGi的用处在于“模块化”和“热插拔”。模块化包括模块化、版本化和面向服务的设计。热插拔也就是说模块/bundle的热插拔,它可以实现更新和升级模块/bundle(即系统的一部分)而无需重启整个系统。
如果你的系统套用了OSGi架构,bundle的相互依赖关系复杂,又没有bundle动态加载、动态更新、动态卸载和动态监听的机制,都是静态启动所有bundle,那就是为了OSGi架构而OSGi架构,把问题复杂化了。其代价也是很大的,因为原来你的jar包用maven来处理依赖关系和自动更新也很方便,而由于整个系统建立在OSGi规范上,你的应用所依赖的其他组件也“不得不”迁移到OSGI上来,再加上OSGI独特的ClassLoader设计,使bundle间的类互相访问受到一定的约束,一切都需要迁移到OSGi的约束上来。
举个例子来说,就像Eclipse提供了动态加载、更新和删除插件的机制,因为它里面有一个插件注册和反注册的接口和插件加载、更新和删除的监听线程,这样允许你动态加载、更新和删除Eclipse插件而无需重启Eclipse。当然,如果你当前进程调用了某插件,比如js语法高亮,而某插件更新了,那么当前的js实例还是需要重新打开的。但整个Eclispe无需重启。
OSGi的一个重要特性就是模块化,OSGi提供了一套模块化的体系,这其中则会有明确的模块之间接口暴露以及依赖的定义,因此能够更好的实现高内聚和低耦合。那么,Java模块化难点在哪?模块的实现和传统的编程方法确实有一些差别,主要体现在模块之间类访问的隔离、版本选择这两个方面。如希望更好的设计模块化的系统,开发者需要掌握ClassLoader机制、模块之间类的交互方法(这包括了模块怎么样对外提供可访问的package、怎么样访问其他模块提供的package、如何选择适合版本的package等)。如果不懂以上这些,贸然套用OSGi框架会误入歧途。
Bundle — A bundle is a JAR file with special OSGi entries in its manifest and containing classes, resources, and other JARs。Bundle,可以将其理解为自描述的 JAR 文件。Bundle在OSGi中是部署的最小单位,因此,可以把它理解为模块。在 bundle 的 manifest 文件中,会有对本 bundle 的标识、提供的功能 (Export-package) 及依赖性 (Import-Package/Require-Bundle) 的定义。每个 bundle 在运行时自己的类加载器 (Class Loader),这样可以做到一方面把不同的 bundle 里面的类区别开来,当 bundle 被卸载时,只有这个 bundle 的类加载器中的信息会丢失;另一方面,可以在自己的 bundle 内部充分利用 Java 的成员访问控制机制。
Bundle通过MANIFEST.MF进行自描述,下面是一个例子:
1
2
3
4
5
6
7
8
9
10 |
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Popup Plug-in Bundle-SymbolicName: com.example.myosgi; singleton:= true Bundle-Version: 1.0 . 0 Bundle-Activator: com.example.myosgi.Activator Require-Bundle: org.eclipse.ui, org.eclipse.core.runtime Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE- 1.6 |
每个Bundle均为独立的ClassLoader,是java动态化实现的基础。默认情况下有Boostrap classLoader (jre/lib/classes)、Extension classloader (jre/lib/ext)、 System classloader (classpath指定),应用可以自行实现classloader及动态的加载类,或加载特定目录下的类。
Lifecycle — A lifecycle is the sequence of states a bundle goes through: uninstalled, installed, resolved, starting, stopping, active. 生命周期图如下所示:
要注意的是:bundle状态变为Resolved并不表示能提供服务,所以启动所有的bundle不表示类都已经加载到内存了。Resolve bundle做以下的几件事情:寻找bundle依赖的包是否存在以及被resolve,寻找匹配的import package,required bundle,如寻找则进入检查,检查没有冲突就形成绑定关系,以便加载类的时候能直接加载(但仅仅Resolved,不代表类被加载了)。如果你的BundleActivationPolicy是LAZY惰性加载,bundle.loadClass()调用才会到达Active状态。如果你的bundle的MANIFEST.MF中配置的Bundle-activator存在,那就调用其start方法,从starting进入active状态。
1
2
3
4
5 |
osgi> ss "Framework is launched." id State Bundle 15
STARTING com.example.serviceconsumer_1. 0.0 .X 16
RESOLVED com.example.serviceprovider_1. 0.0 .X |
下面的图更详细的解释了这一点:
Service — A service is an object instance exposed under the one or more interfaces that it implements and a map of properties. 简单来说,Service model允许每个bundle对外分享一组服务,其它的bundle都可以调用这些接口的服务。这也就是OSGi bundle之间调用的方式。Service可以用来:
实际做法来看,通常会把接口和实现分开。接口放到一个bundle里面。实现(service)放到另外一个bundle里面,类似下面的图示中,bundle A和B是Service,其interface放到Bundle C:
也可以是提供一个jar包,里面定义了扩展接口,然后规定新的扩展bundle必须实现该jar包里面定义的interface。实现示意图如下所示(OsgiCommand接口定义在扩展点jar包里面,新的bundle必须包含):
Bundle的Service之间交换方式和注册方式:
第二种通过ServiceTracker 来查询或侦听服务注册和注销的例子代码:
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84 |
package
com.ibm.osg.example.mygetservice; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.util.tracker.ServiceTracker; import com.ibm.osg.example.mtservice.MyTestService; public class MyBundleActivator implements
BundleActivator, Runnable { private
boolean done= false ; private
ServiceTracker testServiceTracker; // Bundle Activator Start Method public
void start(BundleContext context) { /* Here we initialize and open our ServiceTracker. It will track any service registering under the "com.ibm.osg.example.mtservice.MyTestService" interface. */ testServiceTracker = new
ServiceTracker(context, "com.ibm.osg.example.mtservice.MyTestService" , null ); testServiceTracker.open(); // Here we start a thread that will continue // to use our service until // the bundle is stopped. Thread t = new
Thread( this ); t.setName( "mygetservice thread" ); t.start(); } /*Bundle Activator Stop Method -- here we stop the thread and close the ServiceTracker*/ public
void stop(BundleContext context) { done= true ; testServiceTracker.close(); } //Here is a method that uses the service //we are tracking. First we get //the service //from the tracker, then we call its printMessage //method. public
void useService(String message){ MyTestService testService = (MyTestService) testServiceTracker.getService(); if ( testService != null
) { // If the service is available then use it. testService.printMessage(message); } else { // If the service is not available then perform an acceptable action. // Here we just print the message to standard out and indicate the service // was not available. System.out.println( "No MyTestService available - "
+ message); } } // Simply continues to use the test service // every second until the done flag is set. public
void run(){ int
i = 0 ; done = false ; while
(!done) { useService( "message from test "
+ i++); try { Thread.sleep( 1000 ); } catch ( InterruptedException ie ){ } } } } |
从Eclipse创建OSGi的bundle是非常简单的,简单起步可以参考这几篇文章:<Creating a new OSGi Bundle using Eclispe>,以前这篇。
Slideshare: OSGi理论与实战
具体实现:
就写到这把,具体的代码就不放上来了,公司的项目保密要求。原理上面都讲了,自己去研究吧。
原文:http://www.cnblogs.com/Mainz/p/3548396.html