Što je JPMS? Predstavljamo sustav modula Java Platform

Do Jave 9 paket je bio Java-ov element najviše razine organizacije koda. Počevši od Jave 9 koja se promijenila: iznad paketa sada je modul. Modul zajedno prikuplja povezane pakete.

Sustav Java Platform Module (JPMS) struktura je na razini koda, tako da ne mijenja činjenicu da Java pakiramo u JAR datoteke. U konačnici, sve je još uvijek povezano u JAR datoteke. Sustav modula dodaje novi deskriptor više razine koji JAR-ovi mogu koristiti uključivanjem module-info.javadatoteke.

Velike aplikacije i organizacije iskoristit će prednosti modula za bolju organizaciju koda. Ali svi će trošiti module, jer su JDK i njegove klase sada modularizirani.

Zašto Java trebaju module

JPMS je rezultat projekta Jigsaw, koji je poduzet sa sljedećim navedenim ciljevima: 

  • Olakšajte programerima organizaciju velikih aplikacija i knjižnica
  • Poboljšajte strukturu i sigurnost same platforme i JDK
  • Poboljšajte izvedbu aplikacije
  • Bolje podnijeti razgradnju platforme za manje uređaje

Vrijedno je napomenuti da je JPMS značajka SE (Standard Edition) i stoga utječe na svaki aspekt Jave od temelja. Nakon toga, promjena je dizajnirana da omogući većini koda da funkcionira bez izmjena pri prelasku s Jave 8 na Javu 9. Postoje neke iznimke od toga, a zabilježit ćemo ih kasnije u ovom pregledu.

Glavna ideja koja stoji iza modula je omogućiti prikupljanje povezanih paketa koji su vidljivi modulu, a istovremeno skrivanje elemenata od vanjskih potrošača modula. Drugim riječima, modul omogućuje drugu razinu inkapsulacije.

Put predavanja nasuprot puta modula

U Javi je do sada put klase bio dno za ono što je dostupno pokrenutoj aplikaciji. Iako put predavanja služi u tu svrhu i dobro se razumije, na kraju postaje velika, nediferencirana skupina u koju su smještene sve ovisnosti.

Put modula dodaje razinu iznad puta klase. Služi kao spremnik za pakete i određuje koji su paketi dostupni aplikaciji.

Moduli u JDK

Sam JDK sada se sastoji od modula. Počnimo s gledanjem matica i vijaka JPMS-a tamo.

Ako na vašem sustavu imate JDK, imate i izvor. Ako vam JDK nije poznat i kako ga dobiti, pogledajte ovaj članak.

Unutar vašeg direktorija za instalaciju JDK nalazi se /libdirektorij. Unutar tog direktorija nalazi se src.zipdatoteka. Raspakirajte ga u /srcdirektorij.

Pogledajte unutar /srcdirektorija i prijeđite na /java.basedirektorij. Tamo ćete pronaći module-info.javadatoteku. Otvori.

Nakon Javadoc-ovih komentara na čelu, pronaći ćete odjeljak s nazivom koji  module java.base slijedi niz exportsredaka. Ovdje se nećemo zadržavati na formatu koji postaje prilično ezoteričan. Pojedinosti možete pronaći ovdje.

Možete vidjeti da se mnogi poznati paketi s Jave java.ioizvoze iz java.basemodula. To je bit modula koji okuplja pakete.

Druga strana  exportsje requiresupute. To omogućuje da modul bude potreban modulu koji se definira.

Kada pokrećete Java kompajler protiv modula, odredite putanju modula na sličan način kao put klase. To omogućuje rješavanje depedencija.

Izrada modularnog Java projekta

Pogledajmo kako je strukturiran modulirani Java projekt.

Napravit ćemo mali program koji ima dva modula, jedan koji isporučuje ovisnost, a drugi koji koristi tu ovisnost i izvozi izvršnu glavnu klasu.

Stvorite novi direktorij negdje prikladno na vašem datotečnom sustavu. Nazovi to /com.javaworld.mod1. Prema dogovoru, Java moduli žive u direktoriju koji ima isto ime kao i modul.

Sada, unutar ovog direktorija, stvorite module-info.javadatoteku. Iznutra dodajte sadržaj s popisa 1.

Popis 1: com.javaworld.mod1 / module-info.java

module com.javaworld.mod1 { exports com.javaworld.package1; }

Primijetite da su modul i paket koji izvozi različita imena. Definiramo modul koji izvozi paket.

Sada stvoriti datoteku na tom putu, unutar direktorija koji sadrži module-info.javadatoteku: /com.javaworld.mod1/com/javaworld/package1. Nazovite datoteku  Name.java. U njega stavite sadržaj Popisa 2.

