??xml version="1.0" encoding="utf-8" standalone="yes"?>
本文讲到的例子是仿照|上甚ؓ行的一个例子,但苦于一直未扑ֈ源码Q网上脓的都是一些{帖,代码片段Q估计初学者很隑ְ其还原ƈ调通!我最开始弄q个咚咚的时候,其过E之痛苦Q难以言喻,所以想着仿照该例子的设计Q给予实玎ͼ文后贴出源码Q希望能帮到大家?br />
该例子是一个关于计器的实例,osgi.example.compute bundle(下文Ucompute bundle)提供了统一的计接口:ComputeQ另外两个bundle分别为osgi.example.compute.add(下文Uadd bundle)和osgi.example.compute.multiply(下文Umultiply bundle)Q在q两个bundle中,各自对compute bundleq行不同的实玎ͼ一个实现加法,一个实C法。另外还有一个服务消费者osgi.example.compute.consumer bundle(下文Uconsumer bundle)Qconsumer bundle负责消费add bundle和multiply bundle提供的服务。上q?个bundle之间的关pd下图所C:
创徏4个bundle之后的工E目录如下图所C:
通过该示例,演C如何利用Spring DM发布和调用OSGi服务Q同时还演COSGi的动态服务调用能力?br />
1. bundle osgi.example.compute
compute bundle只提供一个接口——ComputeQ因此无需依赖更多的bundleQ只需最基本的osgi卛_。因Z涉及注册资源之类的,所以也无需Activator入口cR?br />
Computer接口源代码如下所C:
add bundle是对compute bundle的具体服务实玎ͼ在MANIFEST.MF文g需要引入osgi.example.compute包;当然也可以通过d依赖bundle的Ş式,即不引入包,而直接在Required Plug-ins中添加compute bundle。如下图所C:
注意Q?span style="color: #ff0000">OSGi官方指出Q当需要用到其?span style="color: #ff0000">bundle的类型时Q不提倡依?span style="color: #ff0000">bundleQ应该尽可能采用Import-package的方式引入包Q因Z?span style="color: #ff0000">bundle可能在加?span style="color: #ff0000">bundle的时候发生问题?/strong>
通过引入osgi.example.compute包,osgi.example.compute bundle被加Cadd bundl的classpath当中Q解决了开发时期的cd识别问题?br /> q样一来,在add bundle中就能用compute bundle中的接口了,Computer接口的实现如下:
Compute的实现已l实CQ那么如何将其发布出dQ这个是由Spring DM负责QSpring DM利用OSGi命名I间下的<service>元素bean导出为OSGi服务。最单的形式为:
从示例中可以看出QbeanToPublish被service元素声明导出?br /> 另外Qservicel点q有一些高U属性,如depends-on、context-class-loader、ranking{待Q详情请看spring dm reference?br /> 首先Q需要在add bundle的工E根目录下的”META-INF”的文件夹下创Z个文件夹Q取?#8221;spring”QSpring DM能够自动解析该文件夹下所有的spring配置文g。spring配置文g的具体内容如下所C:
该bundle和add bundle怼Q在q就不赘qC?br />
4. bundle osgi.example.compute.client
思义Q该bundle作为add 、multiply两个bundle的客户bundleQ演C如何导入服务?br /> OSGi的测试工作比较麻烦,q方面还没研IӞ在这里利用spring实例化bean的时期,从构造函数入手,Ҏ务进行测试。Clientcȝ实现很简单,如下所C:
另外Q因为client用到了其他几个bundle的类型,所以需要导入相应的包,步骤在上面已有讲到?br /> spring dm?lt;reference>元素来引入服务,最单的形式如下所C:
如果需要用到该服务Q如某个bean包含一个com.xyz.MessageService属性,则配|该bean如下所C:
需要勾选中spring bundle版(2.5.6Q,spring dm的几个核心包Qcore、extender、io再点validate bundles按钮Q校验是否已全部选中其依赖的bundle。然后即可点击运行了?br />
q行之后Q我们发现控制台输出l果Q?br />
The Sum is---11
通过ss命oQ如下:
5 ACTIVE osgi.example.compute.multiply_1.0.0
6 ACTIVE osgi.example.compute.add_1.0.0
7 ACTIVE osgi.example.compute.client_1.0.0
?停掉:stop 6
然后再refresh 7Q控制台输出如下l果Q?br />
The Multiply is---30
通过 ss 命oQ如下:
5 ACTIVE osgi.example.compute.multiply_1.0.0
6 RESOLVED osgi.example.compute.add_1.0.0
7 ACTIVE osgi.example.compute.client_1.0.0
现在multiply处于q行状态,而add已经被停止,所以client导入的服务实际是由multiply提供的?br />
6. ȝ
通过该文档,我们已经清楚了,如何使用Spring DM导出、导入服务。Spring DM的一些高U特性请查阅spring dm reference?br />
附gQ?a href="/Files/benniaolk/OSGi/Spring与OSGi的整?1.rar">osgi.example.compute.rar
现在的eclipse都已l包含了EquinoxQ无需单独下蝲?br /> 2. 开发OSGi的HelloWorld应用E序
在这一节,我们开发一个OSGi bundleQ演C如何利用Equinoxq行OSGi bundle的开发、运行及调试Qؓ之后的示例做准备?br />
首先Q新Z个Plug-in工程Q如下图所C:
下一步,注意选择目标q_Q默认ؓEclipse version *Q将其改成EquinoxQ如下图所C:
之后按默认下一步即可,到最后一步时Q无需Ҏ模板创徏工程Q去掉默认的勾,如下图:
点击完成Q创建的工程目录如下图所C:
在弹出的对话框中Q新Z个OSGi Frameworkq行环境Q双击OSGi Framework卛_Q这里ؓ其取名EquinoxQ,选中helloworld(1.0.0)Q然后点d边的Add Required Bundles按钮Qeclipse自动选中q行helloworld的依赖bundleQ如果有必要Q可以点d下的Validate Bundles验证按钮Q验证程序正常运行所需的bundle是否都被选中Q最后点击运行,回到控制収ͼ
osgi> Hello World!
可以看到Q之前在Activator的startҎ中的输出语句已经被输出到控制収ͼ我们可以通过命oss查看Equinox的运行情况,可以看到一p行了两个bundleQ如下:
ss
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.4.3.R34x_v20081215-1030
1 ACTIVE helloworld_1.0.0
q记得之前讲到过的bundle的几U状态吧Qhelloworld已经q行h了?br />
那么Equinox具体支持哪些命o呢?下表列出了主要的一些命令,如需查看更详l的命o清单Q则可以在控制台键入help?/p>
cd |
命o |
含义 |
控制框架 |
|
启动框架 |
|
停止框架 |
|
|
关闭、退出框?/p> |
|
|
立即退出,相当?System.exit |
|
|
卸蝲所?bundleQ前提是已经 shutdownQ?/p> |
|
|
讄属性,在运行时q行 |
|
控制 bundle |
|
安装 |
|
卸蝲 |
|
|
启动 |
|
|
停止 |
|
|
h |
|
|
更新 |
|
展示状?/strong> |
|
展示安装?bundle 和注册的服务 |
|
展示所?bundle 的简单状?/p> |
|
s |
展示注册服务的详l信?/p> |
|
|
展示导入、导出包的状?/p> |
|
|
展示所有已l安装的 bundles 的状?/p> |
|
|
展示 bundles 的头信息Q即 MANIFEST.MF 中的内容 |
|
|
展示 LOG 入口信息 |
|
其它 |
|
在另外一个进E中执行一个命令(d状态) |
|
?EXEC 不同的是不会引vd |
|
|
促垃圾回收 |
|
|
得到属性,或者某个属?/p> |
|
控制启动U别 |
|
得到某个 bundle 或者整个框架的 start level 信息 |
|
讄框架?start level |
|
|
讄 bundle ?start level |
|
|
讄初始?bundle ?start level |
xQ我们已l成功的演示了helloworldQ初步了解了OSGi的bundle是如何开发ƈq行的?br />
补充资料Q?br />
在Eclipse 里,当编辑器QEditorQ被ȀzLQ大U视图自动通过q个~辑器的getAdapter()ҎL它提供的大纲Q大U实现IcontentOutlinePage 接口Q。GEF 提供了ContentOutlinePage cȝ来实现大U视图,我们要做的就是实C个它的子c,q点实现createControl()Ҏ。ContentOutlinePage 是org.eclipse.ui.part.Page 的一个子c,大纲视图则是PageBookView 的子c,在大U视图中有一个PageBookQ包含了很多Page q可以在它们之间切换Q切换的依据是当前zd的Editor。因此,我们在createControl()Ҏ里要做的是构造这个PageQ简化后的代码如下所C:
׃我们在构造方法里指定了用树l构昄大纲Q所以createControl()里的W一句就会outline 变量得到一个TreeQ见org.eclipse.gef.ui.parts.TreeViewer 的代码)Q第二句把TreeViewer 加到选择同步器中Q从而让用户不论在大U或~辑区域里选择EditPart Ӟ另一斚w能自动做出同L选择Q最后三行的作用在以前的帖子里都有介l,M目的是把大纲视图的模型与~辑区域的模型联pd一Pq样Q对于同一个模型我们就有了两个视图Q体会到MVC 的好处了吧?br /> 实现大纲视图最重要的工作基本就是这些,但还没有完,我们要在init()Ҏ里绑定UNDO/REDO/DELETE {命令到Eclipse ȝ口,否则当大U视囑֤于活动状态时Q主工具条上的这些命令就会变Z可用状态;?getControl()Ҏ里要q回我们的outline 成员变量Q也是指定让这个控件出现在大纲视图中;在dispose()Ҏ里应该把q个TreeViewer 从选择同步器中U除Q最后,必须在PracticeEditor 里覆盖getAdapter()ҎQ前面说q,q个Ҏ是在Editor ȀzL被大U视图调用的Q所以在q里必须把我们实现好的OutlinePage q回l大U视图用,代码如下Q?/p>
要实现序列化Q则必须实现serializable或Externalizable接口。后者承自前者,两者的区别Q实现前者的cd以采用默认的序列化方式。而实现后者的cd完全pw来控制序列化的行ؓ?br /> 在序列化和反序列化过E中需要特D处理的cdM用下列准签名来实现ҎҎQ?/p>
如果CustomercdCExternalizable接口Q那么Customercdd现readExternal
(ObjectInput in)和writeExternal(ObjectOutput out)Ҏ。在q种情况下,按照以下方式序列化及反序列化Customer对象Q?/p>
ObjectOutputStream只能对实CSerializable接口的类的对象进行序列化。默认情况下QObjectOutputStream按照默认方式序列化,q种序列化方式仅仅对对象的非transient的实例变量进行序列化Q而不会序列化对象的transient的实例变量,也不会序列化静态变量?/p>
而当ObjectInputStream按照默认方式反序列化Ӟ有以下特点:
被transient修饰W来修饰的实例变量是不会序列化的Q一般用transient来修C下类型的的变量:
Q) 实例变量不代表对象的固有的内部数据,仅仅代表h一定逻辑含义的时数据?br /> Q) 实例变量表示一些比较敏感的信息Q如密码Q出于安全方面的原因Q不希望对其序列化?br /> Q) 实例变量需要按照用戯定义的方式序列化Q如l过加密后再序列化。这q种情况下可以将其用transient修饰Q然后在writeObject()Ҏ中对其序列化?nbsp;
参见SerializableDemo中的Customer2及Order2cM间的关系?br /> 当通过ObjectOutputStream对象的writeObject(customer)Ҏ序列化Customer2对象Ӟ也会序列化与它关联的Order2对象。而当通过ObjectInputStream对象的readObject()Ҏ反序列化Customer2对象Ӟ实际上会Ҏ个对象图反序列化?br /> 如果对象A持有对象B的引用(注意是A持有B的引用)Q以及间接持有其他对象的引用Q则按照默认方式序列化对象AӞ会将A以及A持有的以及间接持有的所有对象都序列化。反序列化也是如此?/p>
如果用户希望控制cȝ序列化方式,可以在可序列化类中提供以下Ş式的writeObject()Ҏ和readObject()ҎQ?/p>
一般的做法是,在writeObject()Ҏ中,选留骼ObjectOutputStream的defaultWriteObject()ҎQ得对象输出流先执行默认的序列化操作?br /> 反序列化Ӟ在readObject()Ҏ中,先调用ObjectInputStream的defaultReadObject()Ҏ?/p>
如果一个类提供了readResolve()ҎQ那么在执行反序列化操作Ӟ先按照默认方式或者用戯定义的方式进行反序列化,最后再调用readResolve()ҎQ该Ҏq回的对象ؓ反序列化的最l结果?br />
readResolve()Ҏ应该能够被类本n、同一个包中的c,或者访问,因此readResolve()Ҏ的访问权限可以是pirvate、默认或protectedU别?br />
readResolve()Ҏ用来重新指定反序列化得到的对象,与此对应QJava序列化规范还允许在可序列化类中定义一个writeReplace()ҎQ用来重新指定被序列化的对象。writeReplace()Ҏq回一个Objectcd的对象,q个q回对象才是真正要被序列化的对象。权限也可以Z上三U之一?nbsp;
Externallizable接口l承自Serializable接口Q如果一个类实现了Externalizable接口Q那么将完全p个类控制自n的序列化行ؓ。Externalizable接口中声明了两个ҎQ?br />
writeExternal负责序列化操作,readExternal负责反序列化操作。在对实CExternallizable接口的类的对象进行反序列化时Q会先调用类的不带参数的构造方法,q是有别默认反序列化方式的(见2Q。所以实现Externalizable接口的类必须要有不含参数的构造方法?/p>
实例见SerializableDemo里的Customer5的两个版本。将CustomerQ?Q和SimpleServer攑֜server端,Customer2.0和SimpleClient攑֜Client端,直接q行Q将发现抛出错误Q显CZ兼容Q解军_法是手动两个Customer的serialVersionUID设ؓ同一个|q样p兼容。但是这U办法的能力很有限,当一个类的不同版本的serialVersionUID相同Q仍然有可能出现序列化不兼容的情c因为序列化兼容性不仅取决于serialVersionUIDQ还取决于类的不同版本的实现l节和序列化l节?/p>
估计大家?/span>finalize()都有个基本的认识Q那是作清理资源之用。比如在cȝ某个Ҏ中打开了一个文Ӟ那么你可能需要通过finalize()Ҏ来释放该资源。但是话说回来,java世界里,一切皆对象Q而Q何一个学java的h都知道,java对象是不需要手动去清理的,因ؓjava有强大的垃圾回收器,有h可能会有疑问Q那既然如此Qؓ什么还需要在finalize()中手动释放资源呢Q当Ӟ面对q一问题Q有很好的理由去解释Q因源是有限的,而我们又知道垃圾回收器的U程优先U非怽Q在g得以的情况下Q它才会工作Q也是_只要内存_Q失效的对象׃会被清理Q它所持有的资源也得不到释放Q而资源又是有限的Q比如数据库q接Q所以需要我们去手动释放。不q这一点,?/span>JDK7里面g是有所发展Q听说是会自动实现这一点,也就是说Q?/span>sun可能为我们做了这个工作,以后的程序中可能不需要手动释攄似数据库资源的代码了?/span>
如果的确如此Q那是不是意味着finalize()没用了呢?错。不q?/span>finalize()实用到的时候不会很多,它主要用于一些本地方法调用过E中产生的资源清理。比如你通过Native method调用c语言?/span>mellocҎ分配了一些内存空_而这部分内存是需?/span>c语言?/span>free()来进行释攄Q如果不q样Q就会生内存泄漏,所以你需要在finalize()中用Native methodҎ调用free()其释放?/span>
不过话说回来Q?/span>finalize()Ҏq不十分保险Q因?/span>finalize()只有在垃圑֛收器工作的时候才会被调用Q也是_通过它进行资源释攑ƈ不能保马上被释放,甚至可能Ҏ不会被释放(因ؓ垃圾回收器可能不会工作)Q因此,资源释放量另想办法Q别太相?/span>finalize()和垃圑֛收器了?/span>
目前Q关?/span>OSGi的h变得来多Q?/span>OSGi本n也早已从那个专注于嵌入式q_?#8220;?#8221;角色转变q来了,慢慢向企业应用市场渗透?/span>
从某U角度说Q?/span>Eclipse可以说是OSGi在企业应用领域的试金矻I2003q?/span>Eclipse3.0做了一ơ架构上的调_l箋延用插g的架构思想Q但是从3.0版之后,是基?/span>OSGi的实现。也是q一ơ的调整Q推动了Eclipse的迅猛发展?/span>
q是在桌面应用的成功CZQ?/span>OSGi的目标远不止于此Q以其优U的类加蝲机制Q可以说Q只需对其q行扩展Q那么就可以目标瞄准M一个领域。谈C业应用,无可避免的要涉及Web应用Q相信大多数人都在从事该领域的开发?/span>OSGi R4版已明确提出了对Web的支持方式——当Ӟq种方式q是很有限的Q这一点将在后面的文章中详l说明?/span>
鉴于此,很多开源框枉已经或正准备发布支持OSGi的版本,比如Spring DM?/span>Struts1.8.1?/span>CXF{等Q基本上日常所用的开源框枉发布了基?/span>OSGi版本。另外,有不应用服务器也已l基?/span>OSGiq行实现Q比?/span>WAS?/span>6.1版就已经是基?/span>OSGi的实玎ͼ?/span>Spring也在2009q推ZSpring DM ServerQ可?/span>OSGi的吸引力q是很大的?/span>
那么q一切将会ؓ以后的开发带来什么媄响呢Q本文试图从OSGi和构件化开发的角度认识一下该问题?/span>
OSGi的类加蝲机制非常优秀Qؓ每个q行于其中的bundle创徏了独立的cd载器环境Q而运行于同一?/span>OSGi框架内部?/span>bundle之间不像Web服务器中?/span>Web应用那样l缘Q它们之间是可以q行包依赖的Q当?/span>OSGi也提供了服务注册{相xӞ保bundle之间的相互协作能力。而且OSGi支持bundle动态部|Ӏ所以,pȝ无需停机重启Q就可以实现其内部的动态更新?/span>
构g化开发的概念已经提了很多q了Q似乎一直缺乏一U有效的机制对其q行支持。构件化开发ؓpȝ开发带来了很多好处Q整个系l的开发可以按照模块的方式q行划分Q从而按模块q行开发;pȝ由各模块之间D而成Q通过代码一U的依赖或者服务依赖的方式Q对于某个模块进行更斎ͼ只要保持接口不变Q则可以实现对外界无影响?/span>
׃~Z有效的底层支持,构g化开发方式一直没有推行v来。由其对?/span>Web应用而言Q想要实现构件化开发,g更是无从谈v?/span>
OSGi的出玎ͼ让这一事情有所转机。传l的Web应用开发过E中Q可能大多数人的做法是一个大?/span>Web应用按功能模块进行划分,通过包切分来实现模块化开发,但是各模块之间ƈ没有从物理上隔离Q只是以一U组l方式上的约束,使其从表面看上去是分ȝ。对于这U方式,动态更新某个模块也无从谈起了?/span>
而如果将OSGi引入Web应用Q每个模块都对应于一?/span>bundleQ那么模块与模块之间则从物理上进行了隔离Q能有效的将模块内部实现对外隐藏。模块之间的交互Q可以通过接口的导Z引用来实玎ͼ对于未经导出的部分,对于其他模块是不可见的。另外,׃OSGi支持bundle的热部vQ那意味着Q在当前Web应用未停机的情况下,可以Ҏ个或某些模块Q对?/span>OSGi中的bundleQ进行动态更斎ͼ而不影响整个应用的正常运行?/span>
q种从物理上的有效隔ȝ方式Qؓ重用带来了新的方式。以前代码一U的重用Q一般都通过Jar包的形式q行Q而这U方式存在上q的一些缺点,卛_装性不够。而通过ZOSGi?/span>bundle的Ş式,则可以有效隐藏内部实玎ͼ从包一U进行隐藏(q一点个得可以作?/span>Java装性的一U扩展,以前的封装只能做到类一U,不知道未来是否会?/span>Java规范吸收Q?/span>
p一点而言Q?/span>OSGiҎ件化开发进行了很好的支持,当然OSGiq不为此而生?/span>
以上所说的构g依赖都是ZAPI一U的Q那么当Ӟ我们也可以将Web Service{类型服务封装至构g当中Q再l合ESBQ是不是?/span>SOA的一U更好实现呢Q?/span>ESB的关注点在于服务Q这是一U细_度的,它ƈ不考虑服务的封装、重用等斚w——这些方面都p计h员去把握。而结?/span>OSGiQ当然还需要其他设施的支持Q比?/span>CXF已经发布了基?/span>OSGi的实玎ͼ可以服务封装至一?/span>bundleQ以bundle为单元,向外界提供服务,q种服务卛_成了一U粗_度的——构件的特点之一?/span>
lg所qͼ我相信,OSGi的发展,会为构件化的开发方式带来福韟?/span>
引出话题Q?/span>OSGi?/span>SCA在很多方面有着异曲同工q妙Qƈ?/span>SCA的实现框架—?/span>Tuscany也对OSGi做了支持。这两者有互补Ҏ,有时间将对这两者做一个对比研I?/span>
目前Q?/span>OSGi规范的最新版本ؓR4.2Q有兌规范的详l情况请阅读OSGi实战的第7节—?a href="/Files/benniaolk/OSGi/OSGi初识/1.pdf">深入OSGi?/span>OSGi框架主要分ؓ四部分:q行环境Q?/span>executionenvironmentQ、模块(ModulesQ、生命周期管理(Life CycleQ、服务注册(Service RegistryQ。运行在OSGi环境中的是一个个?/span>BundleQ也是Modules的具体实现?/span>
对于每个bundleQ都有各自的ClassLoaderQ在q一点上和传l的Web应用有相g处,在传l的Web应用开发完成之后,都会其部v?/span>Tomcat?/span>Jboss{服务器上,q些Web应用都有着各自?/span>ClassLoader环境Q而两者之间的区别在于Q传l的Web应用无法做到资源的共享,因ؓ它们是完全独立、隔ȝ?/span>OSGi框架?/span>bundle之间的协作提供了底层支持Q通过?/span>bundle?/span>MANIFEST.MF文g?/span>Import-Package?/span>Export-Package{项Q?/span>bundle之间p怺׃n资源及服务,在以后的博文中,我将l出一个具体的CZ?/span>
׃OSGih良好的模块化l构Q我个h认ؓq将为将来的软g开发方式带来很大的冲击Q将更进一步推q模块化开发。目?/span>Web应用的开发一般采?/span>SSH框架Q将整个应用大致分ؓWebQ负责前台展玎ͼ?/span>ServiceQ负责业务逻辑处理Q?/span>DAOQ负责数据持久化Q?/span>DomainQ全局实体c)几个模块Q而发布的时候,被一h?/span>WAR包,部vx务器上。如果采?/span>bundle的Ş式,每个模块可以做ؓ独立?/span>bundleq行开发和部vQ?/span>bundle之间的协作可以通过上述的方式进行,而这样带来的好处是Q一旦需要对某个模块q行更改Q在保证依赖接口不变的前提下Q就可以单独更改相应?/span>bundleQ再q行热部|即可,q样一来,好处是显而易见的Q有效的分离了各个模块,减少了维护成本?/span>
׃采用bundle的Ş式,也增Z模块的复用性。这也是得益?/span>OSGi良好的模块化方式?/span>
另外一个很重要的点是OSGi具备热拔插特性,bundle的安装、启动、停止、卸载都可以在运行时指定Qƈ且可以随时更攏V这样一来,我们可以做到无需重启整个应用Q而只寚w要更改的部分q行升或打补丁卛_?/span>Bundl的状态图转换如下图所C:
?/span> 1 OSGi bundle状态{换图
以上?/span>OSGi的一些基本的Q但也是很重要的东西大概介绍了一下,在以后的博文中逐步深入吧。以上都是关?/span>OSGi原理性的东西Q那么实现该规范的有哪些产品呢?最有名的应该要?/span>Eclipse?/span>Equinox框架了,在网上查资料见有q,Eclipse3.0的那一ơ升U把自n的构架做了一ơ非常大的调_其主要原因就是采用了OSGi框架Q更好的支持?/span>Eclipse的插件体pR另外还?/span>Felix?/span>knopflerfish{?/span>
不过话说回来Q尽?/span>OSGi有很多好处,但是现在主要q是应用在服务器端,如现在的应用服务器基本上都采?/span>OSGi的框Ӟ而真正的应用市场仍处理v步阶D,q和OSGi的生态环境还不成熟,可喜的是Spring推出了其Spring DM?/span>SpringSource DM ServerQ前者能够很方便发布和引用服务,q且?/span>Spring Frameworkq_相融合,?/span>OSGi?/span>bundle context?/span>Spring applicationContext融合在一P大大方便?/span>OSGi的应用。后者是OSGi bundle的运行环境,是一个将Equinox?/span>Tomcat融合在一L服务器。在以后的博文中详l介l这些内宏V?/span>