JSR 294经常被描述为模块化系统,但这是错误的。事实上,JSR 294提供的是一系列语言和虚拟机的特性,这些特性对OSGi和Jigsaw这些模块化系统有好处——就好像JSR 292提供的虚拟机特性对JRuby和Jython这些动态语言运行时有好处一样(参考阅读:JVM动态语言支持详解。这是Java 7当中加入的一个重要新功能。感兴趣的读者可以浏览51CTO的Java 7专题)。JSR 292标准化的是链接协议,而非链接行为;JSR 294标准化的是模块的可用性(accessibility),而非模块的界限。
就职于IBM的OSGi CTO,BJ Hargrave曾在2009年三月撰文一篇,介绍了模块化的两个重要概念:可视性(visibility)和可用性(accessibility)的不同。以下为全文:
可视性指的是一个类型是否能看到另一个类型。在JLS(Java Language Specification,Java语言规范)当中,这个概念被当作可观察性(observability)被讨论。在JVMS(Java Virtual Machine Specification,Java虚拟机规范)当中,这个概念与类加载相关。基本上,这个概念描述出来就是:T类型对于S类型而言是否是可视的。在编译时,这个概念指编译器在编译S类型的时候能否为类型T定位。在运行时,这个概念指S类型的类加载器能否加载(直接的或通过委托的)T类型。一个类型也可以通过反射建立可视性。即使S类型的类加载器无法加载T类型,S类型仍然可以在反射时接触到T类型。比如说,一个对象是S类型通过I类接口直接引用的,但它仍然可能是T类型的对象。
可用性指的是一个类型能否使用(access)另一个类型,或另一个类型的成员。这在JLS和JVMS当中都进行过探讨。很多人通过public,protected和private关键字了解到可用性这个概念。
可视性和可用性的概念有重合,但他们是两个不同的概念;并且在我们实现Java模块化的时候,这两个概念必须要分开理解。首先,要使用一个类型,这个类型必须是可视的。然后才牵扯到这个类型或这个类型相关联的成员是否可用的问题。一个类型可以同时是可视的而不可用的。
OSGi通过严格限制可视性实现模块化。这很有道理,因为OSGi建立在类加载器(ClassLoader)模型之上。所以,如果bundle A导入了bundle B导出的包(packages),那么bundle B当中的其他所有包对于bundle A的类加载器而言都是不可视的。但是,如果bundle B注册了一个服务,而实现这个服务的类型对于bundle A的类加载器而言是不可视的,bundle A仍然能够得到这个服务的类对象(通过service.getClass())。即使那个类当中包含服务接口之外的公共方法(public methods),bundle A仍然可以呼叫这些方法。
往Java当中添加一个module可用性关键字的意义在于,当一个类型尝试透过模块之间的界限使用另一个类型时,我们可以让虚拟机强制对可用性进行控制。比如说,在上述例子当中,如果bundle A和B分属不同的模块(按照JSR 294的定义),那么bundle A将不能使用bundle B的服务实施类的模块可用性成员。这样一来,bundle的编写者就拥有了更多控制和封装的能力。
困难在于模块之间界限的定义。这有关虚拟机是否强制控制可用性,而JSR 294工作组当中对这一块仍然在不停地讨论。相关问题还有是否让Java编译器自身理解模块界限来进行可视性的处理,以及新的模块可用性关键字。javac目前有一个简化的可视性视图:-classpath/-sourcepath。这和OSGi这样的模块化系统当中的严格的可视性完全不是一个等级的。让Java编译器拥有可视性,从而更好的满足运行时的需求,这是个主要的挑战。