第 6 章 JBoss EAP 的类加载

JBoss EAP 使用一个模块类加载系统来控制所部署的应用程序的类路径。这个系统和传统的分级结构类加载程序系统相比,可以提供更多的灵活性以及控制功能。开发人员可以在他们的应用程序对使用的类进行“细颗粒”控制,并可以把部署配置为使用自己的类而不是使用应用程序服务器所提供的类。

模块化类加载程序把所有 Java 类分离为不同的逻辑组,这些逻辑组被称为模块。每个模块都可以定义它的依赖模块,从而把依赖模块中的类加到自己的模块路径中。因为每个部署的 JAR 和 WAR 文件都被看作为一个模块,因此开发人员可以通过把模块配置添加到应用程序中来对应用程序类的内容进行控制。

6.1. 模块

模块是类的一个逻辑组,它被用来进行类加载以及对依赖模块的管理。JBoss EAP 有两种模块:静态(static)动态(dynamic)。这两种模块的主要区别是如何进行打包。

静态模块

静态模块在应用程序服务器的 EAP_HOME/modules/ 目录中定义。每个模块都以一个子目录的形式存在(例如 EAP_HOME/modules/com/mysql/)。每个模块目录包括一个子目录,默认为 main 并包括 module.xml 配置文件和所需的 JAR 文件。所有由应用程序服务器提供的 API(包括 Java EE API 以及其它 API)都以静态模块的形式提供。

MySQL JDBC Driver module.xml 文件示例

<?xml version="1.0" ?>
<module xmlns="urn:jboss:module:1.1" name="com.mysql">
  <resources>
    <resource-root path="mysql-connector-java-5.1.36-bin.jar"/>
  </resources>
  <dependencies>
    <module name="javax.api"/>
    <module name="javax.transaction.api"/>
  </dependencies>
</module>

模块名(com.mysql)必须匹配模块的目录结构,除了 Slot 名称(main)。

如果许多应用程序部署在一个使用相同第三方程序库的服务器上时,创建自定义的静态模块就会非常有用。系统管理员可以创建并安装一个包括了这些程序库的模块,而不需要为每个应用程序单独绑定这些程序库。然后,应用程序就可以声明显性依赖(explicit dependency)于这个静态模块。

JBoss EAP 所提供的模块包括在 EAP_HOME/modules 目录下的 system 目录中。这将使这些模块和第三方所提供的模块相分离。所有红帽所提供的、位于 JBoss EAP 之上的产品都会在 system 目录中安装这些模块。

用户需要保证自定义的模块被安装在 EAP_HOME/modules 目录中,每个模块使用一个目录。这可以确保存在于 system 目录中的自定义版本模块被加载,而不是加载原来的标准版本。这样,用户提供的模块会在系统模块之前使用。

如果使用 JBOSS_MODULEPATH 环境变量修改 JBoss EAP 搜索模块的位置,则产品会在一个指定的位置中查找 system 子目录结构。一个 system 结构需要存在于 JBOSS_MODULEPATH 指定的位置中。

动态模块

动态模块由应用程序服务器为每个 JAR 或 WAR 部署(或一个 EAR 中的子部署)创建并加载。动态模块的名字由部署的归档名获得。因为部署会作为模块加载,所以它们可以配置依赖关系并可用作为其它部署的依赖模块。

只在需要时才加载模块。这通常在部署具有显性或隐性依赖关系的应用程序时才会发生。

6.2. 模块依赖关系

一个模块依赖关系就是一个声明,它指定了一个模块需要一个或多个其它模块的类才可以正常工作。当 JBoss EAP 加载一个模块时,模块化类加载程序会解析这个模块的依赖关系,并把每个依赖模块的类添加到它的类路径中。如果一个指定的依赖模块没有找到,则模块加载将失败。

注意

关于模块及模块类加载系统的完整细节,请参考模块章节。

部署的应用程序(JAR 或 WAR)被加载为动态模块并利用依赖关系来访问 JBoss EAP 提供的 API。

有两种依赖关系:显性(explicit)隱性(implicit)

显性依赖关系
显性依赖关系由程序员在一个配置文件中声明。一个静态模块可以在它的 module.xml 文件中声明依赖关系;而一个动态模块可以在部署的 MANIFEST.MFjboss-deployment-structure.xml 部署描述符(descriptor)中声明依赖关系。
隐性依赖关系

当在部署里找到某些条件或元数据时,JBoss EAP 会自动添加隐性依赖关系。JBoss EAP 提供的 Java EE7 API 是通过检测部署里的隐性依赖关系而添加的模块示例。

