Movatterモバイル変換


[0]ホーム

URL:


The State of the Module System

Initial Edition

Mark Reinhold

2015/9/8 17:12 -0700 [72deb34d31df]
Copyright © 2015 Oracle and/or its affiliates · All Rights Reserved
This version:http://openjdk.java.net/projects/jigsaw/spec/sotms/2015-09-08
Latest version:http://openjdk.java.net/projects/jigsaw/spec/sotms/
Comments to:jpms-spec-comments@openjdk.java.net

This is an informal overview of enhancements to the JavaSE Platform prototyped inProject Jigsaw and proposed as thestarting point forJSR 376. A related documentdescribes enhancements to JDK-specific tools and APIs, which are outsidethe scope of the JSR.

As describedin the JSR, the specific goals of themodule system are to provide

These features will benefit application developers, library developers,and implementors of the Java SE Platform itself directly and, also,indirectly, since they will enable a scalable platform, greater platformintegrity, and improved performance.

Modules

To achieve the above goals we treat modules as a fundamental new kind ofJava program component. Amodule is a named, self-describingcollection of code and data. Its code is organized as a set of packagescontaining types,i.e., Java classes and interfaces; its data includesresources and other kinds of static information.

To control how its code refers to types in other modules, a moduledeclares which other modules itrequires in order to be compiled andrun. To control how code in other modules refers to types in itspackages, a module declares which of those packages itexports.

As we shall see below, the module system locates required modules and,unlike the class-path mechanism, ensures that code in a module can onlyrefer to types in the modules upon which it depends. The access-controlmechanisms of the Java language and the Java virtual machine prevent codefrom accessing types in packages that are not exported by their definingmodules.

To reduce coupling, a module can declare that ituses a serviceinterface whose implementation isprovided at run time by some module.The module system extends the existing reflection APIs to expose arepresentation of modules at both compile time and run time. To supportexisting applications it retains the platform’s present hierarchy ofbuilt-in class loaders and, also, relates types on the class path tomodules.

For advanced usage we define a means for a module to export a package tosome specific modules rather than all modules, and a reflective API whichframeworks can use to ensure that dynamically-loaded classes areaccessible to the frameworks themselves. For sophisticated applicationswhich act as containers for other, hosted applications we provide theability to control which modules are used by hosted applications.

Module declarations

A module’s self-description is expressed in itsmodule declaration, anew construct of the Java programming language. The simplest possiblemodule declaration merely specifies the name of its module:

module com.foo.bar { }

One or morerequires clauses can be added to declare that the moduledepends, by name, upon some other modules, at both compile time and runtime:

module com.foo.bar {requires com.foo.baz;}

Finally,exports clauses can be added to declare that the module makesall, and only, the public types in specific packages available for use byother modules:

module com.foo.bar {    requires com.foo.baz;exports com.foo.bar.alpha;exports com.foo.bar.beta;}

If a module’s declaration contains noexports clauses then it will notexport any types at all to any other modules.

The source code for a module declaration is, by convention, placed in afile namedmodule-info.java at the root of the module’s source-filehierarchy. The source files for thecom.foo.bar module,e.g., mightinclude:

module-info.javacom/foo/bar/alpha/AlphaFactory.javacom/foo/bar/alpha/Alpha.java...

A module declaration is compiled, by convention, into a file namedmodule-info.class, placed similarly in the class-file output directory.

Module names, like package names, must not conflict. The recommended wayto name a module is to use the reverse-domain-name pattern that has longbeen recommended for naming packages. The name of a module will,therefore, often be a prefix of the names of its exported packages, butthis relationship is not mandatory.

A module’s declaration does not include a version string, nor constraintsupon the version strings of the modules upon which it depends. This isintentional: It isnot a goal of the module system to solve theversion-selection problem, which is best left to build tools andcontainer applications.

Module declarations are part of the Java programming language, ratherthan a language or notation of their own, for several reasons. One ofthe most important is that module information must be available at bothcompile time and run time in order to achievefidelity acrossphases,i.e., to ensure that the compile-time environmentof a source file is identical to the run-time environment of theresulting class file. This, in turn, allows many kinds of errors to beprevented or, at least, reported earlier—at compile time—when theyare easier to diagnose and repair.

