Vodič za modulnost Java 9

1. Pregled

Java 9 uvodi novu razinu apstrakcije iznad paketa, službeno poznatu kao Java Platform Module System (JPMS) ili skraćeno "Moduli".

U ovom uputstvu proći ćemo kroz novi sustav i raspraviti njegove različite aspekte.

Izgradit ćemo i jednostavan projekt koji će demonstrirati sve koncepte koje ćemo učiti u ovom vodiču.

2. Što je modul?

Prije svega, moramo razumjeti što je modul prije nego što shvatimo kako ih koristiti.

Modul je skupina usko povezanih paketa i resursa, zajedno s novom datotekom deskriptora modula.

Drugim riječima, riječ je o apstrakciji "paketa Java paketa" koja nam omogućuje da naš kod učinimo još višekratnim.

2.1. Paketi

Paketi unutar modula identični su Java paketima koje koristimo od osnivanja Java.

Kada kreiramo modul, kod interno organiziramo u pakete, baš kao što smo to prije radili s bilo kojim drugim projektom.

Osim organiziranja našeg koda, paketi se koriste za određivanje koji je kod javno dostupan izvan modula. O tome ćemo potrošiti više vremena kasnije u članku.

2.2. Resursi

Svaki je modul odgovoran za svoje resurse, poput medija ili konfiguracijskih datoteka.

Prije bismo stavili sve resurse na korijensku razinu našeg projekta i ručno upravljali resursima koji pripadaju različitim dijelovima aplikacije.

S modulima možemo isporučiti potrebne slike i XML datoteke s modulom koji mu je potreban, što olakšava upravljanje našim projektima.

2.3. Deskriptor modula

Kada stvaramo modul, uključujemo datoteku deskriptora koja definira nekoliko aspekata našeg novog modula:

  • Ime - naziv našeg modula
  • Ovisnosti - popis ostalih modula o kojima ovaj modul ovisi
  • Javni paketi - popis svih paketa kojima želimo pristupiti izvan modula
  • Ponuđene usluge - možemo pružiti implementacije usluga koje mogu koristiti i drugi moduli
  • Potrošene usluge - omogućuje trenutnom modulu da bude potrošač usluge
  • Dozvole za refleksiju - izričito dopušta drugim razredima da koriste refleksiju za pristup privatnim članovima paketa

Pravila imenovanja modula slična su načinu imenovanja paketa (točke su dopuštene, crtice nisu). Vrlo je uobičajeno raditi ili projektni stil (my.module) ili obrnuti DNS (com.baeldung.mymodule) nazivi stilova. U ovom ćemo priručniku koristiti stil projekta.

Moramo navesti sve pakete koje želimo biti javnima jer su prema zadanim postavkama svi paketi privatni moduli.

Isto vrijedi i za razmišljanje. Prema zadanim postavkama ne možemo koristiti refleksiju klasa koje uvozimo iz drugog modula.

Dalje u članku ćemo pogledati primjere upotrebe datoteke deskriptora modula.

2.4. Vrste modula

U novom sustavu modula postoje četiri vrste modula:

  • Sistemski moduli- Ovo su moduli navedeni kada pokrenemo popisni moduli naredba gore. Uključuju Java SE i JDK module.
  • Primjenski moduli - Ovi su moduli ono što obično želimo izgraditi kada se odlučimo koristiti module. Oni su imenovani i definirani u kompiliranom modul-info.razred datoteka uključena u sastavljeni JAR.
  • Automatski moduli - Neslužbene module možemo uključiti dodavanjem postojećih JAR datoteka na put modula. Naziv modula izvest će se iz naziva JAR-a. Automatski moduli imat će potpun pristup za čitanje svih ostalih modula učitanih putem.
  • Neimenovani modul - Kada se klasa ili JAR učita na put predavanja, ali ne i put modula, automatski se dodaje neimenovanom modulu. To je sveobuhvatni modul za održavanje povratne kompatibilnosti s prethodno napisanim Java kodom.

