简介
- Java其他插件化开发(OSGi文档较少,2021-02弃用)
- OSGi:
Open Service Gateway Initiative
是一个Java模块化规范 - 官网:https://www.osgi.org/
- Eclipse的插件机制就是基于OSGI规范实现
- 相关实现(运行时容器)
- Felix 是一个 OSGi 版本 4 规范的 Apache 实现
- Apache Karaf:基于Felix实现,是一个运行基于OSGi的应用程序的平台,提供了如命令行界面将使我们能够与平台进行交互
- ServiceMix:它将Apache ActiveMQ,Camel,CXF和Karaf的特性和功能统一到一个功能强大的运行时平台中,可用于构建自己的集成解决方案,它提供了由OSGi独家提供的完整的企业级ESB。最近更新2017年
- Equinox 是 Eclipse对应的OSGi框架(容器),是AppFuse的一个轻量级版本。对web的默认支持Spring MVC、Hibernat等组件
- Felix 是一个 OSGi 版本 4 规范的 Apache 实现
- Gemimi Blueprint 由Eclipse维护,部分代码由SpringSource捐献的
Spring DM
(Spring Dynamic Modules,前身为Spring OSGi)项目代码 ^3- SpringDM并不是OSGi的标准实现,它的运行必须依赖OSGi的标准容器,比如Equinox、Felix或是Knopflerfish等
- SpringDM完成了OSGi服务的注册、查询、使用和监听,我们也可以将这些OSGi服务称之为Bean
- 基于springboot osgi demo未测试成功
相关文档
OSGi与微服务区别
- OSGi ^2
- 各模块是基于同一个JVM,服务(模块)直接调用是基于方法级别的,不会有网络开销。各服务也叫µServices或纳米服务
- 可基于单体部署
- 微服务(Micro Services)
- 各模块基于不同JVM,甚至可基于不同语言实现。服务见调用存在网络开销,协调许多远程服务之间的通信通常需要异步编程模型并发送消息
- 部署基于微服务的系统需要在DevOps方面进行大量工作
- 也可以选择将两种方法混合使用
OSGi概念
- OSGI规范提供了
Bundle
、Event
、配置管理(ConfigAdmin
)、声明式服务(Delarative Service
)、Service Tracker
、Blueprint
等等运行时机制,方便我们构建模块化的应用系统
Bundle
- bundle其表现就是一个jar包,如eclipse的一个插件
- OSGI 类加载器并不遵循 Java 的双亲委派模型,OSGi 为每个 bundle 提供一个类加载器,该加载器能够加载 bundle 内部的类和资源,bundle 之间的交互是从一个 bundle 类加载器委托到另一个 bundle 类加载器,所有 bundle 都有一个父类加载器 ^1
- Fragment bundle 是一种特殊的 bundle,不是独立的 bundle,必须依附于其他 bundle 来使用
- 由于基于不同的类加载器,如果其中一个模块无法正常运行,不会影响其他模块运行
bundle生命周期 ^4
- bundle解析优先级
- 已解析的(resolved) > 未解析的(installed)
- 相同优先级,有多个匹配时,版本高者优先,版本相同则选最先安装的
- OSGi类查找顺序
- 如果类所在包以
java.
开头,则委托给父类加载 - 如果类所在包在导入包中,则委托给导出该包的Bundle
- 最后在Bundle自身的类路径上查找
- 如果类所在包以
处理模块耦合(依赖)
- osgi通过import/export package的机制来控制bundle间有限地藕合
- 其他bundle包只能使用明确导出的软件包,模块化的这一层确保在bundle包之间仅共享API类,并且严格隐藏实现类,不能使用
new ServiceImpl()
等类似基于实现的代码 - Export/Import package是通过bundle里的
META-INF/MANIFEST.MF
文件里指定的。如可使用Maven-jar-plugin
等插件实现MANIFEST.MF文件的构建
- 其他bundle包只能使用明确导出的软件包,模块化的这一层确保在bundle包之间仅共享API类,并且严格隐藏实现类,不能使用
- 还可以通过osgi service的方式实现藕合 ^4
- osgi service是osgi规范中定义的一种本地服务的机制,“本地”意味着它只是在osgi framework内有效,不可跨osgi framework调用,更不可跨JVM调用
- osgi framework有一个service registry,bundle可以把一个实现某种接口的bean实例作为osgi service注册(register)到service registry上,其它bundle就可以从service registry上发现并引用它,所以,本质上osgi service就是一个bean。
- 实用案例:我们会把接口定义在一个bundle A里,接口的实现则在另一个bundle B里,并将接口实现实例化后注册成osgi service,而第三个bundle C则引用这个osgi service。因为bundle B和C都需要用到bundle A的接口定义,所以bundle A需export接口定义所在的package,而bundle B和C则需import这个package。这样bundle B和C之间就不需用export/import package来藕合了,实现B和C之间的解藕
MANIFEST.MF文件(Import/Export package使用)
- MANIFEST.MF 文件(一般通过Maven自动生成)
1 | # bundle命名空间、名称、版本 |
osgi服务
BundleActivator
定义组件被启动或停止时的动作- start
- stop
ServiceListener
监听服务状态- serviceChanged(ServiceEvent), ServiceEvent包含有REGISTERED(注册)、MODIFIED、UNREGISTERING(注销)、MODIFIED_ENDMATCH
ServiceTracker
类
osgi service registry
- 发布服务 ^4
1 | public class ActivatorA implements BundleActivator { |
- 引用服务
1 | public class ActivatorB implements BundleActivator { |
osgi服务动态性
Blueprint
- Blueprint ^4
- 为了适应OSGI的动态环境,spring发展出spring dynamic modules(SpringDM),Blueprint的规范则是来源于SpringDM的进一步发展
- 目前,Blueprint规范主要有两个实现:Aries blueprint和Gemini blueprint,它们分别来自Apache和Eclipse两个开源组织
- Blueprint可以象Spring那样,通过XML的方式构建应用,当然也可以通过Blueprint annotation的方式实现同样的目的。由于XML可以和bundle分离,单独部署到servicemix上,所以比annotation的方式更具灵活性,所以我们推荐使用XML的方式
- 除了Blueprint之外,OSGI还可以支持Delerative Service(DS)、iPojo等方式,达到类似的功能。但是由于blueprint还可以集成很多功能,例如:Camel。所以推荐使用Blueprint
- blueprint是在bundle启动之后(即bundle状态成为ACTIVE)才开始被解析、构建应用,所以,要成功构建bundle的blueprint应用,必须先确保bundle本身能正常启动
- blueprint容器的状态
GracePeriod
blueprint正在等待所需的依赖条件Creating
blueprint已满足了依赖条件,并开始构建blueprint应用Failure
blueprint没法满足所需的依赖条件,或者无法根据xml文档构建相应的应用(可能是xml在语法上有错误)Created
blueprint应用已成功构建
- 在bundle里,blueprint的xml文档是默认放在jar包里的
OSGI-INF/blueprint
文件夹里,如果你将它放在其它位置,则需要在manifest.mf里添加一个Bundle-Blueprint的项,例如:Bundle-Blueprint:OSGI-INF/myapplication/*.xml
案例
服务端新建
resources/OSGI-INF/blueprint/my-service-bp.xml
,这个文档就起和spring的ApplicationContext.xml类似的作用1
2
3
4
5
6
7
8
9
10
11
12"1.0" encoding="UTF-8" xml version=
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">
<!-- 实例化MyService -->
<bean id="MyServiceImpl" class="cn.aezo.osgi.impl.MyServiceImpl"/>
<!-- 发布成OSGI服务。因此就不用在Activator中发布服务了 -->
<service id="MyService" ref="MyServiceImpl" interface="cn.aezo.osgi.MyService">
<service-properties>
<entry key="ServiceName" value="MyService"/>
</service-properties>
</service>
</blueprint>客户端新建
resources/OSGI-INF/blueprint/my-client-bp.xml
1
2
3
4
5
6
7
8
9
10
11"1.0" encoding="UTF-8" xml version=
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">
<!--引用服务-->
<reference id="MyService" interface="cn.aezo.osgi.MyService" filter="(ServiceName=MyService)"/>
<!-- 实例化 -->
<bean id="MyBean" class="cn.aezo.osgi.impl.DIWithBlueprint">
<!--注入服务引用-->
<property name="myService" ref="MyService"/>
</bean>
</blueprint>客户端
DIWithBlueprint.java
接受注入1
2
3
4
5
6
7public class DIWithBlueprint {
private MyService myService;
public void setMyService(MyService myService) {
this.myService = myService;
}
}
动态配置
OSGI里面用于操作配置文件(cfg)的接口有2个
org.osgi.service.cm.ManagedService
用于操作单个配置文件org.osgi.service.cm.ManagedServiceFactory
用于操作一组相关的配置文件1
2
3
4
5
6
7// ManagedServiceFactory 参考 https://blog.csdn.net/mn960mn/article/details/50450494
public class ConfigManagedExample implements ManagedService {
// Dictionary 是一个Java抽象类,用来存储键/值对,作用和Map类相似
public void updated(Dictionary<String, ?> properties) throws ConfigurationException {
System.out.println("--------properties被修改,会触发此方法---------");
}
}
基于Blueprint实现动态配置
1 | "1.0" encoding="UTF-8" xml version= |
OSGi示例
- 参考:https://www.baeldung.com/osgi
- 示例源码:https://github.com/oldinaction/smjava/tree/master/osgi
- 常见的OSGi容器Apache Felix和Eclipse’s Equinox。而Eclipse’s Equinox很久没有更新,因此基于Felix容器测试
- 下载Felix容器,或者直接下载Apache Karaf容器(推荐,Karaf是基于Felix的OSGi管理平台,包含命令界面)
1 | # https://karaf.apache.org/get-started.html |
- 引入依赖
1 | <dependency> |
- 上述打包出来的MANIFEST.MF文件如下
1 | Manifest-Version: 1.0 |
- 测试
1 | # 在本项目目录启动 |
idea使用
- 配置. 参考:https://www.jb51.net/article/160461.htm
- 下载Felix
- File - Settings - Languages & Frameworks - OSGi Framework Instances - 导入Felix
- File - Settings - Languages & Frameworks - OSGi
- 启动配置:Edit configuration - 新建一个OSGi启动配置
karaf容器
karaf命令
1 | ls # 列举bundle |
参考文章