Java Module System — Part II

Java Module System — Part IIJubin KuriakoseBlockedUnblockFollowFollowingMay 24This is a continuation of the module system introduced to Java 9 .

You can find Part — I at the link below:Modular programming in JavaJava Platform Module Systemmedium.

comNuancesThe module system comes with its on set of rules on how it can be accessed.

The module should be allowed to access the other module via the requires keyword.

The other module should export the package and the type itself should be public (to make it visible)The module system also requires that two modules cannot have the same package name.

This enforcement separates the module-path from the class-path paradigm where packages could be overridden by multiple jars having same package name.

This enforces stricter security on the classes being referred.

There are also performance optimization with respect to class loading as the JVM no longer needs to search the class-path but rather maps the package the module it needs to pick-up fromThe module system also have set of other directives (apart from requires and export) for managing dependencies.

transitiveThere are cases where when module A depends on another module B, module A might also require to access types that are actually part of module B’s dependencies by virtue of its requires keyword.

This is not possible out of the box as dependencies are not transitive.

Module A requires Module B requires Module C, doesn’t mean Module A automatically requires Module CTo make this happen we would need to use the transitive keywordmodule com.

jtk.

nutrition.

lib { requires transitive com.

jtk.

nutrition.

loader; requires transitive com.

jtk.

service;}The above declaration makes an module that requires com.

jtk.

nutrition.

lib to also have readability to modules com.

jtk.

nutrition.

loader and com.

jtk.

service (a service module — more on that below).

This way the top module (that requires com.

nutrition.

lib) doesn’t have to require all the dependencies internally.

aggregatorThe above module in the application also acts as an aggregator, in that it doesn’t have to have any source except for the module-info.

java.

In this way we can collate all the dependencies to a particular module and in the main module above ( com.

jtk.

nutrition.

cli) we just require the aggregator: com.

jtk.

nutrition.

libServicesJava module system also introduces the concept of services.

These are modules that only exposes the interfaces to the client.

The actual implementations are separate modules that uses a special keyword to indicate a service implementation.

As shown below our application is enhanced to include a service com.

jtk.

service and its two implementations com.

jtk.

validate.

number and com.

jtk.

validate.

stringThe clientThe module com.

jtk.

service has the a single interface that is invoked by the clients.

This invocation is made possible by the uses directive:module com.

jtk.

nutrition.

cli{ requires java.

logging; requires com.

jtk.

nutrition.

lib; uses com.

jtk.

service.

api.

ValidationService;}Unlike dependency injection where the implementation is injected at run-time, here the dependencies are looked up using ServiceLoader.

load(ValidationService.

class);This returns a collection of implementations that can be called as suchIterable<ValidationService> validationServices = ServiceLoader.

load(ValidationService.

class);for (ValidationService s : validationServices) { s.

validate(o)}The problem with this is that, the client might need to call a particular implementation and there is no way to achieve this without having the interface expose a static factory method to retrieve the apt implementation (hidden behind the interface of-course! ).

Again this is not dependency injection so you shouldn’t have the same expectations.

Also for reducing code duplicity, the above code needs to be part of the service interface; to that affect the uses directive is moved to the service module as shown below:module com.

jtk.

service { exports com.

jtk.

service.

api; uses com.

jtk.

service.

api.

ValidationService;}And the interface exposes a factory method to select the implementation as below:public interface ValidationService<T> { boolean validate(T t); boolean isSupportedType(Object t); public static List<ValidationService> getValidators(Object item) { List<ValidationService> validationServiceList = new ArrayList<>(); Iterable<ValidationService> validationServices = ServiceLoader.

load(ValidationService.

class); for (ValidationService s : validationServices) { if (s.

isSupportedType(item)) validationServiceList.

add(s); } return validationServiceList; }}Now as the client doesn’t call the ServiceLoader directly and the client module com.

jtk.

nutrition.

cli doesn’t require the uses directivemodule com.

jtk.

nutrition.

cli{ requires java.

logging; requires com.

jtk.

nutrition.

lib;*(note)}*The com.

jtk.

nutrition.

lib module is an aggregator of the service and other modules required by the application (see aggregators above)The implementationOn the implementation side of things i.

e.

com.

jtk.

validate.

number and com.

jtk.

validate.

string modules have a special directive that registers itself as services to an interface:module com.

jtk.

validate.

string { requires com.

jtk.

service; provides com.

jtk.

service.

api.

ValidationService with com.

jtk.

validate.

string.

FirstCapsValidator;}module com.

jtk.

validate.

number { requires com.

jtk.

service; provides com.

jtk.

service.

api.

ValidationService with com.

jtk.

validate.

number.

DoubleValidation;}The provides – with keyword registers the implementations to the interface as above.

With that we have registered the implementation com.

jtk.

validate.

string and com.

jtk.

validate.

number to the service module com.

jtk.

service and have provided a dependency to the client library via the aggregator com.

jtk.

nutrition.

lib as shown belowmodules dependency graphSummaryWith this we have looked into the various rules regarding modules and how to handle aggregates and transitive dependencies.

There are other directives but these are the essential ones to develop a robust modular system.

We also looked into how services are created and how they are called with-in the module system.

In the next and final article on java modular system I intend to explore the jlink binary that comes with the module system in Java 9Below is a reference to the source code for the above articlesjtkSource/jpms-nutritioninfoReference code to my exploration of the java module system – jtkSource/jpms-nutritioninfogithub.

comReferences:I have referred primarily the below book and also other websites in exploring the module system in java.

Its a very good read!Modular Programming in Java 9: Build large scale applications using Java modularity and Project…Key FeaturesMaster design patterns and best practices to build truly modular applications in Java 9Upgrade your old…www.

amazon.

com.

. More details

Leave a Reply