Popis 2: Ime.java

 package com.javaworld.package1; public class Name { public String getIt() { return "Java World"; } } 

Popis 2 postat će klasa, paket i modul o kojima ovisimo.

Ajmo sada stvoriti još jedan paralelni direktorij /com.javaworld.mod1 i nazvati ga /com.javaworld.mod2. U ovom direktoriju napravimo module-info.javadefiniciju modula koja uvozi modul koji smo već kreirali, kao u Popisu 3.

Popis 3: com.javaworld.mod2 / module-info.java

 module com.javaworld.mod2 { requires com.javaworld.mod1; } 

Popis 3 prilično je samorazumljiv. Definira com.javaworld.mod2modul i zahtijeva com.javaworld.mod1.

Unutar /com.javaworld.mod2imenik, stvoriti klase put ovako: /com.javaworld.mod2/com/javaworld/package2.

Sada dodajte datoteku koja se zove Hello.javasa kodom navedenim u Popisu 4.

Popis 4: Hello.java

 package com.javaworld.package2; import com.javaworld.package1.Name; public class Hello { public static void main(String[] args) { Name name = new Name(); System.out.println("Hello " + name.getIt()); } } 

U Popisu 4 započinjemo definiranjem paketa, a zatim uvozom com.javawolrd.package1.Nameklase. Imajte na umu da ti elementi funkcioniraju kao i uvijek do sada. Moduli su promijenili način na koji su paketi dostupni na razini strukture datoteka, a ne na razini koda.

Similarly, the code itself should be familiar to you. It simply creates a class and calls a method on it to create a classic “hello world” example.

Running the modular Java example

The first step is to create directories to receive the output of the compiler. Create a directory called /target at the root of the project. Inside, create a directory for each module: /target/com.javaworld.mod1 and /target/com.javaworld.mod2.

Step 2 is to compile the dependency module, outputting it to the /target directory. At the root of the project, enter the command in Listing 5. (This assumes the JDK is installed.)

Listing 5: Building Module 1

 javac -d target/com.javaworld.mod1 com.javaworld.mod1/module-info.java com.javaworld.mod1/com/javaworld/package1/Name.java 

This will cause the source to be built along with its module information.

Step 3 is to generate the dependent module. Enter the command shown in Listing 6.

Listing 6: Building Module 2

 javac --module-path target -d target/com.javaworld.mod2 com.javaworld.mod2/module-info.java com.javaworld.mod2/com/javaworld/package2/Hello.java 

Let’s take a look at Listing 6 in detail. It introduces the module-path argument to javac. This allows us to define the module path in similar fashion to the --class-path switch. In this example, we are passing in the target directory, because that is where Listing 5 outputs Module 1.

Next, Listing 6 defines (via the -d switch) the output directory for Module 2. Finally, the actual subjects of compilation are given, as the module-info.java file and class contained in Module 2.

To run, use the command shown in Listing 7.

Listing 7: Executing the module main class

 java --module-path target -m com.javaworld.mod2/com.javaworld.package2.Hello 

The --module-path switch tells Java to use /target directory as the module root, i.e., where to search for the modules. The -m switch is where we tell Java what our main class is. Notice that we preface the fully qualified class name with its module.

You will be greeted with the output Hello Java World.

Backward compatibility 

You may well be wondering how you can run Java programs written in pre-module versions in the post Java 9 world, given that the previous codebase knows nothing of the module path. The answer is that Java 9 is designed to be backwards compatible. However, the new module system is such a big change that you may run into issues, especially in large codebases.

When running a pre-9 codebase against Java 9, you may run into two kinds of errors: those that stem from your codebase, and those that stem from your dependencies.

For errors that stem from your codebase, the following command can be helpful: jdeps. This command when pointed at a class or directory will scan for what dependencies are there, and what modules those dependencies rely on.

For errors that stem from your dependencies, you can hope that the package you are depending on will have an updated Java 9 compatible build. If not you may have to search for alternatives.

One common error is this one:

How to resolve java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException

This is Java complaining that a class is not found, because it has migrated to a module without visibility to the consuming code. There are a couple of solutions of varying complexity and permanency, described here.

Opet, ako otkrijete takve pogreške u ovisnosti, obratite se projektu. Oni mogu imati Java 9 gradnju koju ćete moći koristiti.

JPMS je prilično velika promjena i trebat će vremena da se usvoji. Srećom, nema žurbe jer je Java 8 izdanje za dugoročnu podršku.

To je rečeno, dugoročno gledano, stariji projekti trebat će migrirati, a novi će trebati inteligentno koristiti module, nadam se da će iskoristiti neke od obećanih koristi.

Ovu priču, "Što je JPMS? Uvođenje Java Platform Module System" izvorno je objavio JavaWorld.