New feature of jdk9: jpms modularization

New feature of jdk9: jpms modularization

brief introduction

Jdk9 introduces a new feature called jpms (Java platform module system), which can also be called project jigsaw. The essence of modularization is to split a large project into modules. Each module is an independent unit, and different modules can refer to and call each other.

In the module, there will be metadata to describe the information of the module and the relationship between the module and other modules. These modules are combined to form the final running program.

Does it sound like a module in gradle or Maven?

Through componentization, we can distinguish specific modules according to their functions, so as to maintain high aggregation within modules and low coupling between modules.

In addition, we can hide the specific implementation content of the interface through modularization, so as not to affect the calls between modules.

Finally, we can describe the dependencies between modules by displaying declarations. So that developers can better understand the application logic of the system.

Implementation of module in jdk9

Before jdk9, Java distinguished and isolated functions through different packages and jars.

But in jdk9, everything sends a change.

The first is the change of jdk9 itself. JDK is now distinguished by different modules. If you expand the JDK dependency in the IDE, you will see Java base,java. Compiler and other modules.

Where Java The base module is special. It is an independent module, which means that it does not depend on any other module, and Java Base is the foundation of other modules, so there is no need to explicitly reference Java. Base in other modules base。

Let's summarize:

Class is a collection of fields and methods, package is a collection of classes, and module is a collection of packages.

Generally speaking, users can't feel using or not using modules, because you can use the jar package of the module as an ordinary jar package, or you can use the ordinary jar package as a jar package of the module.

So what is the difference between module and ordinary jar package?

Let's explore the use of module in detail in specific examples.

Module in JDK

Just mentioned that JDK is also distinguished by modules, and all modules are based on Java base。 We can use Java -- List modules to list all existing modules:

java --list-modules 

java.base@14.0.1
java.compiler@14.0.1
java.datatransfer@14.0.1
java.desktop@14.0.1
java.instrument@14.0.1
java.logging@14.0.1
java.management@14.0.1
java.management.rmi@14.0.1
....

You can also use Java -- describe module to view specific module information:

java --describe-module java.base

java.base@14.0.1
exports java.io
exports java.lang
exports java.lang.annotation
exports java.lang.constant
exports java.lang.invoke
exports java.lang.module
exports java.lang.ref
exports java.lang.reflect
exports java.lang.runtime
...

Let's take a closer look at module info Contents in class file:

module java.base {
    exports java.io;
    exports java.lang;
    exports java.lang.annotation;
    ...

The document is very long, so I won't list them one by one. Interested friends can refer to it by themselves.

After seeing the JDK's own modules, let's create some of our own modules to have a look.

Create your own module

Suppose we have a controller, a service interface, and two service implementations.

For simplicity, we divide these four classes into different modules.

Creating a module in the idea is very simple. You only need to add module info. In the Java folder Java file is OK. As shown in the figure below:

The code is actually very simple. The controller refers to the service interface, and the implementation of the two services implements the service interface.

Look at the module info files of the controller and service modules:

module com.flydean.controller {
    requires com.flydean.service;
    requires lombok;
}
module com.flydean.service {
    exports com.flydean.service;
}

Requires indicates the module name to be used by the module. Exports represents the package name in the module exposed by the module. If the module is not exposed, other modules cannot reference these packages.

See how to compile, package and run modules on the command line:

$ javac
    --module-path mods
    -d classes/com.flydean.controller
    ${source-files}
$ jar --create
    --file mods/com.flydean.controller.jar
    --main-class com.flydean.controller.ModuleController.Main
    -C classes/com.flydean.controller .
$ java
    --module-path mods
    --module com.flydean.controller

Deep understanding of module info

In the previous section, we the requirements and exports in module info. In this section, we will explain more about other uses of module info.

transitive

First look at the code of ModuleA:

    public ModuleService getModuleService(){
        return new ModuleServiceA();
    }

The getmoduleservice method returns a moduleservice, which belongs to module COM flydean. Service, let's see the definition of module Info:

module com.flydean.servicea {
    requires com.flydean.service;
    exports com.flydean.servicea;
}

It seems that there is no problem, but if there are other modules to use ServiceA's getmoduleservice method, there will be a problem because the method returns module COM flydean. Class defined in service. So we need to use transitional here.

module com.flydean.servicea {
    requires transitive com.flydean.service;
    exports com.flydean.servicea;
}

Transitive means all read com flydean. The module of ServiceA can also read com flydean. service。

static

Sometimes, when we use some classes in our code, the jar package containing these classes must be compiled. However, we may not use these classes when running, so we can use static to represent that the module is optional.

For example, the following module Info:

module com.flydean.controller {
    requires com.flydean.service;
    requires static com.flydean.serviceb;
}

exports to

In module info, if we only want to expose the package export to a specific module or some modules, we can use exports to:

module com.flydean.service {
    exports com.flydean.service to com.flydean.controller;
}

Above we will com flydean. Service is only exposed to com flydean. controller。

open pacakge

Using static, we can mask modules at run time, while using open, we can compile some packages that are not available at compile time, but are available at run time.

module com.flydean.service {
    opens com.flydean.service.subservice;
    exports com.flydean.service to com.flydean.controller,com.flydean.servicea,com.flydean.serviceb;
}

In the above example, com flydean. service. Subservice is not available at compile time, but is available at run time. In general, this definition is needed when reflection is used.

provides with

Suppose we want to use the specific implementation of service in the controller, such as ServiceA and serviceb. One way is to use ServiceA and serviceb directly in the controller module, which is highly coupled.

In modules, we can use provides with to decouple the coupling between modules.

Let's look at the code:

module com.flydean.controller {
    uses com.flydean.service.ModuleService;
    requires com.flydean.service;
    requires lombok;
    requires slf4j.api;
}
module com.flydean.servicea {
    requires transitive com.flydean.service;
    provides com.flydean.service.ModuleService with com.flydean.servicea.ModuleServiceA;
    exports com.flydean.servicea;
}
module com.flydean.serviceb {
    requires transitive com.flydean.service;
    provides com.flydean.service.ModuleService with com.flydean.serviceb.ModuleServiceB;
    exports com.flydean.serviceb;
}

In the controller, we use uses to expose the interface to be implemented. In the specific implementation module, provide with is used to expose the specific implementation.

How to use it? In the controller:

public static void main(String[] args) {
    List<ModuleService> moduleServices = ServiceLoader
    .load(ModuleService.class).stream()
    .map(ServiceLoader.Provider::get)
    .collect(toList());
    log.info("{}",moduleServices);
    }

Here we use serviceloader to load the implementation of the interface. This is a good decoupling method, so that I can put the specific modules I need to use in the modulepath to realize dynamic modification.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>