OSGI模块化的真相

OSGI带来了模块化的讨论,原来我们认为架构是基于组件的架构,Modularity & Architecture(模块化和架构)一文谈论了基于模块化的架构,这不仅让我个人有些疑惑,组件不等于模块化?组件和模块化
是什么关系?是划分标准不一样还是粒度不一样。

文中首先谈论了众多的架构定义:
1.软件系统组织重要的结构元素
2.是一些组件和组件之间的关系
3.TOGAF定义:构件级别指导实施的计划,代表组件的结构
4.是一种体系结构中关键的设计决策,能够减轻以后因为变化带来的成本。

架构的目标是减少因为变化拓展带来的成本,实现这一目标是通过提高灵活性,同时驯服复杂性达到的。

面向对象设计中的开闭原则:禁止修改,鼓励拓展继承。我们可以通过不断的继承和抽象,但是带来的问题是复杂性,不断增多的之类和父子家族?如何组织管理他们?

文章提出一个系统是由模块和连接体joints组成的,连接体就是跨越模块之间的设计。变化被隔离封装在一个模块中,这样变化带来的影响就变得很小。也就是说,不要让变化跨越模块。虽然很难做到,但是这应该是架构设计的目标。

基于组件化的SOA好像是另外一种思路,将一定功能封装在组件中,组件以服务形式向外提供接口,通过对不同服务的多样化调用流程组合实现不同的业务。这也是一种开闭原则。

直觉告诉我:这两种开闭原则好像正好相反,一个是在模块中封装变化,让变化不要跨越模块;一个是在组件中封装不变性,通过跨越组件的不同调用应付变化。

我们可以从OSGI具体模块化实现来看,也验证了这样设计思路,在META-INF/MANIFEST.MF中需要指定当前包的依赖包,试想想:如果依赖包关系经常变化,那不是要经常更改META-INF/MANIFEST.MF,有几百个,不是改得头晕眼花,形成闭环依赖?头大啊。

所以,使用OSGI前提就是:依赖关系不经常变,变化的是bundle中的类,可以在运行时,动态插拔一个对象。

进一步设想:我们使用IOC/DI就是为解决类的依赖关系变化带来的麻烦,由IOC框架帮助我们自动解决依赖,而OSGI和IOC这种解决类依赖目的的模式正好相反哦。

看来:模块化的OSGI从另外一个架构思路为我们解决问题提供可选方案。当初OSGI诞生于微系统,因为微系统因为资源有限,不可能有太多组件或类,所以,包之间的依赖关系不复杂,可以看成是不变的,而将在微系统中运行的内容组件看成是变化的。

结论:OSGI模块化最大的特点是其开闭原则的独特性,OSGI优势不是在动态插拔,XML/类动态加载/AOP都可以在运行时动态插拔,我想后者是很多吹捧OSGi优点的一个误区。

OSGI这种独特的模块化设计思路为我们架构设计提供了一条新的选择。

参考:
The Two Faces of Modularity & OSGi
[该贴被banq于2009-09-17 17:31修改过]
[该贴被banq于2009-09-17 17:32修改过]

有时候我在想,在系统中需要多个模块,依赖关系特别复杂的情况下,OSGI怎么解决依赖关系?难道自己手动管理,累死,这岂不是又回到没有IOC的时代?所以正如banq老师所说的,OSGI它的立足在于:依赖关系相对稳定的情况下,动态插拔某个bundle或者说是更新某一个bundle内部的类,即使bundle被更新了,但是依赖关系还是固定的。所以我觉得OSGI这个东东的粒度非常的粗,不能以IOC细粒度的思想去看待这个粗粒度的东西。不过话说回来了,OSGI中的每个bundle内部可以是细粒度的,这样某一个bundle内部就可以通过IOC来管理,而bundle之间的依赖不能太复杂。

>依赖关系特别复杂的情况下,OSGI怎么解决依赖关系?
这实际就是OSGI开闭原则的特殊之处,如果把开闭原则看成优缺点的话,或者把闭合看成是前提条件的话,OSGI的缺点是不擅长处理依赖关系,优点可以承载任意组件或粒度的任意变化。

所以,依赖关系不是OSGI的擅长,而是IOC的擅长,所以,OSGI才能和IOC整合在一起。这两者结合在一起,才能形成真正灵活的架构。

OSGI实际就是一个强封装的套子,通过这个强封装,可以将变化限定在这个强封装中,不会跨越边界半步。

结合Evans DDD的聚合边界,我觉得用OSGI把核心模型和其边界装进去,这将是实现领域中边界强烈划分的好办法。

更多人看到了OSGI强封装,把它自然和服务器联系起来,因为一个Server也是一个强封装,服务器依赖的包都不会变化相对固定,这也是GlassFish引入OSGI实现J2EE Server的原因,包括Spring DM Server。

我觉得OSGI不和领域模型结合起来,就非常可惜,就象没用到刀刃上。

还有:OSGI的模块化组件和EJB的分布式组件有些不一样,他们两个都是输出jar包形式,不过对Jar的定义不一样,这两者之间能否共享共用,也是有趣的话题。
[该贴被banq于2009-09-19 12:35修改过]

还有一个问题就是OSGI动态插拔的问题。我觉得OSGI的这种替换也更加倾向于水平方向的互相独立的bundle的插拔,在垂直方向上,我们如何替换一个被别的bundle依赖的bundle。比如我们做了一个平台,这个平台上面有很多的独立的bundle,而又有很多bundle依赖于这个大的平台构建,这个时候我们平台的bundle怎么插拔?所以我觉得bundle的划分在一定的程度上应该从水平的角度来,一个系统分为相互独立,没有依赖关系的水平方向上的很多模块,每个模块是一个bundle,这些bundle可以进行插拔,但是在垂直方向上,bundle之间存在相互依赖关系,这样替换就比较麻烦。

不要从依赖去理解OSGI,说白了 这是它的软肋,所以,才有套子和强封装一说。

OSGI只是架构中的一个独特设计,但不是架构模块化的全部。

恩,多谢banq老师。OSGI我觉得也体现出了一种解耦的思想在里面。模块化程度越高,bundle耦合度就越,bundle复用性也就越高。

谈OSGI从两个方面入手,一个是从OSGI/bundle内部,这无疑是一种组合方式的封装;如果从OSGI/bundle外部来看,也就是bundle之间依赖关系,则就没有内部那么美妙了。

组合与模块化

infoQ最新一篇OSGI是什么文章:
Modular Java: What Is It?
[该贴被banq于2009-09-27 08:29修改过]

模块化远不止依赖问题那么简单。。。
1,替代传统的classpath。。。
2。解决冲突问题,如现有的方式两种不同版本的jar(假设api不兼容)很难共存,如spring 所依赖的cglib与hibernate依赖的cglib。在模块化环境中,每个模块都可以使用自己的classloader,不同版本的模块可以共存。
3. 可以决定要暴露哪些 API,不是像现在classpath那样所有的jar中的api都可以访问。
4。完整的生命周期。
...

最近写了一个 NetBeans 插件, http://hantsy.cublog.cn...感觉模块化标准之争,SUN 是有道理的,IBM 打着反清复明的口号,目的不得而知。
在 NetBeans 中,NetBeans Module System 的应用时间比 eclipse 中的 osgi(3.3 开始引入) 更长一些,更有说服力。