Expressing module declarations in a source file which is compiled, alongwith the other source files in a module, into a class file forconsumption by the Java virtual machine is the natural way in which toestablish fidelity. This approach will be immediately familiar todevelopers, and not difficult for IDEs and build tools to support. Buildtools, in particular, can synthesize module declarations from informationalready available in project descriptions.

Module artifacts

Existing tools can already create, manipulate, and consume JAR files, sofor ease of adoption and migration we definemodular JAR files. Amodular JAR file is like an ordinary JAR file in all possible ways,except that it also includes amodule-info.class file in its rootdirectory. A modular JAR file for the abovecom.foo.bar module,e.g., might have the following content:

META-INF/META-INF/MANIFEST.MFmodule-info.classcom/foo/bar/alpha/AlphaFactory.classcom/foo/bar/alpha/Alpha.class...

A modular JAR file can be used as a module, in which case itsmodule-info.class file is taken to contain the module’s declaration.It can, alternatively, be placed on the ordinary class path, in whichcase itsmodule-info.class file is ignored. Modular JAR files allowthe maintainer of a library to ship a single artifact that will work bothas a module, on Java 9 and later, and as a regular JAR file on the classpath, on all releases. We expect that implementations of Java SE 9 whichinclude ajar tool will enhance that tool to make it easy to createmodular JAR files.

For the purpose of modularizing the Java SE Platform’s referenceimplementation, the JDK, we will introduce a new artifact format thatgoes beyond JAR files to accommodate native code, configuration files,and other kinds of data that do not fit naturally, if at all, into JARfiles. This format leverages another advantage of expressing moduledeclarations in source files and compiling them into class files, namelythat class files are independent of any particular artifact format.Whether this new format, provisionally named “JMOD,” should bestandardized is an open question.

Module descriptors

A final advantage of compiling module declarations into class files isthat class files already have aprecisely-defined and extensibleformat. We can thus considermodule-info.class files in a moregeneral light, asmodule descriptors which include the compiled formsof source-level module declarations but also additional kinds ofinformation recorded in class-file attributes which are inserted afterthe declaration is initially compiled.

An IDE or a build-time packaging tool,e.g., can insert attributescontaining documentary information such as a module’s version, title,description, and license. This information can be read at compile timeand run time via the module system’s reflection facilities for use indocumentation, diagnosis, and debugging. It can also be used bydownstream tools in the construction of OS-specific package artifacts. Aspecific set of attributes will be standardized but, since the Javaclass-file format is extensible, other tools and frameworks will be ableto define additional attributes as needed. Non-standard attributes willhave no effect upon the behavior of the module system itself.

Platform modules

The Java SE 9 Platform Specification, rather than this JSR, will dividethe platform into a set of modules. An implementation of the Java SE 9Platform can contain all of the platform modules or, possibly, just someof them.

The only module known specifically to the module system, in any case, isthe base module, which is namedjava.base. The base module defines andexports all of the platform’s core packages, including the module systemitself:

module java.base {    exports java.io;    exports java.lang;    exports java.lang.annotation;    exports java.lang.invoke;    exportsjava.lang.module;    exports java.lang.ref;    exports java.lang.reflect;    exports java.math;    exports java.net;    ...}

The base module is always present. Every other module depends implicitlyupon the base module, while the base module depends upon no othermodules.

The remaining platform modules will share the “java.” name prefix andare likely to include,e.g.,java.sql for database connectivity,java.xml for XML processing, andjava.logging for logging. Modulesthat are not defined in the Java SE 9 Platform Specification butinstead specific to the JDK will, by convention, share the “jdk.” nameprefix.

Module graphs

Individual modules can be built-in to the compile-time or run-timeenvironment, or else defined in artifacts, but in order to make use ofthem in either phase we must first figure out how they relate to eachother.