2.5. Distribucija

Moduli se mogu distribuirati na jedan od dva načina: kao JAR datoteka ili kao "eksplodirani" kompilirani projekt. To je, naravno, isto kao i bilo koji drugi Java projekt, pa ne bi trebalo čuditi.

Možemo stvoriti projekte s više modula koji se sastoje od „glavne aplikacije“ i nekoliko knjižničnih modula.

Moramo biti oprezni jer po JAR datoteci možemo imati samo jedan modul.

Kada postavljamo datoteku za izgradnju, moramo osigurati da svaki modul u našem projektu združimo u zasebnu jar.

3. Zadani moduli

Kada instaliramo Javu 9, možemo vidjeti da JDK sada ima novu strukturu.

Uzeli su sve originalne pakete i premjestili ih u novi sustav modula.

Što su ovi moduli možemo vidjeti upisivanjem u naredbeni redak:

java --list-moduli

Ti su moduli podijeljeni u četiri glavne skupine: java, javafx, jdk, i Oracle.

Java moduli su klase implementacije za osnovnu SE specifikaciju jezika.

javafx moduli su FX UI knjižnice.

Sve što je potrebno samom JDK čuva se u jdk modula.

I konačno, sve što je specifično za Oracle nalazi se u proročište modula.

4. Deklaracije modula

Da bismo postavili modul, moramo staviti posebnu datoteku u korijen naših nazvanih paketa module-info.java.

Ova je datoteka poznata kao deskriptor modula i sadrži sve podatke potrebne za izgradnju i upotrebu našeg novog modula.

Modul konstruiramo s deklaracijom čije je tijelo prazno ili se sastoji od direktiva modula:

modul myModuleName {// sve su direktive neobavezne}

Deklaraciju modula započinjemo s modul i slijedimo to s nazivom modula.

Modul će raditi s ovom deklaracijom, ali obično će nam trebati više informacija.

Tu dolaze smjernice modula.

4.1. Zahtijeva

Naša prva direktiva je zahtijeva. Ova nam direktiva modula omogućuje deklariranje ovisnosti modula:

modul my.module {zahtijeva module.name; }

Sada, moj.modul ima i vrijeme izvođenja i ovisnost o vremenu kompajliranja na modul.ime.

A svim javnim vrstama izvezenim iz ovisnosti naš modul može pristupiti kada koristimo ovu direktivu.

4.2. Zahtijeva statički

Ponekad napišemo kod koji upućuje na drugi modul, ali koji korisnici naše knjižnice nikada neće htjeti koristiti.

Na primjer, mogli bismo napisati uslužnu funkciju koja prilično ispisuje naše unutarnje stanje kada je prisutan drugi modul za bilježenje. Ali, neće svaki potrošač naše knjižnice poželjeti ovu funkcionalnost i ne želi uključiti dodatnu knjižnicu dnevnika.

U tim slučajevima želimo koristiti neobaveznu ovisnost. Korištenjem zahtijeva statički direktivu, kreiramo ovisnost samo o vremenu kompajliranja:

modul my.module {zahtijeva statički module.name; }

4.3. Zahtijeva prijelazno

Često surađujemo s knjižnicama kako bismo si olakšali život.

Ali, moramo biti sigurni da će bilo koji modul koji unese naš kod također unijeti ove ekstra 'prijelazne' ovisnosti ili neće raditi.

Srećom, možemo koristiti zahtijeva prijelazno direktiva koja će prisiliti sve potrošače na nižem stupnju da pročitaju i naše potrebne ovisnosti:

modul my.module {zahtijeva prijelazni module.name; }

Sad, kad programer zahtijeva moj.modul, neće također morati reći zahtijeva module.name kako bi naš modul i dalje radio.

4.4. Izvoz

Prema zadanim postavkama, modul ne izlaže nijedan svoj API drugim modulima. Ovaj jaka inkapsulacija bio je jedan od ključnih motivatora za stvaranje modularnog sustava.

Naš je kod znatno sigurniji, ali sada moramo izričito otvoriti svoj API svijetu ako želimo da bude upotrebljiv.

Koristimo izvoz direktiva za izlaganje svih javnih članova imenovanog paketa:

modul my.module {izvozi com.my.package.name; }

Sad, kad netko to učini zahtijeva moj.modul, imat će pristup javnim vrstama u našem com.my.package.name paket, ali ne i bilo koji drugi paket.

4.5. Izvoz ... Do

Možemo koristiti izvoz ... u otvoriti naše javne satove svijetu.

Ali, što ako ne želimo da cijeli svijet pristupi našem API-ju?

Možemo ograničiti koji moduli imaju pristup našim API-ima koristeći izvoz ... u direktiva.

Slično kao izvoz direktivu, deklariramo paket kao izvezeni. Ali, mi također navodimo kojim modulima dopuštamo uvoz ovog paketa kao a zahtijeva. Pogledajmo kako ovo izgleda:

modul my.module {izvoz com.my.package.name u com.specific.package; }

4.6. Koristi

A servis je implementacija određenog sučelja ili apstraktne klase koja može biti konzumira po ostalim razredima.

Usluge koje naš modul troši određujemo s koristi direktiva.

Imajte na umu da naziv razreda mi koristiti je ili sučelje ili apstraktna klasa usluge, a ne klasa implementacije:

modul my.module {koristi class.name; }

Ovdje bismo trebali primijetiti da postoji razlika između a zahtijeva direktiva i koristi direktiva.

Mogli bismo zahtijevati modul koji pruža uslugu koju želimo konzumirati, ali ta usluga implementira sučelje iz jedne od svojih prijelaznih ovisnosti.

Umjesto da prisiljava naš modul da zahtijeva svi za svaki slučaj koristimo prijelazne ovisnosti, koristimo koristi direktivu za dodavanje potrebnog sučelja na stazu modula.

4.7. Pruža ... sa

Modul također može biti davatelj usluga koje drugi moduli mogu trošiti.

Prvi dio direktive je pruža ključna riječ. Ovdje smo stavili naziv sučelja ili apstraktne klase.

Dalje, imamo s direktivu gdje pružamo naziv klase implementacije koji bilo provodi sučelje ili proteže se apstraktni razred.

Evo kako to izgleda zajedno:

modul my.module {pruža MyInterface s MyInterfaceImpl; }

4.8. Otvorena

Ranije smo spomenuli da je inkapsulacija pokretački motiv za dizajn ovog modularnog sustava.

Prije Jave 9 bilo je moguće upotrijebiti refleksiju za ispitivanje svake vrste i člana u paketu, čak i privatni one. Ništa uistinu nije zatvoreno, što programerima knjižnica može otvoriti sve vrste problema.

Jer Java 9 provodi jaka inkapsulacija, sada moramo izričito dati dopuštenje da se drugi moduli odražavaju na našu nastavu.

Ako i dalje želimo dopuštati potpuno razmišljanje kao što su to činile starije verzije Jave, možemo jednostavno otvorena cijeli modul gore:

otvori modul my.module {}

4.9. Otvara se

Ako trebamo dopustiti odraz privatnih tipova, ali ne želimo da sav naš kôd bude izložen, možemo koristiti otvara direktiva za izlaganje određenih paketa.

Ali ne zaboravite, ovo će paket otvoriti cijelom svijetu, pa budite sigurni da je to ono što želite:

modul my.module {otvara com.my.package; }

4.10. Otvara se… Za

U redu, pa je razmišljanje ponekad sjajno, ali svejedno želimo onoliko sigurnosti koliko možemo dobiti inkapsulacija. Pakete možemo selektivno otvoriti unaprijed odobrenom popisu modula, u ovom slučaju, pomoću otvara ... do direktiva:

modul my.module {otvara com.my.package za moduleOne, moduleTwo, itd .; }

5. Opcije naredbenog retka

Do sada je podrška za module Java 9 dodana Mavenu i Gradleu, tako da nećete trebati raditi puno ručnih izrada projekata. Međutim, to je još uvijek vrijedno znati kako za korištenje sustava modula iz naredbenog retka.

Koristit ćemo naredbenu liniju za naš cjeloviti primjer dolje kako bismo učvrstili kako cijeli sustav funkcionira u našim mislima.

  • modul-putKoristimo –Modul-put opcija za određivanje putanje modula. Ovo je popis jednog ili više direktorija koji sadrže vaše module.
  • dodati-čita - Umjesto da se oslanjamo na datoteku deklaracije modula, možemo koristiti ekvivalent naredbenog retka zahtijeva direktiva; –Dodaje-čita.
  • dodatak-izvozZamjena naredbenog retka za izvoz direktiva.
  • add-opensZamijenite otvorena klauzula u datoteci deklaracije modula.
  • add-moduliDodaje popis modula u zadani skup modula
  • popisni moduliIspisuje popis svih modula i nizove njihovih verzija
  • zakrpa-modul - Dodavanje ili poništavanje klasa u modulima
  • ilegalni pristup = dozvola | upozoriti | odbiti - Ili opustite snažnu inkapsulaciju prikazivanjem jednog globalnog upozorenja, prikazuje svako upozorenje ili ne uspije s pogreškama. Zadana vrijednost je dozvola.

6. Vidljivost

Trebali bismo potrošiti malo vremena razgovarajući o vidljivosti našeg koda.

Mnogo knjižnica ovisi o razmišljanju da bi djelovalo na svoju čaroliju (JUnit i proljeće mi padnu na pamet).

Prema zadanim postavkama u Javi 9, hoćemo samo imaju pristup javnim klasama, metodama i poljima u našim izvoženim paketima. Čak i ako koristimo refleksiju kako bismo dobili pristup nejavnim članovima i nazvali setAccessible (true), nećemo moći pristupiti tim članovima.

Možemo koristiti otvorena, otvara, i otvara ... do opcije za odobravanje pristupa samo za vrijeme izvođenja. Bilješka, ovo je samo vrijeme izvođenja!

Nećemo moći kompajlirati protiv privatnih tipova, a ionako to nikada ne bismo trebali.

Ako moramo imati pristup modulu radi razmišljanja, a nismo vlasnik tog modula (tj., Ne možemo koristiti otvara ... do direktiva), tada je moguće koristiti naredbeni redak –Dodaj-otvara mogućnost dopuštanja pristupa refleksiji vlastitih modula zaključanom modulu za vrijeme izvođenja.

Jedino upozorenje ovdje je da trebate imati pristup argumentima naredbenog retka koji se koriste za pokretanje modula da bi ovo funkcioniralo.

7. Sve to zajedno

Sad kad znamo što je modul i kako ih koristiti, krenimo i gradimo jednostavan projekt koji će pokazati sve koncepte koje smo upravo naučili.

Da stvari budu jednostavne, nećemo koristiti Maven ili Gradle. Umjesto toga, za izgradnju naših modula oslanjat ćemo se na alate naredbenog retka.

7.1. Postavljanje našeg projekta

Prvo, moramo postaviti strukturu našeg projekta. Stvorit ćemo nekoliko direktorija za organiziranje naših datoteka.

Započnite s izradom mape projekta:

mkdir modul-projekt cd modul-projekt

Ovo je osnova cijelog našeg projekta, zato ovdje dodajte datoteke, poput Maven ili Gradle datoteka za izgradnju, druge izvorne direktorije i resurse.

Također smo stavili direktorij za sve module specifične za naš projekt.

Dalje kreiramo direktorij modula:

mkdir jednostavni moduli

Evo kako će izgledati naša struktura projekta:

module-project | - // src ako koristimo zadani paket | - // datoteke za izgradnju također idu na ovu razinu | - jednostavni-moduli | - hello.modules | - com | - baeldung | - module | - hello | - main .app | - com | - baeldung | - moduli | - glavni

7.2. Naš prvi modul

Sad kad imamo osnovnu strukturu, dodajte svoj prvi modul.

Ispod jednostavni moduli direktorij, stvorite novi direktorij pod nazivom zdravo.moduli.

To možemo imenovati kako god želimo, ali slijediti pravila imenovanja paketa (tj. točke za odvajanje riječi itd.). Čak kao naziv modula možemo koristiti naziv našeg glavnog paketa, ako želimo, ali obično se želimo držati istog imena koje bismo koristili za stvaranje JAR-a ovog modula.

Pod našim novim modulom možemo stvoriti pakete koje želimo. U našem ćemo slučaju stvoriti jednu strukturu paketa:

com.baeldung.modules.halo

Zatim stvorite novu klasu pod nazivom HelloModules.java u ovom paketu. Kod ćemo održavati jednostavnim:

paket com.baeldung.modules.hello; javna klasa HelloModules {javna statička void doSomething () {System.out.println ("Zdravo, moduli!"); }}

I konačno, u zdravo.moduli korijenski direktorij, dodajte u naš modul deskriptor; module-info.java:

modul hello.modules {izvozi com.baeldung.modules.hello; }

Da bi ovaj primjer bio jednostavan, sve što radimo je izvoz svih javnih članova com.baeldung.modules.halo paket.

7.3. Naš drugi modul

Naš prvi modul je sjajan, ali ne čini ništa.

Možemo stvoriti drugi modul koji ga sada koristi.

Pod našim jednostavni moduli direktorij, stvorite drugi direktorij modula pod nazivom glavni.app. Ovaj put započet ćemo s opisom modula:

modul main.app {zahtijeva hello.modules; }

Ne trebamo ništa izlagati vanjskom svijetu. Umjesto toga, sve što trebamo učiniti je ovisiti o našem prvom modulu, tako da imamo pristup javnim klasama koje izvozi.

Sada možemo stvoriti aplikaciju koja ga koristi.

Stvorite novu strukturu paketa: com.baeldung.modules.main.

Sada stvorite novu datoteku klase pod nazivom MainApp.java.

paket com.baeldung.modules.main; uvoz com.baeldung.modules.hello.HelloModules; javna klasa MainApp {javna statička void main (String [] args) {HelloModules.doSomething (); }}

I to je sve što nam treba za demonstraciju modula. Naš sljedeći korak je izgradnja i pokretanje ovog koda iz naredbenog retka.

7.4. Izgradnja naših modula

Za izgradnju našeg projekta možemo stvoriti jednostavnu bash skriptu i smjestiti je u korijen našeg projekta.

Stvorite datoteku pod nazivom compile-simple-modules.sh:

#! / usr / bin / env bash javac -d outDir --module-source-path jednostavni-moduli $ (pronađi jednostavne-module -ime "* .java")

Postoje dva dijela ove naredbe, javac i pronaći naredbe.

The pronaći naredba jednostavno daje popis svih.Java datoteke u našem direktoriju jednostavnih modula. Tada taj popis možemo uvesti izravno u Java kompajler.

Jedino što moramo učiniti drugačije od starijih verzija Jave je pružiti modul-izvor-put parametar za obavještavanje prevoditelja da gradi module.

Jednom kada pokrenemo ovu naredbu, imat ćemo vanDir mapa s dva prevedena modula unutra.

7.5. Pokretanje našeg koda

I sada napokon možemo pokrenuti naš kod kako bismo provjerili rade li moduli ispravno.

Stvorite drugu datoteku u korijenu projekta: run-simple-module-app.sh.

#! / usr / bin / env bash java --module-path outDir -m main.app/com.baeldung.modules.main.MainApp

Da bismo pokrenuli modul, moramo osigurati barem modul-put i glavna klasa. Ako sve uspije, trebali biste vidjeti:

> $ ./run-simple-module-app.sh Pozdrav, moduli!

7.6. Dodavanje usluge

Sad kad imamo osnovno razumijevanje kako graditi modul, učinimo to malo složenijim.

Vidjet ćemo kako se koristi pruža ... s i koristi direktive.

Započnite definiranjem nove datoteke u zdravo.moduli modul imenovan HelloInterface.Java:

javno sučelje HelloInterface {void sayHello (); }

Da bismo olakšali stvari, implementirat ćemo ovo sučelje s našim postojećim HelloModules.java razred:

javna klasa HelloModules implementira HelloInterface {public static void doSomething () {System.out.println ("Hello, Modules!"); } javna praznina sayHello () {System.out.println ("Zdravo!"); }}

To je sve što trebamo učiniti da bismo stvorili servis.

Sada moramo svijetu reći da naš modul pruža ovu uslugu.

Dodajte sljedeće našem module-info.java:

pruža com.baeldung.modules.hello.HelloInterface s com.baeldung.modules.hello.HelloModules;

Kao što vidimo, deklariramo sučelje i koja ga klasa implementira.

Dalje, ovo moramo potrošiti servis. U našem glavni.app modula, dodajmo sljedeće u naš module-info.java:

koristi com.baeldung.modules.hello.HelloInterface;

Konačno, u našoj glavnoj metodi ovu uslugu možemo koristiti putem ServiceLoader-a:

Usluge koje se mogu provjeriti = ServiceLoader.load (HelloInterface.class); Usluga HelloInterface = services.iterator (). Next (); service.sayHello ();

Sastavite i pokrenite:

#> ./run-simple-module-app.sh Pozdrav, moduli! Zdravo!

Koristimo ove direktive kako bismo puno jasnije objasnili kako se koristi naš kod.

Implementaciju bismo mogli staviti u privatni paket dok sučelje izlažemo u javnom paketu.

To naš kod čini mnogo sigurnijim s vrlo malo dodatnih troškova.

Naprijed i isprobajte neke od ostalih smjernica kako biste saznali više o modulima i njihovom radu.

8. Dodavanje modula neimenovanom modulu

Koncept neimenovanog modula sličan je zadanom paketu. Stoga se ne smatra stvarnim modulom, ali se može gledati kao zadani modul.

Ako klasa nije član imenovanog modula, tada će se automatski smatrati dijelom ovog neimenovanog modula.

Ponekad, da bismo osigurali određene module platforme, biblioteke ili davatelja usluga u grafikonu modula, moramo dodati module u zadani korijenski skup. Na primjer, kada pokušavamo pokrenuti programe Java 8 takvi kakvi jesu s Java 9 kompajlerom, možda ćemo morati dodati module.

Općenito, opcija dodavanja imenovanih modula u zadani skup korijenskih modula je –Add-moduli (,)* gdje je naziv modula.

Na primjer, pružiti pristup svima java.xml.bind modula sintaksa bi bila:

--add-moduli java.xml.bind

Da bismo ovo koristili u Mavenu, možemo ga umetnuti u maven-compiler-plugin:

 org.apache.maven.plugins maven-compiler-plugin 3.8.0 9 9 --add-module java.xml.bind 

9. Zaključak

U ovom opsežnom vodiču usredotočili smo se na i pokrili osnove novog sustava Java 9 Module.

Počeli smo razgovarajući o tome što je modul.

Zatim smo razgovarali o tome kako otkriti koji su moduli uključeni u JDK.

Također smo detaljno pokrili datoteku deklaracije modula.

Teoriju smo zaokružili govoreći o raznim argumentima naredbenog retka koji su nam potrebni za izgradnju naših modula.

Napokon, sve svoje prethodno znanje primijenili smo u praksi i stvorili jednostavnu aplikaciju izgrađenu na vrhu sustava modula.

Da biste vidjeli ovaj i više koda, provjerite ga na Githubu.