使用 jboss-deployment-structure.xml 部署描述符文件可以把部署配置为排除特定的隐性依赖关系。当一个应用程序绑定了某个特定版本的库, JBoss EAP 将试图加载这个库作为一个隐性依赖时非常有用。

可选的依赖关系

显性依赖关系可指定为可选的。加载可选依赖关系失败不会导致模块加载失败。然而,如果依赖关系之后变成可用的,它将不会被添加到模块的类路径里。加载模块时依赖关系必须是可用的。

导出依赖关系

一个模块的类路径只包括它自己的类,以及它直接的依赖关系。一个模块无法访问它的依赖模块的依赖模块的库。但是,一个模块可以指定输出一个显性依赖关系,所有依赖于这个模块的模块都可以使用这个输出的依赖关系。

例如,模块 A 依赖于模块 B,模块 B 依赖于模块 C。模块 A 可以访问模块 B 的类,模块 B 可以访问模块 C 的类。除非满足以下条件,模块 A 将无法访问模块 C 的类:

  • 模块 A 声明了对模块 C 的显性依赖关系,或
  • 模块 B 导出它对模块 C 的依赖关系。

全局模块

全局模块是 JBoss EAP 为每个应用程序作为依赖关系提供的模块。通过加入 JBoss EAP 的全局模块列表,任何模块都可以称为全局的。它不需要修改模块。

详情请参考定义全局模块章节。

6.3. 创建自定义模块

通过添加自定义模块,可以使资源对运行在 JBoss EAP 上的部署有效。您可以手工创建模块使用管理 CLI 创建模块

在创建模块后,为了使资源对应用程序有效,您需要作为依赖关系添加模块

手工创建自定义模块

您可以通过以下步骤手工创建自定义模块。

  1. EAP_HOME/modules/ 目录中创建适当的目录结构。

    示例:创建 MySQL JDBC 驱动目录结构

    $ cd EAP_HOME/modules/
    $ mkdir -p com/mysql/main

  2. 把 JAR 文件或其它所需资源复制到 main/ 子目录中。

    示例:复制 MySQL JDBC 驱动 JAR

    $ cp /path/to/mysql-connector-java-5.1.36-bin.jar EAP_HOME/modules/com/mysql/main/

  3. main/ 子目录中创建 module.xml 文件,在文件中指定适当的资源和依赖程序。

    示例:MySQL JDBC 驱动的 module.xml 文件

    <?xml version="1.0" ?>
    <module xmlns="urn:jboss:module:1.1" name="com.mysql">
      <resources>
        <resource-root path="mysql-connector-java-5.1.36-bin.jar"/>
      </resources>
      <dependencies>
        <module name="javax.api"/>
        <module name="javax.transaction.api"/>
      </dependencies>
    </module>

使用管理 CLI 创建自定义模块

You can create a custom module using the module add management CLI command.

重要

Using the module management CLI command to add and remove modules is provided as technology preview only. This command is not appropriate for use in a managed domain or when connecting to the management CLI remotely. Modules should be added and removed manually in a production environment.

  1. 启动 JBoss EAP 服务器。
  2. 启动管理 CLI,但请不要使用 --connect-c 参数来连接运行的实例。

    $ EAP_HOME/bin/jboss-cli.sh
  3. 使用 module add 管理 CLI 命令来添加新的核心模块。

    module add --name=MODULE_NAME --resources=PATH_TO_RESOURCE --dependencies=DEPENDENCIES

关于使用这个命令添加或删除模块的详情,请执行 module --help

添加依赖的模块

为了让您的应用程序能够访问这个模块的资源,需要将模块添加为依赖关系。

  • 关于将模块作为依赖关系添加至所有应用程序的说明,请参考定义全局模块章节。