Suppose we have an application that uses the abovecom.foo.bar moduleand also the platform’sjava.sql module. The module that contains thecore of the application is declared as follows:

module com.foo.app {    requires com.foo.bar;    requires java.sql;}

Given this initial application module, the module systemresolves thedependences expressed in itsrequires clauses by locating additionalmodules to fulfill those dependences, and then resolves the dependencesof those modules, and so forth, until every dependence of every module isfulfilled. The result of this transitive-closure computation is amodule graph which, for each module with a dependence that is fulfilledby some other module, contains a directed edge from the first module tothe second.

To construct a module graph for thecom.foo.app module we inspect thedeclaration of thejava.sql module, which is:

module java.sql {    requires java.logging;    requires java.xml;    exports java.sql;    exports javax.sql;    exports javax.transaction.xa;}

We also inspect the declaration of thecom.foo.bar module, alreadyshown above, and also those of thecom.foo.baz,java.logging, andjava.xml modules; for brevity, these last three are not shown heresince they do not declare dependences upon any other modules.

Based upon all of these module declarations, the graph computed for thecom.foo.app module contains the following nodes and edges:

Module graph

In this figure the dark blue lines represent explicit dependencerelationships, as expressed inrequires clauses, while the light bluelines represent the implicit dependences of every module upon the basemodule.

Readability

When one module depends directly upon another in the module graph thencode in the first module will be able to refer to types in the secondmodule. We therefore say that the first modulereads the second or,equivalently, that the second module isreadable by the first. Thus,in the above graph, thecom.foo.app module reads thecom.foo.bar andjava.sql modules but not thecom.foo.baz,java.xml, orjava.logging modules. Thejava.logging module is readable by thejava.sql module, but no others. (Every module, by definition, readsitself.)

The readability relationships defined in a module graph are the basis ofreliable configuration: The module system ensures that every dependenceis fulfilled by precisely one other module, that no two modules read eachother, that every module reads at most one module defining a givenpackage, and that modules defining identically-named packages do notinterfere with each other.

Reliable configuration is not just more reliable; it can also be faster.When code in a module refers to a type in a package then that package isguaranteed to be defined either in that module or in precisely one of themodules read by that module. When looking for the definition of aspecific type there is, therefore, no need to search for it in multiplemodules or, worse, along the entire class path.

Module paths

To resolve a dependence while constructing a module graph the modulesystem can select a module built-in to the compile-time or run-timeenvironment or, alternatively, a module defined in an artifact. In thelatter case the module system locates artifacts on one or moremodulepaths defined by the host system. A module path is a sequence ofdirectories containing module artifacts which are searched, in order, forthe first artifact that defines a suitable module.

Module paths are materially different from class paths, and more robust.The inherent brittleness of the class path is due to the fact that it isa means to locate individual types in all the artifacts on the path,making no distinction amongst the artifacts themselves. This makes itimpossible to tell, in advance, when an artifact is missing. It alsoallows different artifacts to define types in the same packages, even ifthose artifacts represent different versions of the same logical programcomponent, or different components entirely.

A module path, by contrast, is a means to locate whole modules ratherthan individual types. If the module system cannot fulfill a particulardependence with an artifact from a module path, or if it encounters twoartifacts defining modules of the same name but different versions insome directory of a module path, then resolution will fail and thecompiler or virtual machine will report an error and exit.

Once the module system finds an artifact on a module path that fulfills aparticular dependence then no further artifacts are considered and, afterconstructing the module graph, the module system ensures that relatedmodules do not define types in the same packages.

The modules built-in to the compile-time or run-time environment,together with those defined by artifacts on module paths, arecollectively referred to as the universe ofobservable modules.

Accessibility

The readability relationships defined in a module graph, combined withtheexports clauses in module declarations, are the basis ofstrongencapsulation: The Java compiler and virtual machine consider the publictypes in a package in one module to beaccessible by code in some othermodule only when the first module is readable by the second module, inthe sense defined above, and the first module exports that package. Thatis, if two typesS andT are defined in different modules, andT ispublic, then code inScan accessT if:

  1. S’s module readsT’s module, and
  2. T’s module exportsT’s package.

