The main goal of this project is to explore basic features of
modularity
introduced in Java 9
:
- exports
- exports
X
toY
- requires
- requires transitive
- open module
Reference: Java 9 Modularity
-
moduleA, moduleB, moduleC - exploring
exports
,exports X to Y
,requires
,requires transitive
. -
openModule, ordinaryModule, reflection - exporing
open module
and correlatemodularity
withreflection
.
Assume that we analyze module-info.java
under module X
- exports
Y
- exporting packageY
. - exports
Y
toZ
(Qualified Export
) - export packageY
to moduleZ
.
In general, we should avoid usingqualified exports
between modules in an application. - requires
Y
- indicates a dependency on the moduleY
.
Remember that the moduleY
has to be also added to dependencies ofX
(directly tomodule-path
or topom.xml
). - requires transitive
Y
- requiring automatically all modules required byY
. - open module - all its types are available for
deep reflection
(break into private parts of types atrun-time
) by other modules, while strong encapsulation is still upheld at compile-time. This property holds regardless of whether any packages are exported.
Deep reflection
is used by many libraries. For example,Spring
orHibernate
inject values into nonpublic fields of classes.
module moduleA {
exports moduleA.export;
exports moduleA.exportOnlyToB to moduleB;
}
so package:
moduleA.export
will be exported to other modulesmoduleA.exportOnlyToB
will be exported only tomoduleB
moduleA.internal
is encapsulated & other modules don't have access to it. For example if you uncomment the line inmoduleB.TestB
:project will not compile.// InternalA internalA; // no access
module moduleB {
requires transitive moduleA;
exports moduleB.export;
}
<dependency>
<groupId>basic</groupId>
<artifactId>moduleA</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
so (we don't repeat keyword defined in previous step):
moduleB
is reporting dependency onmoduleA
, what is more - every further module that will be requiringmoduleB
will get access tomoduleA
also.- Note that we have to put
moduleA
also inpom.xml
(withoutpom.xml
we should addmoduleA
tomodule-path
ofmoduleB
otherwise project will not compile).
module moduleC {
requires moduleB;
}
<dependency>
<groupId>basic</groupId>
<artifactId>moduleA</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>basic</groupId>
<artifactId>moduleB</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
so:
- if we want to use transitive dependency on
moduleA
(throughmoduleB
) we have to also add it topom.xml
- if not, nothing more should be done. - note that if you uncomment the line in
moduleC.TestC
:// ExportAOnlyToB exportAOnlyToB; // no access
project will not compile, so package ExportAOnlyToB
was correctly
exported only to moduleB
(moduleB.TestB
)
- in
moduleC.TestC
we use transitive dependency onmoduleA
:ExportA.export();
Module with only one simple class Invoker
to invoker static methods:
public static void invokeStatic(Object obj, String methodName) throws IllegalAccessException {
try {
obj.getClass().getMethod(methodName).invoke(null);
} catch (InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
open module openModule {
requires reflection;
}
Running openModule.Test
class will cause printing to console: "Hello
from openModule!" - because of that the modul is marked as open
in the
module-info.java
file.
module ordinaryModule {
requires reflection;
}
Running ordinaryModule.Test
class will cause exception:
java.lang.IllegalAccessException: class Invoker (in module reflection) cannot access class
Test (in module ordinaryModule) because module ordinaryModule does not export ordinaryModule
to module reflection
- If we are using
maven
the filemodule-info.java
should be added to theSources Root
(src/main/java
) - Note that if we put file
moduleA.TestA
on the same level asmodule-info.java
compilation will succedd, but running main method will result inerror
:Error occurred during initialization of boot layer java.lang.module.FindException: Error reading module: path\moduleB\target\classes Caused by: java.lang.module.InvalidModuleDescriptorException: TestB.class found in top-level directory (unnamed package not allowed in module)