作为一个示例,以下步骤会添加一个包括了多个属性的 JAR 文件作为一个模块,并定义一个全局模块。然后,一个应用程序就可以加载这些属性。

  1. 将 JAR 文件添加为核心模块。

    module add --name=myprops --resources=/path/to/properties.jar
  2. 将这个模块定义为全局模块,使其为所有部署可用。

    /subsystem=ee:write-attribute(name=global-modules,value=[{name=myprops}]
  3. 应用程序然后可以从 JAR 文件包含的其中一个属性文件里获取属性。

    Thread.currentThread().getContextClassLoader().getResource("my.properties");

6.4. Remove a Custom Module

Custom static modules can be removed manually or by using the management CLI.

Remove a Custom Module Manually

Before manually removing a module, ensure that it is not required by deployed applications or elsewhere in the server configuration, such as by a datasource.

To remove a custom module, remove the module’s directory under EAP_HOME/modules/, which includes its module.xml file and associated JAR files or other resources. For example, remove the EAP_HOME/modules/com/mysql/main/ directory to remove a custom MySQL JDBC driver module in the main slot.

Remove a Custom Module Using the Management CLI

You can remove a custom module using the module remove management CLI command.

重要

Using the module management CLI command to add and remove modules is provided as technology preview only. This command is not appropriate for use in a managed domain or when connecting to the management CLI remotely. Modules should be added and removed manually in a production environment.

  1. 启动 JBoss EAP 服务器。
  2. 启动管理 CLI,但请不要使用 --connect-c 参数来连接运行的实例。

    $ EAP_HOME/bin/jboss-cli.sh
  3. Use the module remove management CLI command to remove the custom module.

    module remove --name=MODULE_NAME
    • Use the --slot argument if the module to remove is in a slot other than main.

    Example: Remove a MySQL Module

    module remove --name=com.mysql

关于使用这个命令添加或删除模块的详情,请执行 module --help

6.5. 定义全局模块

您可以为 JBoss EAP 定义一个全局模块列表,它将模块作为依赖关系添加至所有的部署。

注意

您必须知道将被配置为全局模块的模块名。关于部署里的模块命名规则,请参考动态模块命名章节。

使用下列管理 CLI 命令来定义全局模块列表。

/subsystem=ee:write-attribute(name=global-modules,value=[{name=MODULE_NAME_1},{name=MODULE_NAME_2}]

全局模块可以通过管理控制台的 Configuration 标签页里的 EE 子系统并选择 Global Modules 来添加或删除。

6.6. 配置子部署隔离

一个 Enterprise Archive(EAR)中的每个子部署都是一个带有自己的类加载程序的动态模块。子部署都会在父模块中有一个隐性依赖关系,从而使它们可以访问 EAR/lib 中的类。在默认情况下,子部署可以访问那个 EAR 中的其它子部署的资源。

如果您不允许子部署访问属于其他子部署的类,在 JBoss EAP 里您可以启用严格的子部署隔离。这个设置将影响到所有的部署。

为所有部署启用子部署隔离

子部署隔离(subdeployment isolation)功能可以通过 ee 子系统的管理控制台或管理 CLI 启用或禁用。在默认情况下,子部署隔离被设置为“false(禁用)”,这将允许子部署访问一个 EAR 部署中的其它子部署的资源。

使用下列管理 CLI 来启用 EAR 子部署隔离。

/subsystem=ee:write-attribute(name=ear-subdeployments-isolated,value=true)

EAR 里的子部署不再可以访问其他子部署里的资源。

6.7. 定义外部的 JBoss EAP 模块目录

JBoss EAP 模块的默认目录是 EAP_HOME/modules。您可以用 JBOSS_MODULEPATH 为 JBoss EAP 模块指定不同的目录。以下步骤在 JBoss EAP 启动配置文件里设置这个变量。

注意

您也可以把 JBOSS_MODULEPATH 设置为一个环境变量,而不是在 JBoss EAP 启动配置文件里设置它。

  1. 编辑启动配置文件。

    • 当以独立服务器运行时,编辑 EAP_HOME/bin/standalone.conf 文件(或对于 Windows Server,编辑 standalone.conf.bat)。
    • 当运行在受管域里时,编辑 EAP_HOME/bin/domain.conf 文件(或对于 Windows Server,编辑 domain.conf.bat)。
  2. 设置 JBOSS_MODULEPATH 变量,例如:

    JBOSS_MODULEPATH="/path/to/modules/directory/"

    要指定一个目录列表,请使用冒号(:)来分隔目录列表。

    注意

    对于 Windows Server,使用下列语法来设置 JBOSS_MODULEPATH 变量:

    set "JBOSS_MODULEPATH /path/to/modules/directory/"

    要指定一个目录列表,请使用分号(;)来分隔目录列表。

6.8. 动态模块的命名规则

JBoss EAP 将所有部署加载为模块,它们是根据下列规则进行命名的。

  • WAR 和 JAR 文件的部署使用下列格式来命名:

    deployment.DEPLOYMENT_NAME

    例如,inventory.warstore.jar 的模块名分别是deployment.inventory.wardeployment.store.jar

  • EAR 里的子部署用下列格式来命名:

    deployment.EAR_NAME.SUBDEPLOYMENT_NAME

    例如, EAR accounts.ear 里的 reports.war 的模块名是 deployment.accounts.ear.reports.war