A type referenced across module boundaries that is not accessible in thisway is unusable in the same way that a private method or field isunusable: Any attempt to use it will cause an error to be reported by thecompiler, or anIllegalAccessError to be thrown by the Java virtualmachine, or anIllegalAccessException to be thrown by reflectiverun-time APIs. Thus, even when a type is declaredpublic, if itspackage is not exported in the declaration of its module then it willonly be accessible to code in that module.

A method or field referenced across module boundaries is accessible ifits enclosing type is accessible, in this sense, and if the declarationof the member itself also allows access.

To see how strong encapsulation works in the case of the above modulegraph, we label each module with the packages that it exports:

Module graph, with exports

Code in types in thecom.foo.app module can access public typesdeclared in thecom.foo.bar.alpha package becausecom.foo.app dependsupon, and therefore reads, thecom.foo.bar module, and becausecom.foo.bar exports thecom.foo.bar.alpha package. Ifcom.foo.barcontains an internal packagecom.foo.bar.internal then types incom.foo.app cannot access it sincecom.foo.bar does not export thatpackage. Types incom.foo.app cannot refer to any types exported bythecom.foo.baz module sincecom.foo.app does not depend uponcom.foo.baz, and therefore does not read it.

Implied readability

If one module reads another then, in some situations, it should logicallyalso read some other modules.

The platform’sjava.sql module,e.g., depends upon thejava.loggingandjava.xml modules, not only because it contains implementation codethat uses types in those modules but also because it defines exportedtypes whose signatures refer to types in those modules. Thejava.sql.Driver interface, in particular, declares the public method

public Logger getParentLogger();

whereLogger is a type declared in the exportedjava.util.loggingpackage of thejava.logging module.

Suppose that code in thecom.foo.app module invokes this method inorder to acquire a logger and log a message:

String url = ...;Properties props = ...;Driver d = DriverManager.getDriver(url);Connection c = d.connect(url, props);d.getParentLogger().info("Connection acquired");

If thecom.foo.app module is declared as above then this will not work:ThegetParentLogger method is defined in theLogger class, which isin thejava.logging module, which is not read by thecom.foo.appmodule. That class is therefore inaccessible to code in thecom.foo.app module and so the invocation of thegetParentLoggermethod will fail, at both compile time and run time.

One solution to this problem is to hope that every author of every modulethat both depends upon thejava.sql module and contains code thatinvokes thegetParentLogger method remembers also to declare adependence upon thejava.logging module. This approach is unreliable,of course, since it violates the principle of least surprise: If onemodule depends upon a second module then it is natural to expect thatevery type needed to use the first module, even if the type is defined inthe second module, will immediately be accessible to a module thatdepends only upon the first module.

To achieve this we extend module declarations so that one module cangrant readability to additional modules, upon which it depends, to anymodule that depends upon it. Suchimplied readability is expressed byincluding thepublic modifier in arequires clause. The declarationof thejava.sql module actually reads:

module java.sql {    requirespublic java.logging;    requirespublic java.xml;    exports java.sql;    exports javax.sql;    exports javax.transaction.xa;}

Thepublic modifiers mean that any module that depends upon thejava.sql module will read not only thejava.sql module but also thejava.logging andjava.xml modules. The module graph for thecom.foo.app module, shown above, thus contains two additional dark-blueedges, linked by green edges to thejava.sql module since they areimplied by that module:

Module graph, with implied reads

Thecom.foo.app module can now include code that accesses all of thepublic types in the exported packages of thejava.logging andjava.xml modules, even though its declaration does not mention thosemodules.

In general, if one module exports a package containing a type whosesignature refers to a package in a second module then the declaration ofthe first module should include arequires public dependence upon thesecond. This will ensure that other modules that depend upon the firstmodule will automatically be able to read the second module and, hence,access all the types in that module’s exported packages.

Services

