十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
这篇文章将为大家详细讲解有关怎么执行OSGi模块化,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。
十载的老边网站建设经验,针对设计、前端、开发、售后、文案、推广等六对一服务,响应快,48小时及时工作处理。成都营销网站建设的优势是能够根据用户设备显示端的尺寸不同,自动调整老边建站的显示方式,使网站能够适用不同显示终端,在浏览器中调整网站的宽度,无论在任何一种浏览器上浏览网站,都能展现优雅布局与设计,从而大程度地提升浏览体验。创新互联公司从事“老边网站设计”,“老边网站推广”以来,每个客户项目都认真落实执行。
Lars Vogel,Simon Scholz(c)2008,2017 vogella GmbH版本5.2,202.02.2017
目录
OSGi简介软件模块化
OSGi是一组规范,在其核心规范中定义了Java的组件和服务模型。OSGi的实际优点是每个软件组件可以通过一组导出的Java包来定义其API,并且每个组件可以指定其所需的依赖性。
组件和服务可以被动态地安装,激活,去激活,更新和卸载。
OSGi规范有几个实现,例如Eclipse Equinox,Knopflerfish OSGi或Apache Felix。
Eclipse Equinox是基本OSGi规范的参考实现。它也是Eclipse应用程序所基于的运行时环境。
一个插件可以通过Eclipse中通过生成文件 ? 新建 ? 其他... ? 插件开发 ? 插件工程菜单项。相应的向导允许指定多个选项。此脚本调用使用以下选项生成的插件:简单插件或简单包。
无激活剂
对用户界面没有贡献
不是一个富客户端应用程序
生成时没有模板
技术上OSGi插件是 带有附加元信息的.jar文件。此元信息存储在 META-INF / MANIFEST.MF 文件中。此文件称为 清单 文件,是标准Java规范的一部分,OSGi向其中添加了额外的元数据。根据Java规范,任何Java运行时必须忽略未知的元数据。因此,在其他Java环境中可以无限制地使用插件。
以下列表是清单文件的示例。
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
下表说明了清单文件中的标识符。有关OSGi中通常使用的版本模式的信息,请参阅使用OSGi进行语义版本控制。
标识符 | 描述 |
---|---|
软件包名称 | 插件的简短描述。 |
Bundle-SymbolicName | 插件的唯一标识符。如果此插件使用Eclipse的扩展点功能,则必须将其标记为Singleton。您可以通过在Bundle-SymbolicName标识符之后添加以下语句来实现: ; singleton:=true |
Bundle-Version | 定义插件版本,如果发布了插件的新版本,则必须递增。 |
Bundle-Activator | 定义实现接口的可选激活器类BundleActivator。该类的实例在插件被激活时创建。无论何时启动或停止插件,都会调用它start()和stop()方法。OSGi激活器可用于在启动期间配置插件。激活程序的执行会增加应用程序的启动时间,因此应谨慎使用此功能。 |
Bundle-RequiredExecutionEnvironment(BREE) | 指定运行插件所需的Java版本。如果不满足此要求,则OSGi运行时不会加载插件。 |
Bundle-ActivationPolicy | 将此设置为 延迟 将告诉OSGi运行时,只有当其插件之一(即类和接口)被其他插件使用时,才会激活该插件。如果未设置,Equinox运行时不会激活插件,即,此插件提供的服务不可用。 |
Bundle-ClassPath | Bundle-ClassPath指定从bundle加载类的位置。默认值为“。”。它允许从bundle的根加载类。您还可以向其中添加JAR文件,这些文件称为 嵌套JAR文件。 |
OSGi建议对 通过 字段标识符定义的版本号使用 。。模式 Bundle-Version。如果更改插件代码,请根据以下规则集增加版本。
如果所有更改都向后兼容,则会增加。
如果公共API已更改,但所有更改都向后兼容,则增加。
如果更改不向后兼容,则会增加。
有关此版本方案的更多信息,请参阅 Eclipse版本编号Wiki。
通过在OSGi运行时中安装插件,插件将保留在本地bundle缓存中。OSGi运行时然后尝试解析它的依赖关系。
如果解决了所有必需的依赖关系,则插件位于
RESOLVED 状态,否则它保持在 INSTALLED 状态。
在存在可以满足依赖性的几个插件的情况下,使用具有最高有效版本的插件。
如果版本相同,则使用具有最低唯一标识符(ID)的插件。每个插件都会在安装期间获取框架分配的此ID。
插件启动时,其状态为 STARTING。成功启动后,它将变为 ACTIVE。
此生命周期如下图所示。
在MANIFEST.MF文件中,插件还通过导出包标识符定义其API。未显式导出的所有软件包对其他插件不可见。
所有这些限制都通过特定的OSGi实施classloader。每个插件都有自己的类加载器。不使用reflection.s不能访问受限类
不幸的是,OSGi不能阻止你使用Java反射来访问这些类。这是因为OSGi基于尚不支持模块化层的Java运行时。
通过x-internal标志,OSGi运行时可以将导出的包标记为临时。这允许其他插件使用相应的类,但表示这些类不被视为官方API。
以下屏幕截图显示如何x-internal在清单编辑器中设置包。
这是相应的清单文件的外观。
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Provider Bundle-SymbolicName: de.vogella.osgi.xinternal.provider Bundle-Version: 1.0.0.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Export-Package: de.vogella.osgi.xinternal.provider;x-internal:=true
您可以配置Eclipse Java编辑器如何显示临时API的使用。这种访问可以被配置为显示为,错误,警告或者如果这种访问应当不导致附加消息。
默认值为显示警告消息。您可以通过Eclipse的喜好调整这个窗口 ? 首选项 ? 的Java ? 编译器 ? 错误/警告首选项设置。
您可以定义一组插件可以访问临时API,而不会出现警告或错误消息。这可以通过x-friends指令完成。如果您在清单编辑器的“ 运行时 ”选项卡上的“ 包可见性”部分添加插件,则会添加此标志。
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Provider Bundle-SymbolicName: de.vogella.osgi.xinternal.provider Bundle-Version: 1.0.0.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Export-Package: de.vogella.osgi.xinternal.provider;x-friends:="another.bundle"
该x-friends设置具有相同的效果,x-internal但是x-friends设置中提到的所有插件都可以访问包,而不会收到错误或警告消息。
OSGi控制台就像一个命令行shell。在此控制台中,您可以键入命令以执行OSGi操作。这可以用于分析应用程序的OSGi层上的问题。
例如,使用命令 ss 获取所有包的概述,它们的状态和bundle-id。下表是最重要的OSGi命令的参考。
命令 | 描述 |
---|---|
help | 列出可用的命令。 |
ss | 列出安装的软件包及其状态。 |
ss vogella | 列出捆绑包及其在其名称中包含vogella的状态。 |
start | 使用 ID 启动包。 |
stop | 使用 ID 停止捆绑。 |
diag | 诊断特定包。它列出所有缺失的依赖项。 |
install URL | 从URL安装包。 |
uninstall | 使用 ID 卸载捆绑软件。 |
bundle | 显示具有 ID的捆绑包的信息,包括已注册和已使用的服务。 |
headers | 显示包的MANIFST.MF信息。 |
services filter | 显示所有可用的服务及其消费者。过滤器是一个可选的LDAP过滤器,例如,查看提供ManagedService实现的所有服务使用“services(objectclass = * ManagedService)”命令。 |
如果 在启动配置中指定 -console参数,Eclipse将允许您与OSGi控制台交互。默认情况下,使用Eclipse IDE创建的OSGi启动配置包含此参数。通过以下参数,您可以打开一个端口,您可以通过telnet协议连接到该端口。
-console 5555
如果打开到OSGi控制台的telnet会话,您可以使用tab完成和类似于 Linux下的Bash shell 的命令历史记录 。
如果您计划向Eclipse平台添加功能,则应下载最新的Eclipse版本。官方版本具有稳定的API,因此是添加插件和功能的良好基础。
Eclipse IDE提供了不同的版本。虽然可以在任何Eclipse包中安装必要的工具,但通常更容易下载Eclipse Standard发行版,其中包含插件开发的所有必要工具。其他软件包增加了Eclipse插件开发不需要的更多工具。
浏览到Eclipse下载站点并下载Eclipse Standard软件包。
Eclipse 4.5还提供了一个新的Eclipse安装程序安装程序。如果您要下载多种版本的Eclipse,安装程序将非常有用,因为它使用共享安装池用于常用插件。 |
创建一个名为com.example.e4.rcp.todo.model的简单插件项目(请参阅命名约定:简单插件)。
以下屏幕截图描述了插件项目向导的第二页及其相应的设置。按此页上的完成按钮,以避免使用模板。
创建com.example.e4.rcp.todo.model包和以下模型类。
package com.example.e4.rcp.todo.model; import java.util.Date; public class Todo { private final long id; private String summary = ""; private String description = ""; private boolean done = false; private Date dueDate = new Date(); }
您的最终ID字段出现错误。此错误在下一节中解决。 |
使用来源 ? 生成getter和setter ...菜单来创建getter和setter方法为您的字段。
为什么id字段标记为final? 该ID是终局的,因此Eclipse将创建只有一个getter。这是正确和希望的。我们将使用此字段来生成equals和hashCode()方法,因此它不应该是可变的。更改这是在使用的字段equals和hashCode()方法可以创建错误,这些错误是难以确定的,即,一个对象包含在一个HashMap,但没有找到。 |
调整生成的getter和setter dueDate()字段以进行防御性复制。该Date班是不是一成不变的,我们要避免这种情况的一个实例Todo可以从外部改变,而相应的setter。
public Date getDueDate() { return new Date(dueDate.getTime()); } public void setDueDate(Date dueDate) { this.dueDate = new Date(dueDate.getTime()); }
生成的类应该类似于以下列表。
package com.example.e4.rcp.todo.model; import java.util.Date; public class Todo { private final long id; private String summary = ""; private String description = ""; private boolean done = false; private Date dueDate = new Date(); public Todo(long id) { this.id = id; } public Todo(long id, String summary, String description, boolean done, Date dueDate) { this.id = id; this.summary = summary; this.description = description; this.done = done; setDueDate(dueDate); } public long getId() { return id; } public String getSummary() { return summary; } public void setSummary(String summary) { this.summary = summary; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public boolean isDone() { return done; } public void setDone(boolean done) { this.done = done; } public Date getDueDate() { return new Date(dueDate.getTime()); } public void setDueDate(Date dueDate) { this.dueDate = new Date(dueDate.getTime()); } }
将以下copy() 方法添加 到类中。
public Todo copy() { return new Todo(this.id, this.summary, this.description, this.done, getDueDate()); }
导出com.example.e4.rcp.todo.model包以将其定义为API。
为此,请打开MANIFEST.MF文件并选择“ 运行时 ”选项卡。添加com.example.e4.rcp.todo.model到导出的包。
创建一个名为com.example.e4.rcp.todo.services的新简单插件(请参阅命名约定:简单插件)项目。此插件在以下描述中称为待办服务插件。
MacOS的操作系统踹结尾的文件夹。服务特殊,因此我们使用。服务结束。 |
com.example.e4.rcp.todo.services.internal 在服务插件中创建 包并创建以下类。
package com.example.e4.rcp.todo.services.internal; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.stream.Collectors; import com.example.e4.rcp.todo.model.ITodoService; import com.example.e4.rcp.todo.model.Tag; import com.example.e4.rcp.todo.model.Todo; public class MyTodoServiceImpl implements ITodoService { private static AtomicInteger current = new AtomicInteger(1); private List todos; private TagrootTag; public MyTodoServiceImpl() { todos = createInitialModel(); createRootTag(todos); } @Override public void getTodos(Consumer todosConsumer) { // always pass a new copy of the data todosConsumer.accept(todos.stream().map(t -> t.copy()).collect(Collectors.toList())); } protected List getTodosInternal() { return todos; } // create or update an existing instance of Todo @Override public synchronized boolean saveTodo(Todo newTodo) { // hold the Optional object as reference to determine, if the Todo is // newly created or not Optional todoOptional = findById(newTodo.getId()); // get the actual todo or create a new one Todo todo = todoOptional.orElse(new Todo(current.getAndIncrement())); todo.setSummary(newTodo.getSummary()); todo.setDescription(newTodo.getDescription()); todo.setDone(newTodo.isDone()); todo.setDueDate(newTodo.getDueDate()); if (!todoOptional.isPresent()) { todos.add(todo); } return true; } @Override public Optional getTodo(long id) { return findById(id).map(todo -> todo.copy()); } @Override public boolean deleteTodo(long id) { Optional deleteTodo = findById(id); deleteTodo.ifPresent(todo -> { todos.remove(todo); }); return deleteTodo.isPresent(); } // Example data, change if you like private List createInitialModel() { List list = new ArrayList<>(); list.add(createTodo("Application model", "Flexible and extensible")); list.add(createTodo("DI", "@Inject as programming mode")); list.add(createTodo("OSGi", "Services")); list.add(createTodo("SWT", "Widgets")); list.add(createTodo("JFace", "Especially Viewers!")); list.add(createTodo("CSS Styling", "Style your application")); list.add(createTodo("Eclipse services", "Selection, model, Part")); list.add(createTodo("Renderer", "Different UI toolkit")); list.add(createTodo("Compatibility Layer", "Run Eclipse 3.x")); return list; } private Todo createTodo(String summary, String description) { return new Todo(current.getAndIncrement(), summary, description, false, new Date()); } private Optional findById(long id) { return getTodosInternal().stream().filter(t -> t.getId() == id).findAny(); } }
练习:创建服务包
8.2。导出服务插件中的包
com.example.e4.rcp.todo.services 通过“ 运行时 ”选项卡上的MANIFEST.MF 文件 导出 包 ,以便其他插件可用。
请注意,Eclipse工具不支持导出空包。您必须在包中至少创建一个类,然后才能将其导出。 |
通过创建一个新的简单的插件项目“com.vogella.osgi.firstbundle.internal” 文件 ? 新建 ? 其他... ? 插件开发 ? 插件项目。
创建以下线程类。
package com.vogella.osgi.firstbundle.internal; public class MyThread extends Thread { private volatile boolean active = true; public void run() { while (active) { System.out.println("Hello OSGi console"); try { Thread.sleep(5000); } catch (Exception e) { System.out.println("Thread interrupted " + e.getMessage()); } } } public void stopThread() { active = false; } }
将Activator.java类更改为以下类。
package com.vogella.osgi.firstbundle; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import de.vogella.osgi.firstbundle.internal.MyThread; public class Activator implements BundleActivator { private MyThread myThread; public void start(BundleContext context) throws Exception { System.out.println("Starting com.vogella.osgi.firstbundle"); myThread = new MyThread(); myThread.start(); } public void stop(BundleContext context) throws Exception { System.out.println("Stopping com.vogella.osgi.firstbundle"); myThread.stopThread(); myThread.join(); } }
导出您的捆绑包。这将允许您将其安装到OSGi运行时。选择您的捆绑包,然后选择文件 ? 导出 ? 插件开发 ?可部署的插件和碎片。
取消标记导出源的选项。
训练 | 服务和支持 |
---|---|
vogella公司提供 来自Eclipse RCP,Android,Git,Java,Gradle和Spring等领域专家的全面培训和教育服务。我们提供公共和内部培训。无论你决定采取什么样的课程,你都可以在参考之前体验到许多之前的“我参加的最好的IT类”。 | vogella公司提供专家咨询服务,开发支持和辅导。我们的客户范围从财富100强公司到个人开发商。 |
版权所有©2012-2016 vogella GmbH。免费使用软件示例是根据EPL许可证的条款授予的。本教程是根据 Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Germany许可证发布的。
关于怎么执行OSGi模块化就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。