java 静态模块_模块化Java:静态模块化

java 静态模块

模块化是大型Java系统的重要方面。 通常将构建脚本和项目分成多个模块以改善构建,但是在运行时很少考虑到这一点。

在Modular Java系列的第二篇文章中,我们将介绍静态模块化 。 我们将介绍如何创建捆绑包,如何将其安装到OSGi引擎以及如何在捆绑包之间建立(版本化)依赖项。 在下一个情节中,我们将研究动态模块化,并展示捆绑软件如何对即将来临的其他捆绑软件做出React。

正如Modular Java上次介绍的:这是什么? ,Java始终在开发时将包作为模块化的单元,而在部署时将JAR作为模块化的单元。 但是,尽管像Maven这样的构建工具在编译时保证了软件包和JAR的某种组合,但是这些依赖关系可能与运行时类路径不一致。 为了解决此问题,模块可以声明其依赖性要求,以便在运行时可以在执行之前对其进行检查。

OSGi是Java的运行时动态模块系统。 规范描述了OSGi运行时的工作方式。 当前版本是OSGi R4.2 (如先前的InfoQ所述 )。

OSGi模块(也称为捆绑包 )只是一个简单的JAR文件,其附加信息在存档的MANIFEST.MF 。 每个捆绑包的清单至少必须包含:

  • Bundle-ManifestVersion :对于OSGi R4捆绑包必须为2(否则对于OSGi R3捆绑包默认为1)
  • Bundle-SymbolicName捆绑软件的文本标识符,通常采用反向域名格式(例如com.infoq并且通常对应于其中包含的软件包
  • Bundle-Versionmajor.minor.micro.qualifier形式的版本,其中前三个元素为数字(默认为0), qualifier为文本(默认为空字符串)

创建捆绑

最简单的捆绑包仅包含清单文件,如下所示:

Bundle-ManifestVersion: 2
Bundle-SymbolicName:com.infoq.minimal
Bundle-Version: 1.0.0

但是,创建它并不是很令人兴奋,因此,让我们使用激活器创建一个。 这是特定于OSGi的代码段,在捆绑包启动时会被调用,就像每个捆绑包的main方法一样。

package com.infoq;
import org.osgi.framework.*;
public class ExampleActivator implements BundleActivator {
  public void start(BundleContext context) {
    System.out.println("Started");
  }
  public void stop(BundleContext context) {
    System.out.println("Stopped");
  }
}

为了使OSGi知道哪个类是激活器,我们需要在清单中添加两个额外的项目:

Bundle-Activator: com.infoq.ExampleActivator
Import-Package: org.osgi.framework

Bundle-Activator声明要实例化的类,并在捆绑包启动时调用start()方法。 同样,当捆绑包停止时,将调用stop()方法。

Import-Package怎么样? 每个捆绑软件都需要在清单中定义其依赖项,以便在运行时确定是否所有必需的代码都可用。 在这种情况下, ExampleActivator取决于BundleContextorg.osgi.framework包; 如果我们未在清单中声明该依赖关系,则在运行时将收到NoClassDefFoundError

下载OSGi引擎

要编译和测试我们的捆绑软件,我们需要一个OSGi引擎。 下面列出了可用于OSGi R4.2的几种开源引擎。 还可以下载参考API进行编译(以防止使用任何特定于平台的功能); 但是,您仍然需要OSGi引擎来运行它。 以下是选项:

春分点
执照 Eclipse公共许可证
文献资料 http://www.eclipse.org/equinox/
下载 org.eclipse.osgi_3.5.0.v20090520.jar
笔记

所述 org.eclipse.osgi 束包含框架,运行时和壳作为所有功能于一身。 这是唯一的“单一JAR”解决方案(因此在某些方面最容易上手)。 它也是最长的命名文件。 制表符 equinox.jar (或重命名为 equinox.jar将使该问题消失。

要获得控制台,请在命令行上提供 java -jar org.eclipse.osgi_3.5.0.v20090520.jar -console

构架 org.eclipse.osgi_3.5.0.v20090520.jar
费利克斯
执照 Apache许可证
文献资料 http://felix.apache.org/
下载 Felix Framework发行版2.0.0
笔记 它被视为OSGi引擎的最严格合规性,还用于GlassFish和许多其他开源产品。 您需要运行 java -jar bin/felix.jar 而不是 java -jar felix.jar 因为它会查找路径 bundles 以从当前目录自动启动。
构架 bin/felix.ja r
no鱼
执照 Knopflerfish许可证(类似BSD)
文献资料 http://www.knopflerfish.org/
下载 knopflerfish_fullbin_osgi_2.3.3.jar
笔记 该JAR是自解压的zip文件; 您必须首先使用 java -jar 运行 才能解压。 不要下载“ bin_osgi替代方案,因为它无法启动。
构架 knopflerfish.org/osgi/framework.jar

尽管还有一些针对嵌入式设备的较小的OSGi R3运行时(例如Concierge ),但本系列文章将重点介绍OSGi R4。

编译并运行包

得到您的framework.jar ,编译上面的示例是将OSGi框架添加到类路径,然后将其打包到JAR中的情况:

javac -cp framework.jar com/infoq/*/*.java

jar cfm example.jar MANIFEST.MF com

每个引擎都有某种具有相似(但不完全相同)命令的外壳。 在本练习中,我们将仅研究如何启动和运行引擎,以及安装和启动/停止捆绑软件。

引擎启动并运行后,您可以安装捆绑软件(指定为file:// URL),然后使用返回的数字捆绑软件ID启动和停止它。

  春分点 费利克斯 no鱼
发射 java -jar org.eclipse.osgi_*.jar -console java -jar bin/felix.jar java -jar framework.jar -xargs minimal.xarg s
救命 help
清单 ss ps bundles
安装 install file:///path/to/example.jar
开始 start id
更新资料 update id
停止 stop id
卸载 uninstall id
关掉 exit shutdown

尽管所有外壳程序的工作方式大致相同,但是命令之间的细微差别可能会造成一些混乱。 提供了两个协调的控制台项目( Pax ShellPosh )和启动器( Pax Runner )。 OSGi RFC 132是正在进行的提议,用于尝试使命令外壳标准化。 Apache Karaf旨在成为可以在Equinox或Felix之上运行的发行版,并提供统一的外壳以及其他功能。 虽然建议将它们用于实际部署,但本系列文章将重点介绍原始OSGi框架的实现。

如果启动OSGi框架,则应该能够从上方安装com.infoq.minimal-1.0.0.jar (也可以使用链接的地址和install命令从站点直接安装此文件)。 每次install捆绑软件时,它将打印出该捆绑软件的数字ID。

根据系统中其他捆绑软件的种类,不可能知道安装时将得到的捆绑软件标识符。 但您应该能够使用适当的命令列出已安装的捆绑软件以进行查找。

依存关系

到目前为止,我们只有一个捆绑包。 模块化的好处之一是,我们可以将系统分解为多个较小的模块,从而降低应用程序的复杂性。 在某种程度上,这已经使用Java的程序包完成了,例如,在网络应用程序中经常使用带有client程序包和server程序包的common程序包。 未说明的含义是client程序包和server程序包是独立的,并且都依赖于common程序包。 但是就像Jetty的最近示例client意外地依赖于server结束)一样,这并不总是容易实现的。 实际上,OSGi带给项目的一些成功纯粹是模块之间强制执行的模块化约束。

模块化的另一个好处是将“公共”程序包与非公共程序包分开。 Java的编译时系统允许隐藏的非公共类(那些在特定程序包中可见的类),但没有提供更大的灵活性。 但是,在OSGi模块中,您可以选择导出哪些软件包,而隐含的事实是未导出的软件包对其他模块不可见。

假设我们要开发一个函数来实例化URI模板 (用于Restlet )。 由于这可能是可重用的,因此我们希望将其放在自己的模块中,并且需要使用它的客户依赖我们。 (通常,bundle的粒度并不尽如人意;但是它说明了原理。)该函数将采用一个模板,例如http://www.amazon.{tld}/dp/{isbn}/ ,以及用Map包含tld=com,isbn=1411609255 ,我们可以生成URL http://www.amazon.com/dp/1411609255/ (其中一个原因要做到这一点是让我们改变模板,如果亚马逊的网址方案更改,尽管酷URI不变 。)

为了提供一种在不同实现之间进行切换的简便方法,我们将提供一个接口和一个工厂。 这也将使我们看到如何在仍然为客户提供功能的同时将实现隐藏于客户之外。 代码(跨多个源文件)如下所示:

package com.infoq.templater.api;
import java.util.*;
public interface ITemplater {
  public String template(String uri, Map data);
}
// ---
package com.infoq.templater.api;
import com.infoq.templater.internal.*;
public class TemplaterFactory {
  public static ITemplater getTemplater() {
    return new Templater();
  }
}
// ---
package com.infoq.templater.internal;
import com.infoq.templater.api.*;
import java.util.*;
public class Templater implements ITemplater {
  public String template(String uri, Map data) {
    String[] elements = uri.split("\\{|\\}");
    StringBuffer buf = new StringBuffer();
    for(int i=0;i<elements.length;i++)
        buf.append(i%2 == 0 ? elements[i] : data.get(elements[i]));
    return buf.toString();
  }
}

该实现隐藏在com.infoq.templater.internal包中,而公共API在com.infoq.templater.api包中。 如果需要,这将为我们提供灵活性,以便稍后将实现更改为更有效的机制。 ( internal软件包名称有些传统,但是您可以根据需要命名。)

要授予其他捆绑包访问公共API的权限,我们需要其从捆绑包中导出 。 我们的清单如下所示:

Bundle-ManifestVersion: 2
Bundle-SymbolicName: com.infoq.templater
Bundle-Version: 1.0.0Export-Package: com.infoq.templater.api

创建客户端捆绑

现在,我们可以创建使用模板程序的客户端。 使用上面的示例,创建一个激活器,其start()方法如下所示:

package com.infoq.amazon;
import org.osgi.framework.*;
import com.infoq.templater.api.*;
import java.util.*;
public class Client implements BundleActivator {
  public void start(BundleContext context) {
    Map data = new HashMap();
    data.put("tld", "co.uk"); // or "com" or "de" or ...
    data.put("isbn", "1411609255"); // or "1586033115" or ...
    System.out.println( "Starting\n" + 
        TemplaterFactory.getTemplater().template(
        "http://www.amazon.{tld}/dp/{isbn}/", data));
  }
  public void stop(BundleContext context) {
  }
}

我们需要在清单中定义要显式导入templater API,否则我们的捆绑包将无法编译。 我们可以使用Import-PackageRequire-Bundle来指定依赖项。 前者允许我们单独导入软件包; 后者将隐式导入捆绑软件中所有导出的软件包。 (多个包和捆绑包以逗号分隔。)

Bundle-ManifestVersion: 2
Bundle-SymbolicName: com.infoq.amazon
Bundle-Version: 1.0.0
Bundle-Activator: com.infoq.amazon.ClientImport-Package: org.osgi.framework
Require-Bundle: com.infoq.templater

请注意,在前面的示例中,我们已经在使用Import-Package导入org.osgi.framework 。 在这种情况下,我们演示了使用Bundle-SymbolicNameRequire-Bundle 。 我们同样可以将其添加为Import-Package: org.osgi.framework, com.infoq.templater.api

无论我们如何声明对templater包的依赖,我们都只能访问单个导出的包com.infoq.templater 。 尽管客户端可以通过TemplaterFactory.getTemplater()访问模板程序,但我们不能直接从internal包访问该类。 这使我们能够灵活地在将来改变实施方式而不会破坏客户。

测试系统

任何OSGi应用程序实际上只是一组捆绑软件。 在这种情况下,我们需要编译和打包捆绑包(如前所述),启动OSGi引擎,然后安装两个捆绑包。 这是Equinox的外观:

java -jar org.eclipse.osgi_* -console
osgi> installfile:///tmp/com.infoq.templater-1.0.0.jar
Bundle id is 1
osgi> install file:///tmp/com.infoq.amazon-1.0.0.jar
Bundle id is 2
osgi> start 2
Starting
http://www.amazon.co.uk/dp/1411609255

Amazon客户端捆绑包已启动; 然后,它使用先前的(公认的硬编码)值为我们实例化了URI模板。 然后在捆绑包本身启动期间将其打印出来,以确认捆绑包可以正常工作。 显然,一个真实的系统不会那么僵硬; 但可以在任何其他应用程序中使用Templater服务(例如,在基于Web的应用程序中生成链接)。 将来,我们将在OSGi上下文中研究Web应用程序。

版本化依赖

本期文章的最后一点是要注意,我们目前拥有的依赖项是未版本化的。 或者说,我们将可以使用任何版本。 捆绑包和单独的软件包都可以进行版本控制,并且次要编号的增加用于表示新功能(但保持向后兼容)。 的org.osgi.framework包,例如,是版本1.4.0中的OSGi R4.1和1.5.0中的OSGi R4.2。 (顺便说一句,这是将捆绑软件版本和市场营销版本保持为独立概念的一个很好的理由,这是Scala语言尚未学习的内容 。)

要声明对特定版本的依赖关系,我们必须在Import-PackageRequire-Bundle表达它。 例如,我们可以指定Require-Bundle: com.infoq.templater;bundle-version="1.0.0"表示要使用最低版本1.0.0才能正常工作。 同样,我们可以对Import-Package: com.infoq.templater.api;version="1.0.0"进行相同的操作Import-Package: com.infoq.templater.api;version="1.0.0" –但请记住, 软件包的版本与捆绑软件的版本不同。 如果未指定版本,则默认为0.0.0,因此,除非您具有相应的Export-Package: com.infoq.templater.api;version="1.0.0"否则将无法解析此导入。

也可以指定版本范围。 例如,OSGi版本号的常规含义是,主编号的增加表示向后兼容性的变化,因此我们可能只想将自己限制在1.x范围内。 为此,我们可以表示一个(bundle-)version="[1.0,2.0)"依赖关系约束。 在这种情况下,[表示“包含”,而)表示“专有”,换句话说,即“从1.0到但不包括2.0以后”。 实际上,表示依赖性约束为“ 1.0”与“ [1.0, ∞)”相同, 换句话说,大于1.0。

尽管它不在本文讨论范围之内,但有可能一次在OSGi系统中具有捆绑软件的多个版本。 如果您有一个依赖于API 1.0版的旧客户端,又有一个依赖于API 2.0版的新客户端,这可能会很有用。 只要每个捆绑软件的依赖关系是一致的(换句话说,您就没有一个捆绑软件试图同时或直接或间接导入1.0和2.0),则应用程序将运行良好。 作为读者的练习,您可以创建2.0版的Templater API以使用泛型,然后创建一个仅依赖于1.x版以及使用2.x版的客户端。

摘要

在本文中,我们研究了开源OSGi引擎Equinox,Felix和Knopflerfish,并创建了一对相关的捆绑软件。 我们还涉及版本化的依赖项。 目前,这种模块化是静态的。 我们还没有探索OSGi的任何动态特性。 我们将在下一期中讨论它。

可安装的捆绑软件(也包含源代码):

翻译自: https://www.infoq.com/articles/modular-java-static-modularity/?topicPageSponsorship=c1246725-b0a7-43a6-9ef9-68102c8d48e1

java 静态模块

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值