The loose coupling of program components via service interfaces andservice providers is a powerful tool in the construction of largesoftware systems. Java has long supported this technique via thejava.util.ServiceLoader class, which is used by the JDK andalso by libraries and applications. This facility locates serviceproviders at run time by searching the class path for service-definitionfiles in theMETA-INF/services resource directory. If a service isprovided by a module on the module path, however, then it will not be onthe class path, so we must consider how to locate providers amongst theset of observable modules and integrate them into a module graph, and howto enhance theServiceLoader class to expose such providers.

Suppose that ourcom.foo.app module uses a MySQL database, and that aMySQL JDBC driver is provided in an observable module which has thedeclaration

module com.mysql.jdbc {    requires java.sql;    requires org.slf4j;    exports com.mysql.jdbc;}

whereorg.slf4j is a logging library used by the driver andcom.mysql.jdbc is the package that contains the implementation of thejava.sql.Driver service interface. (It is not actually necessary toexport the driver package, but that is done here for clarity.)

In order for thejava.sql module to make use of this driver we must addthe driver module to the run-time module graph and resolve itsdependences so that theServiceLoader class can then instantiate thedriver class via reflection, thus:

module graph, with JDBC module

To make these additions to the module graph, however, the module systemmust be able to locate providers within the set of observable modules.It could do this by scanning module artifacts forMETA-INF/servicesresource entries, just as theServiceLoader class does today. That amodule provides an implementation of a particular service, however, is afundamental aspect of that module’s definition, so it is clearer toexpress it in the module’s declaration, with aprovides clause:

module com.mysql.jdbc {    requires java.sql;    requires org.slf4j;    exports com.mysql.jdbc;provides java.sql.Driverwith com.mysql.jdbc.Driver;}

That a module uses a particular service is equally fundamental, so weexpress that in a module declaration with auses clause:

module java.sql {    requires public java.logging;    requires public java.xml;    exports java.sql;    exports javax.sql;    exports javax.transaction.xa;uses java.sql.Driver;}

Now it is very easy to see, simply by reading these modules’declarations, that one of them provides a service which can be used bythe other.

Declaring service-provision and service-use relationships in moduledeclarations has advantages beyond improved clarity. Servicedeclarations of both kinds can be interpreted at compile time to ensurethat the service interface (e.g.,java.sql.Driver) is accessible toboth the providers and the users of a service. Service-providerdeclarations can be further interpreted to ensure that providers (e.g.,com.mysql.jdbc.Driver) actually do implement their declared serviceinterfaces. Service-use declarations can, finally, be interpreted byahead-of-time compilation and linking tools to ensure that observableproviders are appropriately compiled and linked prior to run time.

Reflection

TheServiceLoader class is a kind offramework, which uses reflectionto load and instantiate other classes at run time. Additional examplesof frameworks in the Java SE Platform itself are resource bundles,dynamic proxies, and serialization, and of course there are many externalframework libraries in popular use.

In a modular setting, frameworks often need to be able to inspect and, insome cases, manipulate the module graph at run time. We therefore exposethe graph via the new classModule, in thejava.lang.reflect package,and some related types in a new package,java.lang.module. An instanceof theModule class represents a single module at run time. Every typeis in a module, so everyClass object has an associatedModuleobject, which is returned by the newClass::getModule method.

The essential operations on aModule object are:

package java.lang.reflect;public final class Module {    public String getName();    public ModuleDescriptor getDescriptor();    public ClassLoader getClassLoader();    public boolean canRead(Module source);    public boolean isExported(String packageName);}

whereModuleDescriptor is a class in thejava.lang.module package,instances of which represent module descriptors; thegetClassLoadermethod returns the module’s class loader; thecanRead method tellswhether the module can read the source module; and theisExportedmethod tells whether the module exports the given package.

Thejava.lang.reflect package is not the only reflection facility inthe platform. Similar additions will be made to the compile-timejavax.lang.model package in order to support annotation processors anddocumentation tools.

Class loaders

