Why use Java 9 Module?
Jack has acme.util.stringsorter.StringSorterUtil
and acme.util.stringsorter.interna.BubbleSortUtil
. StringSorterUtil uses BubbleSortUtil to sort. Jack shares acme.util.stringsorter
inside JAR with colleagues. His colleagues are only supposed to useStringSorterUtil
, but some saw BubbleSortUtil
and went ahead and used it. when Jack wanted to replace BubbleSortUtil with hash sort, those who used BubbleSortUtil
had their code failed. Jack had to put BubbleSortUtil
back.
a class's modifier can only be public or default. If you don't want others to use BubbleSortUtil
, you have to put it inside the same package with StringSorterUtil
Java 9 Module solves this problems by hiding BubbleSortUtil
inside the module. It is not exported, therefor not exposed.
- 没有运行到需要 没加上的JAR包里的类 的类时, 不会意识到缺包了
- 当有重复的不同版本的类时, 第一个副本会被使用
Once a class is in classpath, it's free for all. Developer may use a type they are not supposed to, or a type that might be available at compile time, but not at deployment/runtime.
Multiple copies or multiple versions of same class in 2 different classpath locations.making it unpredictable which version the runtime will pick up during execution. JAR Hell.
This is especially obvious for rt.jar. sun.misc.Unsafe
contains low-level unsafe operations.Despite warnings, many developers use its methods
benefits:
- String encapsulation
- Reliable Configuuration
- Scalable and easy to use
####Project Jigsaw
- Scalable platform
- Move away from a monolithic runtime and enabling the ability to scale the platform down to smaller computing devices
- Security and maintainability
- Better organizational code make it more maintainable. Hiding internal API improves security
- Improved application performance
- Smaller platform with only necessary runtimes, rusulting into faster perfomance
- Easier developer experience
- The combination of modular system and the modular platform makes it easier to develop applications and libraries
A Java 9 module is a named, self-describing collection of code and data that you can create and use in Java applications. It can contain packages(containing classes and interface) as well as resource files or property files
They are like building blocks: can exist on its own and be a part of a bigger whole
-
assign a module name (reverse domain name pattern, all lowercase)
-
create a module root folder
source folder > module root folder > package folder
-
add the module code
![1548319570164](Modular Programming in Java 9 - my notes.assets/1548319570164.png)
-
create and configure the module descriptor
- name is module-info.java
- located inside module root folder
- contains the metadata(inputs and outputs)
module com.my.module.name{ }
![1548319905718](Modular Programming in Java 9 - my notes.assets/1548319905718.png)
![1548320038753](Modular Programming in Java 9 - my notes.assets/1548320038753.png)
D:\programs\java\jdk-11.0.2\bin\java --module-path out --module packt.addressbook/packt.addressbook.Main
-
You dont have to list all classes by their paths to compile them, just use the module name:
because netbeans's fold structure is src/[module_name]/classes/module.info.java I used D:\programs\java\jdk-11.0.2\bin\javac -d out --module-source-path src\*\classes -m packt.addressbook
As per JEP 261 the
--module-source-path
option (for compilation in "multi-module mode") must point to a directory that holds one subdirectory for each contained module, where the directory name must equal the module name.To accommodate layouts where sources are not directly contained in the module directory, the option supports patterns where the token
*
can be used to represent the module name in any part of the path such as in"./*/src/main/java/"
, which will find the modulemy.mod1
in./my.mod1/src/main/java/module-info.java
etc.JEP 261 does not mention any contraints on where in the pattern
*
may occur, but apparentlyjavac
doesn't like patterns starting with*
. This may or may not be intentional.
D:\programs\java\jdk-11.0.2\bin\javac -d out --module-source-path src\*\classes -m packt.addressbook,packt.sortutil
At compilation, initialization and exe
cution, the compiler or runtime checks module-info.java or module-info.class to check if a dependent module is missing and shows the name of missing module
You dont have to package the core java library classes you rely on like collections and threads because every runtime has rt.jar, which contains all compiled classes in the Java platform. The size of rt.jar is about 60M.
java --list-modules //list observable modules and exit
java --describe-module
linux only:
javac --module-source-path src -d out $(find . -name '*.java')
Circular dependency
![1548903299809](Modular Programming in Java 9 - my notes.assets/1548903299809.png)
requires
grands the readability between modules.
It is directional.
Accessibility is between modules and types. We say a type is accessible in a module
For a type t in module B to be accessible in module A:
- A needs to read B
- B needs to export the package that contains t
- t needs to be public
![1549017510278](Modular Programming in Java 9 - my notes.assets/1549017510278.png)
public LibApi:
package packt.lib.external;
public interface LibApi {
static LibApi createInstance() {
return new LibApiImpl();
}
public void testMethod();
}
default LibApiImpl
package packt.lib.external;
class LibApiImpl implements LibApi {
public void testMethod() {
System.out.println("test method executed");
}
}
App from another module:
package packt.app;
import packt.lib.external.LibApi;
public class App {
public static void main(String[] args){
//This will not work because LibApiImpl is not accessible
//LibApiImpl api = new LibApiImpl();
//api.testMethod();
//This will work
LibApi api = LibApi.createInstance();
api.testMethod();
}
}
Given a package in an application, it should be a part of one and only one module on the module path. Modules do not allow sharing of packages or split packages.
However, multiple jars in the class path can contain the same package.
module java.sql{
...
requires transitive java.logging;
...
}
![1549021114934](Modular Programming in Java 9 - my notes.assets/1549021114934.png)
- Defeats the purpose of modularity
- Mainly used for legacy code migration purposes like CORBA
- example: java.se.ee
- Exports only to specified modules
exports <package-name> to <module1>, <module2>
- not recommended
- adds a certain level of couping between 2 modules
Modules are tightly coupled. A module is aware of the module it needs.
Steps (using the project p205_Implementing_sorting_services
)
-
Create a module containing the interface that defines the service
module name:
packt.sortutil
packt.util.SortUtil:public interface SortUtil { public <T extends Comparable> List<T> sortList(List<T> list); }
module-info.java:
module packt.sortutil { exports packt.util; }
-
Create one or more implementation modules
-
they require the interface module
- Make sure you dont put both the implementation classes in the 2 modules in the same package to avoid split package
packt.util.impl.bubblesort
andpackt.util.impl.javasort
- Make sure you dont put both the implementation classes in the 2 modules in the same package to avoid split package
-
register themselves as service providers
syntax: provides with `
-
the interface type in
provides
does not belong to the modules itself, but there isrequires
-
Note the fully qualified name for both the interface and implementation types
- implementations modules doesn't need to export themselves, the consumer module will get the instance of the implementation through the service APIs
packt.util.impl.bubblesort.BubbleSortUtilImpl:
package packt.util.impl.bubblesort; public class BubbleSortUtilImpl implements SortUtil { ...
module-info.java:
module packt.sort.bubblesort { requires packt.sortutil; provides packt.util.SortUtil with packt.util.impl.bubblesort.BubbleSortUtilImpl; }
-
Consumer register itself as a consumer of the service
- consumer module also
requires
interface modules - formally declare the need to use the service
- syntax:
uses <interface-type>
- syntax:
module packt.addressbook { requires packt.sortutil; uses packt.util.SortUtil; }
- Call the Service API to access the provider instances in the consumer module's code (this is dependency lookup, not dependency injection)
Iterable<SortUtil> sortUtils = ServiceLoader.load(SortUtil.class); for (SortUtil sortUtil : sortUtils){ System.out.println("Found an instance of SortUtil"); sortUtil.sortList(contacts); }
- consumer module also
-