`
YiSingQ
  • 浏览: 88382 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

模块化Java简介

阅读更多
    在过去几年,Java模块化一直是一个活跃的话题。从JSR 277(现已废止)到JSR 291,模块化看起来是Java进化过程中的必经一环。即便是基于JVM的未来语言,比如Scala,也考虑了模块化的问题。本文是关于模块化Java系列文章中的第一篇,讨论模块化的含义,以及为什么要关注它。

什么是模块化?
模块化是个一般概念,这一概念也适用于软件开发,可以让软件按模块单独开发,各模块通常都用一个标准化的接口来进行通信。实际上,除了规模大小有区别外,面向对象语言中对象之间的关注点分离与模块化的概念基本一致。通常,把系统划分外多个模块有助于将耦合减至最低,让代码维护更加简单。

Java语言并不是按照模块化思想设计的(除了package,按照Java语言规范introduction一 节的介绍,package类似于Modula-3模块),但是在Java社区依然有很多实际存在的模块。任何一个Java类库实际上都是一个模块,无论其 是Log4J、Hibernate还是Tomcat。通常,开源和非开源的应用都会依赖于一个或多个外部类库,而这种依赖关系又有可能传递到其他类库上。

类库也是模块
类库毫无疑问也是模块。对于类库来讲,可能没有一个单一接口与之通信,但往往却有‘public’ API(可能被用到)和‘private’ package(文档中说明了其用途)。此外,它们也有自己依赖的类库(比如JMX或JMS)。这将引起自动依赖管理器引入许多并非必须的类库:以Log4J-1.2.15为例,引入了超过10个依赖类库(包括javax.mail和javax.jms),尽管这些类库中有不少对于使用Log4J的程序来说根本不需要。

某些情况下,一个模块的依赖可以是可选的;换句话说,该模块可能有一个功能子集缺少依赖。在上面的例子中,如果JMS没有出现在运行时 classpath中,那么通过JMS记录日志的功能将不可用,但是其他功能还是可以使用的。(Java通过使用延迟链接——deferred linking来达到这一目的:直到要访问一个类时才需要其出现,缺少的依赖可以通过ClassNotFoundException来处理。其他一些平台的弱链接——weak linking概念也是做类似的运行时检查。)

通常,模块都附带一个版本号。许多开源项目生成的发行版都是以类似log4j-1.2.15.jar的方式命名的。这样开发者就可以在运行时通过手动方式来检测特定开源类库的版本。可是,程序编译的时候可能使用了另一个不同版本的类库:假定编译时用log4j-1.2.3.jar而运行时用log4j-1.2.15.jar,程序在行为上依然能够保持兼容。即使升级到下一个小版本,仍然是兼容的(这就是为什么log4j 1.3 的问题会导致一个新分支2.0产生,以表示兼容性被打破)。所有这些都是基于惯例而非运行时已知约束。

模块化何时能派上用场?
作为一般概念,模块化有助于将应用分解为不同的部件,各个部件可以单独测试(和开发)。正如上面所提到的,大多数类库都是模块。那么,对于那些生产类库提 供给别人使用的人来说,模块化是一个非常重要的概念。通常,依赖信息是在构建工具(maven pom 或 ivy-module)里进行编码并被明确记录在类库使用文档中的。另外,高层类库开发过程中需要修改较低层级类库bug,以提供更好支持的情况并不少 见,即便低层类库的最新版本已经对bug进行了修正。(可是有时候这种情况可能会导致出现一些微妙的问题。)

如果一个类库是提供给他人使用的,那么它就已经是一个模块了。但是世上鲜有“Hello World”这样的类库,也鲜有“Hello World”这样的模块。只有当应用足够大时(或者是用一个模块化构建系统进行构建时),把应用划分为不同部件的概念就派上用场了。

模块化的好处之一是便于测试。一个小模块(具有定义良好的API)通常比应用整体更好测试。在GUI应用中尤其如此,GUI自身可能不好测试,但是其调用的代码却是可测试的。

模块化的另一个好处是便于进化。尽管系统整体有一个版本号,但实际上,其下有多个模块及相应版本(不论开源与否,总有一些类库——甚至是Java版本—— 是系统所依赖的)。这样,每个模块都可以自己的方式自由地进化。某些模块进化得快些,另一些则会长期保持稳定(例如,Eclipse 3.5 的org.eclipse.core.boot从2008年2月以来一直没有改变过)。

模块化也可给项目管理带来方便。如果一个模块公布的API可供其他模块预先使用,那么各个模块就可以由不同的团队分别开发。这在大型项目中必定会发生,各个项目子团队可以负责不同模块的交付。

最后,将一个应用程序模块化,可以帮助识别正在使用依赖类库的哪个版本,以便协调大型项目中的类库依赖。

运行时与编译时
无论在编译时还是运行时,Java的classpath都是扁平的。换句话说,应用程序可以看到classpath上的所有类,而不管其顺序如何(如果没 有重复,是这样;否则,总是找最前面的)。这就使Java动态链接成为可能:一个处于classpath前面的已装载类,不需要解析其所引用的可能处于 classpath后面的那些类,直到确实需要他们为止。

如果所使用的接口实现到运行时才能清楚,通常使用这种方法。例如,一个SQL工具可以依赖普通JDBC包来编译,而运行时(可以有附加配置信息)可以实例化适当的JDBC驱动。这通常是在运行时将类名(实现了预定义的工厂接口或抽象类)提供给Class.forName查找来实现。如果指定的类不存在(或者由于其他原因不能加载),则会产生一个错误。

因此,模块的编译时classpath可能会与运行时classpath有些微妙的差别。此外,每个模块通常都是独立编译的(模块A可能是用模块C 1.1 来编译的,而模块B则可能是用模块C 1.2 来编译的),而另一方面,在运行时则是使用单一的路径(在本例中,即可能是模块C的1.1版本,也可能是1.2版本)。这就会导致依赖地狱(Dependency Hell),特别当它是这些依赖传递的末尾时更是这样。不过,像Maven和Ivy这样的构建系统可以让模块化特性对开发者是可见的,甚至对最终用户也是可见的。

Java有一个非常好的底层特性,叫做ClassLoader, 它可以让运行时路径分得更开。通常情况下,所有类都是由系统ClassLoader装载的;可是有些系统使用不同的ClassLoader将其运行时空间 进行了划分。Tomacat(或者其他Servlet引擎)就是一个很好的例子,每个Web应用都有一个ClassLoader。这样Web应用就不必去 管(无论有意与否)在同一JVM中其他Web应用所定义的类。

这种方式下,每个Web应用都用自己的ClassLoader装载类,这样一个(本地)Web应用实现装载的类不会与其他Web应用实现相冲突。但这就要 求对任何ClassLoader链,类空间都是一致的;这意味着在同一时刻,你的VM可以同时从两个不同的Classloader中各自装载一个Util.class, 只要这两个ClassLoader互相不可见。(这也是为什么Servlet引擎具有无需重启即可重新部署的能力;扔掉了一个ClassLoader,你 也就扔掉了其引用类,让老版本符合垃圾回收的条件——然后让Servlet引擎创建一个新的ClassLoader并在运行时中重新部署应用类的新版本。)

再谈模块
构建一个模块化系统实际上是把系统划分成(有可能)可重用模块的过程,并使模块间耦合最小化。同时,其也是一个减少模块需求耦合的过程:例如,Eclipse IDE许多plugin对GUI和非GUI组件(如jdt.ui和jdt.core)的依赖是分开的,这样就可以在IDE环境之外使用这些非GUI模块(headless builds、分析及错误检查等等)。

除了作为整体的rt.jar之外,任何其他系统都可以被分解为不同的模块。问题是这么做是否值得?毕竟,从头构建一个模块化系统比起把一个单模块系统分割成多个模块要容易得多。

之所以这样,原因之一是跨越模块边界的类泄漏。例如,java.beans包逻辑上不应该依赖于任何GUI代码;可是Beans.instantiate()所使用的java.beans.AppletInitializer引用了Applet,这必然导致对整个AWT的依赖。因此从技术上讲java.beans有依赖于AWT的选项,尽管常识告诉我们不应该有。如果核心java类库从一开始就采用了模块化方法来构建,那么这种错误早在API公布之前就发现了。

有些情况下,一个模块看上去不能再被划分成子模块了。可是,有时候相关功能保持在同一个模块中是为了便于组织,当需要的时候还可以再进一步分解。例如,对重构的支持起初是Eclipse JDT的一部分,现在被抽出为一个模块,以便其他语言(如CDT)利用其重构能力。

Plugins
许多系统都是通过plugin概念进行扩展的。在这种情况下,宿主系统定义了一套plugin必须遵循的API及plugin注入方式。许多应用(如Web浏览器、IDE及构建工具)通常都是通过提供带有适当API的插件来对应用进行定制的。

有时候这些plugin受到限制或只有一些普通操作(音频或视频解码),但是组织起来效果也非常不错(例如,IDE的众多plugin)。有时候,这些 plugin可以提供自己的plugin,以便进一步定制行为,使得系统具有更高可定制性。(可是,增加这些中间层级会使系统难以理解。)

这种plugin API成为各个plugin必须遵守的契约的一部分。这些plugin自己也是模块,也面临依赖链和版本问题。由于(特定)plugin API演化的复杂性,因此plugin自己也面临这一问题(必须维持向后兼容性)。

Netscape plugin API成功的原因之一是其简单性:只需实现少量的函数。只要宿主浏览器用适当的MIME类型将输入重定向,plugin就可以处理其他事情。可是,更复杂的应用(如IDE)通常需要更紧密集成各个模块,因此需要一个更复杂的API来推动。

Java模块化的当前状态
目前,Java领域存在许多模块化系统和plugin体系。IDE是名气最大的,IntelliJ、NetBeans和Eclipse都提供了其自己的 plugin系统作为其定制途径。而且,构建系统(Ant、Maven)甚至终端用户应用(Lotus Notes、Mac AppleScript应用)都有能够扩展应用或系统核心功能的概念。

OSGi是Java领域里无可辩驳的最成熟的模块系统,它与Java几乎是如影相随,最早出现于JSR 8,但是最新规范是JSR 291。 OSGi在JAR的MANIFEST.MF文件中定义了额外的元数据,用来指明每个包所要求的依赖。这就让模块能够(在运行时)检查其依赖是否满足要求, 另外,可以让每个模块有自己的私有 classpath(因为每个模块都有一个ClassLoader)。这可以让dependency hell尽早被发现,但是不能完全避免。和JDBC一样,OSGi也是规范(目前是4.2版),有多个开源(及商业)实现。因为模块不需要依赖任何OSGi的特定代码,许多开源类库现在都将其元信息嵌入到manifest中,以便OSGi运行时使用。有些程序包没有这么做,也可以用bnd这样的工具,它可以处理一个已有的JAR文件并为其产生合适的默认元信息。自2004年Eclipse 3.0 从专有plugin系统切换到OSGi之后,许多其他专有内核系统(JBoss、WebSphere、Weblogic)也都随之将其运行时转向基于OSGi内核。

最近创建的Jigsaw项目是为了模块化JDK自身。尽管其是JDK内部的一部分,并且很可能在其他SE 7 实现中不被支持,但是在该JDK之外使用Jigsaw并无限制。尽管仍在开发当中,Jigsaw还很可能成为前面提到的JSR 294的参考实现。最低要求SE 7(加上目前还没有Java 7的事实)说明了Jigsaw仍在开发中,而且运行在Java 6或更低版本上的系统基本上是用不上了。

为了鼓励采用标准模块化格式,JSR 294专家组目前正在讨论简单模块系统提议:在这一提议中,Java类库(来自Maven库及Apache.org)的开发者能够提供让Jigsaw和OSGi系统都能使用的元信息。结合对Java语言的微小变动(最值得关注的是增加的module关键字),这一信息可以在编译时由高级编译器产生。运行时系统(如Jigsaw或OSGi)可以使用这些信息来校验所安装的模块及其依赖。

总结
本文讨论了模块化的一般概念,以及在Java系统中是如何实现的。由于编译时和运行时路径可能不同,有可能会产生不一致的类库需求,从而导致依赖地狱。然 而,plugin API允许装载多种代码,但其必须遵循宿主的依赖处理规则,这又增加了发生不一致的可能性。为了防止这种情况出现,像OSGi这样的运行时模块化系统可以 在决定应用是否能被正确启动之前就校验各项要求,而不是在运行时不知不觉发生错误。

最后,有人在正在进行中的JSR 294的邮件列表中提出,要为Java语言创建一个模块系统,其可以完全在Java语言规范中被定义,以便Java开发者可以产生带有编码依赖信息的标定过版本的模块,该模块以后可以用于任何模块系统。

查看英文原文:Modular Java: What Is It?。

转载自:InfoQ
分享到:
评论

相关推荐

    java 模块化 开发

    Java 模块化开发 Java 模块化开发是大型 Java 系统的一个重要特征。模块化是指将项目划分为多个模块,以便改进构建过程,但是在运行时却很少考虑划分模块的问题。在 Java 开发中,模块化可以分为静态模块化(static...

    Java9模块化开发核心原则与实践

    Java9是Java语言的一个重大更新,其引入了模块化系统,这一变化对于大型项目和库的管理和维护具有深远影响。本文将深入探讨Java9模块化开发的核心原则与实践,帮助开发者理解和掌握这一新特性。 一、模块化系统:...

    Java 9 模块化编程英文版电子书.pdf

    Jigsaw项目的目标是模块化Java平台本身,并使Java应用程序的开发能够更容易地适应大型项目和微服务架构。 本书的作者Koushik Kothagal是一位拥有丰富经验的Java开发者,他在企业Java和JavaScript技术领域提供免费的...

    Java应用架构设计 模块化模式与OSGi.zip

    Java应用架构设计中,模块化模式与OSGi是两个关键概念,它们对于构建大型、可扩展且易于维护的系统至关重要。模块化模式使得代码组织更加有序,而OSGi(Open Services Gateway Initiative)则是一种实现模块化的动态...

    Java9模块化demo

    Java 9 模块化是 Java 语言发展中的一个重要里程碑,它引入了模块系统,旨在提高软件的可维护性、安全性和性能。这个模块化系统称为 Project Jigsaw,它的核心概念是将大型程序分解为相互独立、清晰定义的模块。在 ...

    JAVA模块化系统实践.pdf

    在模块化Java环境中,每个库或功能都可以作为一个独立的模块存在,例如`java.corba`表示与CORBA相关的模块,而`java.se`则可能代表标准版Java的核心库。 6. **安全与封装**: - JPMS的一个重要特点是提供了更好的...

    Java 9 模块化编程_英文版

    Java 9 模块化编程是Java语言发展中的一个重要里程碑,它引入了模块系统,显著提高了软件的可维护性、可扩展性和可移植性。这一重大改进使得大型项目能够更有序地组织其代码库,降低了依赖冲突的可能性,并优化了...

    java9 模块化讲解文档

    Java 9 是 Java 平台的一个重要版本,它引入了模块化系统,即 Project Jigsaw,这是为了提高Java平台的可维护性、安全性和性能。模块化系统将Java应用程序和库分解为独立的、声明性定义的单元,称为模块,这使得...

    Java应用架构设计模块化模式与OSGi

    Java应用架构设计中,模块化模式与OSGi是两种重要的技术,它们对于构建大型、可扩展且易于维护的系统起着关键作用。本篇将深入探讨这两个概念,以及它们如何协同工作来提升Java应用程序的效率和灵活性。 首先,模块...

    OSGi与Equinox 创建高度模块化的Java系统 第5章 源码

    在学习这个章节的过程中,开发者不仅会掌握OSGi和Equinox的基本概念,还能获得构建和管理高度模块化Java系统的能力。这对于开发大型、复杂的企业级应用,尤其是那些需要频繁迭代和扩展的系统来说,具有非常高的价值...

    图书:Java模块

    《Java模块》是一本深入探讨Java模块化系统的专业图书,旨在帮助读者理解并掌握Java平台的模块化设计。这本书详细阐述了Java 9及其后续版本引入的模块系统(Project Jigsaw),这一重大更新显著改进了Java应用程序的...

    《Java9模块化开发核心原则与实践》书中源码

    Java 9是Java平台的重大更新,引入了模块化系统,这一改变对Java的生态系统产生了深远影响。本书《Java9模块化开发核心原则与实践》由Sander Mak和Paul Bakker共同撰写,旨在深入探讨Java 9的模块化系统,帮助开发者...

    《osgi与equinox创建高度模块化的java系统》第五章源码

    在本章中,我们将深入探讨如何使用OSGi(Open Service Gateway Initiative)框架,特别是Equinox实现高度模块化的Java系统。Equinox是Eclipse基金会提供的一个OSGi实现,它允许开发者构建可热插拔的组件,使得系统...

    Java应用架构设计 模块化模式与OSGi

    Java应用架构设计、 如何进行模块化模式、与OSGi有什么联系?

    java实现模块化 学生选课系统

    在Java编程环境中,构建一个模块化的学生选课系统是一项复杂而重要的任务,它涉及到多个关键技术点。本系统的核心目标是通过JDBC(Java Database Connectivity)与MySQL数据库进行交互,实现数据的存储和检索,以及...

    OSGi与Equinox 创建高度模块化的Java系统 中文扫描版

    OSGi与Equinox 创建高度模块化的Java系统 This first part of the book introduces OSGi and Equinox, Eclipse’s implementation of the OSGi standard. Chapter 1outlines the history and context of OSGi. Its ...

    JAVA 模块开发资源

    【JAVA模块开发资源详解】 在Java开发中,模块化是一种重要的设计原则,它有助于提高代码的可维护性、可重用性和可扩展性。...通过熟练掌握这些知识,开发者能够构建出高效、稳定的模块化Java应用。

    TITAN是一个JAVA模块化开发框架

    **TITAN:Java模块化开发框架详解** TITAN是一个专为Java开发者设计的模块化开发框架,其核心目标是提升开发效率、优化代码结构并实现应用的高可扩展性。通过TITAN,开发者可以轻松地在运行时动态加载、卸载模块,...

    OSGi_Java模块化框架的实现

    ### OSGi——Java模块化框架的实现与进化 #### OSGi概述 OSGi(Open Service Gateway Initiative)是一项面向Java的技术,旨在提供一个动态模型系统来支持Java平台上的模块化开发。随着软件系统的日益复杂,模块化...

Global site tag (gtag.js) - Google Analytics