Every type is in a module, and at run time every module has a classloader, but does a class loader load just one module? The module system,in fact, places few restrictions on the relationships between modules andclass loaders. A class loader can load types from one module or frommany modules, so long as the modules do not interfere with each other andthe types in any particular module are loaded by just one loader.

This flexibility is critical to compatibility, since it allows us toretain the platform’s existing hierarchy of built-in class loaders. Thebootstrap and extension class loaders still exist, and are used to loadtypes from platform modules. The application class loader also stillexists, and is used to load types from artifacts found on the modulepath.

This flexibility will also make it easier to modularize existingapplications which already construct sophisticated hierarchies or evengraphs of custom class loaders, since such loaders can be upgraded toload types in modules without necessarily changing their delegationpatterns.

Unnamed modules

Every type is in a module, but not every type is defined in a named,observable module,i.e., by a built-in module or an artifact on themodule path. For compatibility we must continue to support the classpath, which means that the application class loader must attempt to loadtypes from the class path when those types are in packages not defined inany known module. If every type is in a module, however, then in whatmodule is a type loaded from the class path?

We answer this question with the concept ofunnamed modules. Everyclass loader has a unique unnamed module, which is returned by the newClassLoader::getUnnamedModule method. If a class loader loads a typethat is not defined in a named module then that type is considered to bein the loader’s unnamed module,i.e., thegetModule method of thetype’sClass object will return the loader’s unnamed module. Unnamedmodules are, at a high level, akin to the existing concept ofunnamedpackages.

An unnamed module reads every other module, and it exports all of itspackages to every other module. Any type loaded from the class path bythe application class loader will, therefore, be able to access theexported types of all other modules, which by default will include all ofthe built-in platform modules. An existing class-path applicationrunning on a full Java SE 9 run-time environment will, therefore, workexactly as it does today on Java SE 8, so long as it only uses standardJava SE APIs.

The class path remains supported at compile time as well, although atcompile time there is only one unnamed module rather than many.

Advanced topics

The remainder of this document addresses advanced topics which, whileimportant, are unlikely to be of interest to most developers.

Qualified exports

It is occasionally necessary to arrange for some types to be accessibleamongst a set of modules yet remain inaccessible to all other modules.

Code in the JDK’s implementations of the standardjava.sql andjava.xml modules,e.g., makes use of types defined in the internalsun.reflect package, which is in thejava.base module. In order forthis code to access types in thesun.reflect package we could simplyexport that package from thejava.base module:

module java.base {    ...    exports sun.reflect;}

This would, however, make every type in thesun.reflect packageaccessible to every module, since every module readsjava.base, andthat is undesirable because the some of the classes in that packagedefine privileged, security-sensitive methods.

We therefore extend module declarations to allow a package to be exportedto one or more specifically-named modules, and to no others. Thedeclaration of thejava.base module actually exports thesun.reflectpackage only to a specific set of JDK modules:

module java.base {    ...exports sun.reflectto        java.corba,        java.logging,        java.sql,        java.sql.rowset,        jdk.scripting.nashorn;}

Thesequalified exports can be visualized in a module graph by addinganother type of edge, here colored gold, from packages to the specificmodules to which they are exported:

Module graph, with qualified exports

The accessibility rules stated earlier are refined as follows: If twotypesS andT are defined in different modules, andT ispublic,then code inS can accessT if:

  1. S’s module readsT’s module, and
  2. T’s module exportsT’s package,either directly toS’s module or to all modules.

We also extend the reflectiveModule class with a method to tellwhether a package is exported to a specific module, rather than to allmodules:

public final class Module {    ....    public boolean isExported(String packageName, Module target);}

Qualified exports can inadvertently make internal types accessible tomodules other than those intended, so they must be used with care. Anadversary could,e.g., name a modulejava.corba in order to accesstypes in thesun.reflect package. To prevent this we can analyze a setof related modules at build time and record, in each module’s descriptor,hashes of the content of the modules that are allowed to depend upon itand use its qualified exports. During resolution we verify, for anymodule named in a qualified export of some other module, that the hash ofits content matches the hash recorded for that module name in the secondmodule. Qualified exports are safe to use in an untrusted environment solong as the modules that declare and use them are tied together in thisway.

Increasing readability

If a framework uses reflection to load and instantiate other classes atrun time then it needs to be able to ensure that those classes areaccessible to the framework code itself.

The platform’sstreaming XML parser,e.g.,loads andinstantiates the implementation of theXMLInputFactory service named by the system propertyjavax.xml.stream.XMLInputFactory, if defined, in preference to anyprovider discoverable via theServiceLoader class. Ignoring exceptionhandling and security checks the code reads, roughly:

String providerName    = System.getProperty("javax.xml.stream.XMLInputFactory");if (providerName != null) {    Class providerClass = Class.forName(providerName, false,                                        Thread.getContextClassLoader());    Object ob = providerClass.newInstance();    return (XMLInputFactory)ob;}// Otherwise use ServiceLoader...

The invocation ofClass::forName will work so long as the packagecontaining the provider class is known to the context class loader, sincethe module system does not affect the visibility of types at run time.

The invocation of the provider class’s constructor via the reflectivenewInstance method, however, will not work: The provider might beloaded from the class path, in which case it will be in the applicationclass loader’s unnamed module, or it might be in some named module, butin either case the framework itself is in thejava.xml module. Thatmodule only depends upon, and therefore reads, the base module, and so aprovider class in any other module will be inaccessible to the framework.

To make the provider class accessible to the framework we need to makethe provider’s module readable by the framework’s module. To enable thatwe further extend the reflectiveModule class with a method to add areadability edge to the module graph at run time:

public final class Module {    ....    public Module addReads(Module source);}

wheresource is the module that is to be made readable by this module.This method is, essentially, the run-time equivalent of arequiresclause in a module declaration. The above code fragment can then berevised to insert the statement

    XMLInputFactory.class.getModule()        .addReads(providerClass.getModule());

before the invocation of the provider class’snewInstance method. (Theuse ofXMLInputFactory.class in this fragment is not mandatory; anyclass in thejava.xml module will do, since we just need to get areference to that module’sModule object.)

Layers

Sophisticated applications with plug-in or container architectures suchas IDEs, test harnesses, and application servers can use dynamic classloading and the reflective module-system API, thus far described, to loadand run hosted applications that consist of one or more modules. In suchsettings, however, two additional kinds of flexibility are oftenrequired:

To support this kind of configuration flexibility we introducelayersof modules. A layer encapsulates a module graph and a mapping from eachmodule in that graph to a class loader. Theboot layer is created bythe Java virtual machine at startup by resolving the application’sinitial module against the observable modules built-in to the run-timeenvironment and also against those found on the module path, as describedearlier.

Most applications, and certainly all existing applications, will neveruse a layer other than the boot layer. A container application, however,can create a new layer for a hosted application on top of an existinglayer by resolving that application’s initial module against a differentuniverse of observable modules. Such a universe can contain alternateversions of upgradeable platform modules and other, non-platform modulesalready present in the lower layer; the resolver will give thesealternate modules priority. Such a universe can also contain differentservice providers than those already discovered in the lower layer; theServiceLoader class will load and return these providers before itreturns providers from the lower layer.

Layers can be stacked: A new layer can be built on top of the boot layer,and then another layer can be built on top of that. As a result of thenormal resolution process the modules in a given layer can read modulesin that layer or in any lower layer. A layer’s module graph can hence beconsidered to include, by reference, the module graphs of every layerbelow it.

Summary

The module system described here has many facets, but most developerswill only need to use some of them on a regular basis. We expect thebasic concepts of module declarations, modular JAR files, module graphs,module paths, and unnamed modules to become reasonably familiar to mostJava developers in the coming years. The more advanced features ofqualified exports, increasing readability, and layers will, by contrast,be needed by relatively few.

Acknowledgements

This document includes contributions from Alan Bateman, Alex Buckley,Mandy Chung, Jonathan Gibbons, Chris Hegarty, Karen Kinnear, and PaulSandoz.


[8]ページ先頭

©2009-2025 Movatter.jp