En guide til Java 9-modularitet

1. Oversikt

Java 9 introduserer et nytt abstraksjonsnivå over pakker, kjent som Java Platform Module System (JPMS), eller kort sagt "Modules".

I denne opplæringen vil vi gå gjennom det nye systemet og diskutere dets forskjellige aspekter.

Vi bygger også et enkelt prosjekt for å demonstrere alle konseptene vi skal lære i denne guiden.

2. Hva er en modul?

Først og fremst må vi forstå hva en modul er før vi kan forstå hvordan vi bruker dem.

En modul er en gruppe nært beslektede pakker og ressurser sammen med en ny modulbeskrivelsesfil.

Med andre ord er det en “pakke med Java-pakker” -abstraksjon som gjør at vi kan gjøre koden vår enda mer gjenbrukbar.

2.1. Pakker

Pakkene i en modul er identiske med Java-pakkene vi har brukt siden starten av Java.

Når vi lager en modul, vi organiserer koden internt i pakker, akkurat som vi tidligere gjorde med andre prosjekter.

Bortsett fra å organisere koden vår, brukes pakker for å bestemme hvilken kode som er offentlig tilgjengelig utenfor modulen. Vi bruker mer tid på å snakke om dette senere i artikkelen.

2.2. Ressurser

Hver modul er ansvarlig for ressursene, som media- eller konfigurasjonsfiler.

Tidligere hadde vi lagt alle ressursene inn på rotnivået i prosjektet vårt og manuelt styrt hvilke ressurser som tilhørte forskjellige deler av applikasjonen.

Med moduler kan vi sende nødvendige bilder og XML-filer med modulen som trenger det, noe som gjør prosjektene våre mye enklere å administrere.

2.3. Modulbeskrivelse

Når vi oppretter en modul, inkluderer vi en deskriptorfil som definerer flere aspekter av den nye modulen:

  • Navn - navnet på modulen vår
  • Avhengigheter - en liste over andre moduler som denne modulen er avhengig av
  • Offentlige pakker - en liste over alle pakker vi ønsker tilgjengelig fra utenfor modulen
  • Tjenester som tilbys - vi kan tilby serviceimplementeringer som kan konsumeres av andre moduler
  • Tjenester forbrukes - lar den nåværende modulen være forbruker av en tjeneste
  • Refleksjonstillatelser - tillater eksplisitt andre klasser å bruke refleksjon for å få tilgang til de private medlemmene av en pakke

Reglene for modulens navngivning ligner på hvordan vi navngir pakker (prikker er tillatt, bindestreker er ikke). Det er veldig vanlig å gjøre enten prosjektstil (my.module) eller Reverse-DNS (com.baeldung.mymodule) stilnavn. Vi bruker prosjektstil i denne guiden.

Vi trenger å liste opp alle pakkene vi ønsker å være offentlige, fordi som standard er alle pakker modul private.

Det samme gjelder refleksjon. Som standard kan vi ikke bruke refleksjon over klasser vi importerer fra en annen modul.

Senere i artikkelen ser vi på eksempler på hvordan du bruker modulbeskrivelsesfilen.

2.4. Modultyper

Det er fire typer moduler i det nye modulsystemet:

  • Systemmoduler- Dette er modulene som er oppført når vi kjører liste-moduler kommandoen ovenfor. De inkluderer Java SE og JDK modulene.
  • Applikasjonsmoduler - Disse modulene er det vi vanligvis vil bygge når vi bestemmer oss for å bruke moduler. De er navngitt og definert i den sammenstilte modul-info.klasse fil inkludert i den monterte JAR.
  • Automatiske moduler - Vi kan inkludere uoffisielle moduler ved å legge til eksisterende JAR-filer i modulbanen. Navnet på modulen blir avledet fra navnet på JAR. Automatiske moduler vil ha full lesetilgang til hver annen modul lastet av banen.
  • Uansett modul - Når en klasse eller JAR lastes inn på kursstien, men ikke modulbanen, blir den automatisk lagt til den ikke navngitte modulen. Det er en catch-all-modul for å opprettholde bakoverkompatibilitet med tidligere skrevet Java-kode.

2.5. Fordeling

Moduler kan distribueres på to måter: som en JAR-fil eller som et "eksplodert" kompilert prosjekt. Dette er selvfølgelig det samme som alle andre Java-prosjekter, så det bør ikke komme som noen overraskelse.

Vi kan lage prosjekter med flere moduler som består av en "hovedapplikasjon" og flere biblioteksmoduler.

Vi må imidlertid være forsiktige fordi vi bare kan ha en modul per JAR-fil.

Når vi setter opp build-filen vår, må vi sørge for å pakke hver modul i prosjektet vårt som en egen krukke.

3. Standardmoduler

Når vi installerer Java 9, kan vi se at JDK nå har fått en ny struktur.

De har tatt alle de originale pakkene og flyttet dem inn i det nye modulsystemet.

Vi kan se hva disse modulene er ved å skrive inn kommandolinjen:

java - liste-moduler

Disse modulene er delt inn i fire hovedgrupper: java, javafx, jdk, og Oracle.

java moduler er implementeringsklasser for kjernen SE Language Specification.

javafx moduler er FX UI-bibliotekene.

Alt som JDK trenger, oppbevares i jdk moduler.

Og endelig, alt som er Oracle-spesifikt er i orakel moduler.

4. Modulerklæringer

For å sette opp en modul, må vi legge en spesiell fil til roten til pakkene våre module-info.java.

Denne filen er kjent som modulbeskriveren og inneholder alle dataene som trengs for å bygge og bruke den nye modulen.

Vi konstruerer modulen med en erklæring hvis kropp enten er tom eller består av moduldirektiver:

modul myModuleName {// alle direktiver er valgfrie}

Vi starter modulerklæringen med modul nøkkelord, og vi følger det med navnet på modulen.

Modulen vil fungere med denne erklæringen, men vi trenger ofte mer informasjon.

Det er der moduldirektivene kommer inn.

4.1. Krever

Vårt første direktiv er krever. Dette moduldirektivet lar oss erklære modulavhengighet:

modul my.module {krever module.name; }

Nå, min.modul har både en kjøretid og en kompileringstidsavhengighetmodule.name.

Og alle offentlige typer eksportert fra en avhengighet er tilgjengelig av modulen vår når vi bruker dette direktivet.

4.2. Krever statisk

Noen ganger skriver vi kode som refererer til en annen modul, men som brukere av biblioteket vårt aldri vil bruke.

For eksempel kan vi skrive en verktøyfunksjon som ganske fint skriver ut vår interne tilstand når en annen loggningsmodul er til stede. Men ikke alle forbrukere av biblioteket vårt vil ha denne funksjonaliteten, og de vil ikke inkludere et ekstra loggbibliotek.

I disse tilfellene ønsker vi å bruke en valgfri avhengighet. Ved å bruke krever statisk direktivet, skaper vi en avhengighet som bare er kompileringstid:

modul my.module {krever statisk modul.navn; }

4.3. Krever Transitive

Vi jobber ofte med biblioteker for å gjøre livene våre enklere.

Men vi må sørge for at en hvilken som helst modul som bringer inn koden vår, også vil bringe inn disse ekstra 'transitive' avhengighetene, ellers fungerer de ikke.

Heldigvis kan vi bruke krever transitive direktivet for å tvinge alle nedstrømsforbrukere til å lese våre nødvendige avhengigheter:

modul my.module {krever transitiv modul.navn; }

Nå, når en utvikler krever min modul, de trenger ikke også å si krever module.name for at modulen vår fortsatt skal fungere.

4.4. Eksport

Som standard utsetter ikke en modul noe av API-en for andre moduler. Dette sterk innkapsling var en av de viktigste motivatorene for å lage modulsystemet i utgangspunktet.

Koden vår er betydelig sikrere, men nå må vi eksplisitt åpne API-en vår for verden hvis vi vil at den skal være brukbar.

Vi bruker eksport direktivet om å avsløre alle offentlige medlemmer av den nevnte pakken:

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

Nå, når noen gjør det krever min modul, vil de ha tilgang til de offentlige typene i vår com.my.package.name pakke, men ikke noen annen pakke.

4.5. Eksport ... Til

Vi kan bruke eksport ... til for å åpne våre offentlige klasser for verden.

Men hva om vi ikke vil at hele verden skal få tilgang til API-en vår?

Vi kan begrense hvilke moduler som har tilgang til APIene våre ved hjelp av eksport ... til direktivet.

I likhet med eksport direktivet, erklærer vi en pakke som eksportert. Men vi lister også opp hvilke moduler vi tillater å importere denne pakken som en krever. La oss se hvordan dette ser ut:

modul my.module {eksport com.my.package.name til com.specific.package; }

4.6. Bruker

EN service er en implementering av et bestemt grensesnitt eller abstrakt klasse som kan være forbrukes av andre klasser.

Vi utpeker tjenestene vår modul bruker med bruker direktivet.

Noter det klassenavnet vi bruk er enten grensesnittet eller abstrakt klasse for tjenesten, ikke implementeringsklassen:

modul my.module {bruker class.name; }

Vi bør merke oss her at det er en forskjell mellom a krever direktivet og bruker direktivet.

Vi skal kanskje krever en modul som gir en tjeneste vi vil konsumere, men den tjenesten implementerer et grensesnitt fra en av dens transitive avhengigheter.

I stedet for å tvinge modulen vår til å kreve alle transitive avhengigheter bare i tilfelle bruker vi bruker direktivet for å legge til det nødvendige grensesnittet til modulbanen.

4.7. Gir ... Med

En modul kan også være en tjenesteleverandør som andre moduler kan konsumere.

Den første delen av direktivet er gir nøkkelord. Her setter vi grensesnittet eller det abstrakte klassenavnet.

Deretter har vi med direktivet hvor vi gir implementeringsklassenavnet som enten redskaper grensesnittet eller strekker den abstrakte klassen.

Slik ser det ut som satt sammen:

modul my.module {gir MyInterface MyInterfaceImpl; }

4.8. Åpen

Vi nevnte tidligere at innkapsling var en drivende motivator for utformingen av dette modulsystemet.

Før Java 9 var det mulig å bruke refleksjon for å undersøke alle typer og medlemmer i en pakke, til og med privat de. Ingenting ble virkelig innkapslet, noe som kan åpne for alle slags problemer for utviklere av bibliotekene.

Fordi Java 9 håndhever sterk innkapsling, Vi må nå eksplisitt gi tillatelse til at andre moduler reflekterer over klassene våre.

Hvis vi vil fortsette å tillate full refleksjon som eldre versjoner av Java gjorde, kan vi ganske enkelt åpen hele modulen opp:

åpen modul my.module {}

4.9. Åpner

Hvis vi trenger å tillate refleksjon av private typer, men vi ikke vil at all koden vår skal eksponeres, vi kan bruke åpnes direktivet om å avsløre spesifikke pakker.

Men husk at dette åpner pakken for hele verden, så sørg for at det er det du vil:

modul my.module {åpner com.my.package; }

4.10. Åpner ... Til

Ok, så refleksjon er bra noen ganger, men vi vil fortsatt ha så mye sikkerhet som vi kan få fra innkapsling. Vi kan selektivt åpne pakkene våre for en forhåndsgodkjent liste over moduler, i dette tilfellet ved hjelp av åpner ... til direktivet:

modul my.module {åpner com.my.package til moduleOne, moduleTwo, etc .; }

5. Kommandolinjealternativer

Nå har støtte for Java 9-moduler blitt lagt til Maven og Gradle, så du trenger ikke å gjøre mye manuell bygging av prosjektene dine. Imidlertid er det fortsatt verdifullt å vite hvordan for å bruke modulsystemet fra kommandolinjen.

Vi bruker kommandolinjen for vårt fulle eksempel nedenfor for å bidra til å styrke hvordan hele systemet fungerer i våre sinn.

  • modul-baneVi bruker –Modul-sti alternativet for å spesifisere modulbanen. Dette er en liste over en eller flere kataloger som inneholder modulene dine.
  • legge til-leser - I stedet for å stole på modulerklæringsfilen, kan vi bruke kommandolinjekvivalenten til krever direktiv; –Legger til.
  • add-eksportKommandolinjebytte for eksport direktivet.
  • tillegg åpnerBytt ut åpen klausul i modulerklæringsfilen.
  • tilleggsmodulerLegger til listen over moduler i standard sett med moduler
  • liste-modulerSkriver ut en liste over alle moduler og deres versjonsstrenger
  • patch-modul - Legg til eller overstyr klasser i en modul
  • illegal tilgang = tillatelse | advare | nekte - Enten slapp av sterk innkapsling ved å vise en enkelt global advarsel, viser hver advarsel, eller mislykkes med feil. Standard er tillate.

6. Sikt

Vi bør bruke litt tid på å snakke om synligheten til koden vår.

Mange biblioteker er avhengige av refleksjon for å utøve sin magi (JUnit og Spring kommer til tankene).

Som standard i Java 9, vil vi kun har tilgang til offentlige klasser, metoder og felt i våre eksporterte pakker. Selv om vi bruker refleksjon for å få tilgang til ikke-offentlige medlemmer og ringe setAccessible (true), vi får ikke tilgang til disse medlemmene.

Vi kan bruke åpen, åpnes, og åpner ... til muligheter for å gi bare kjøretid tilgang til refleksjon. Merk, dette er bare kjøretid!

Vi vil ikke kunne kompilere mot private typer, og vi burde aldri trenge det uansett.

Hvis vi må ha tilgang til en modul for refleksjon, og vi ikke er eieren av den modulen (dvs. vi kan ikke bruke åpner ... til direktiv), så er det mulig å bruke kommandolinjen –Påfylles mulighet for å tillate egne modulers refleksjonstilgang til den låste modulen ved kjøretid.

Den eneste advarselen her er at du trenger å ha tilgang til kommandolinjeargumentene som brukes til å kjøre en modul for at dette skal fungere.

7. Sette det hele sammen

Nå som vi vet hva en modul er og hvordan vi bruker dem, la oss gå videre og bygge et enkelt prosjekt for å demonstrere alle konseptene vi nettopp har lært.

For å gjøre ting enkelt, bruker vi ikke Maven eller Gradle. I stedet vil vi stole på kommandolinjeverktøyene for å bygge modulene våre.

7.1. Sette opp prosjektet vårt

Først må vi sette opp prosjektstrukturen. Vi oppretter flere kataloger for å organisere filene våre.

Start med å lage prosjektmappen:

mkdir modul-prosjekt cd modul-prosjekt

Dette er basen for hele prosjektet vårt, så legg til filer her, for eksempel Maven eller Gradle build-filer, andre kildekataloger og ressurser.

Vi setter også en katalog som inneholder alle våre prosjektspesifikke moduler.

Deretter oppretter vi en modulkatalog:

mkdir enkle moduler

Slik ser prosjektstrukturen vår ut:

modulprosjekt | - // src hvis vi bruker standardpakken | - // build-filer går også på dette nivået | - enkle moduler | - hallo.moduler | - com | - baeldung | - moduler | - hallo | - hoved .app | - com | - baeldung | - moduler | - hoved

7.2. Vår første modul

Nå som vi har den grunnleggende strukturen på plass, la oss legge til vår første modul.

Under enkle moduler katalog, opprett en ny katalog som heter hei. moduler.

Vi kan nevne dette alt vi vil, men følge regler for pakningsnavn (dvs. perioder for å skille ord osv.). Vi kan til og med bruke navnet på hovedpakken vår som modulnavnet hvis vi vil, men vanligvis vil vi holde oss til samme navn som vi ville brukt til å lage en JAR av denne modulen.

Under den nye modulen vår kan vi lage pakkene vi ønsker. I vårt tilfelle skal vi lage en pakkestruktur:

com.baeldung.modules.hello

Deretter oppretter du en ny klasse som heter HelloModules.java i denne pakken. Vi vil holde koden enkel:

pakke com.baeldung.modules.hello; offentlig klasse HelloModules {public static void doSomething () {System.out.println ("Hello, Modules!"); }}

Og til slutt, i hei. moduler rotkatalog, legg til i modulbeskrivelsen vår; module-info.java:

module hello.modules {eksport com.baeldung.modules.hello; }

For å holde dette eksemplet enkelt, alt vi gjør er å eksportere alle offentlige medlemmer av com.baeldung.modules.hello pakke.

7.3. Vår andre modul

Den første modulen vår er flott, men den gjør ingenting.

Vi kan lage en andre modul som bruker den nå.

Under vår enkle moduler katalog, opprett en annen modulkatalog som heter main.app. Vi skal starte med modulbeskrivelsen denne gangen:

modul main.app {krever hallo.modules; }

Vi trenger ikke å utsette noe for omverdenen. I stedet er alt vi trenger å gjøre å avhenge av den første modulen vår, så vi har tilgang til de offentlige klassene den eksporterer.

Nå kan vi lage et program som bruker det.

Opprett en ny pakkestruktur: com.baeldung.modules.main.

Lag nå en ny klassefil som heter MainApp.java.

pakke com.baeldung.modules.main; importere com.baeldung.modules.hello.HelloModules; public class MainApp {public static void main (String [] args) {HelloModules.doSomething (); }}

Og det er all koden vi trenger for å demonstrere moduler. Vårt neste trinn er å bygge og kjøre denne koden fra kommandolinjen.

7.4. Bygg våre moduler

For å bygge prosjektet vårt kan vi lage et enkelt bash-skript og plassere det i roten til prosjektet vårt.

Lag en fil som heter kompilere-enkle-moduler.sh:

#! / usr / bin / env bash javac -d outDir --module-source-path simple-modules $ (finn simple-modules -name "* .java")

Det er to deler til denne kommandoen, javac og finne kommandoer.

De finne kommandoen er ganske enkelt å legge ut en liste over alle.java filer under katalogen vår for enkle moduler. Vi kan deretter mate den listen direkte inn i Java-kompilatoren.

Det eneste vi må gjøre annerledes enn de eldre versjonene av Java, er å tilby en modul-kilde-bane parameter for å informere kompilatoren om at den bygger moduler.

Når vi kjører denne kommandoen, vil vi ha en outDir mappe med to kompilerte moduler inni.

7.5. Kjører koden vår

Og nå kan vi endelig kjøre koden vår for å bekrefte at modulene fungerer som de skal.

Opprett en annen fil i roten til prosjektet: run-simple-module-app.sh.

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

For å kjøre en modul, må vi gi minst modul-bane og hovedklassen. Hvis alt fungerer, bør du se:

> $ ./run-simple-module-app.sh Hei, moduler!

7.6. Legge til en tjeneste

Nå som vi har en grunnleggende forståelse av hvordan vi bygger en modul, la oss gjøre det litt mer komplisert.

Vi skal se hvordan vi bruker gir ... med og bruker direktiver.

Start med å definere en ny fil i hei. moduler modul navngitt Hei grensesnitt.java:

offentlig grensesnitt HelloInterface {void sayHello (); }

For å gjøre ting enkelt, skal vi implementere dette grensesnittet med det eksisterende HelloModules.java klasse:

offentlig klasse HelloModules implementerer HelloInterface {public static void doSomething () {System.out.println ("Hello, Modules!"); } offentlig tomrom siHello () {System.out.println ("Hei!"); }}

Det er alt vi trenger å gjøre for å skape en service.

Nå må vi fortelle verden at modulen vår tilbyr denne tjenesten.

Legg til følgende i vår module-info.java:

gir com.baeldung.modules.hello.HelloInterface med com.baeldung.modules.hello.HelloModules;

Som vi kan se, erklærer vi grensesnittet og hvilken klasse som implementerer det.

Deretter må vi konsumere dette service. I vår main.app modul, la oss legge til følgende i vår module-info.java:

bruker com.baeldung.modules.hello.HelloInterface;

Til slutt, i vår hovedmetode kan vi bruke denne tjenesten via en ServiceLoader:

Iterable services = ServiceLoader.load (HelloInterface.class); HelloInterface-tjeneste = services.iterator (). Neste (); service.sayHello ();

Kompilere og kjør:

#> ./run-simple-module-app.sh Hei, moduler! Hallo!

Vi bruker disse direktivene for å være mye mer eksplisitte om hvordan koden vår skal brukes.

Vi kan sette implementeringen i en privat pakke mens vi eksponerer grensesnittet i en offentlig pakke.

Dette gjør koden vår mye sikrere med veldig lite ekstra overhead.

Gå videre og prøv noen av de andre direktivene for å lære mer om moduler og hvordan de fungerer.

8. Legge til moduler i den navneløse modulen

Det navngitte modulkonseptet ligner på standardpakken. Derfor regnes det ikke som en ekte modul, men kan sees på som standardmodulen.

Hvis en klasse ikke er medlem av en navngitt modul, vil den automatisk bli vurdert som en del av denne ikke navngitte modulen.

Noen ganger, for å sikre spesifikke plattform-, bibliotek- eller tjenesteleverandørmoduler i modulgrafen, må vi legge til moduler til standard rotsett. For eksempel, når vi prøver å kjøre Java 8-programmer som de er med Java 9-kompilatoren, kan det hende vi trenger å legge til moduler.

Generelt, muligheten for å legge til de navngitte modulene til standardsettet med rotmoduler er – Legge til moduler (,)* hvor er et modulnavn.

For eksempel å gi tilgang til alle java.xml.bind moduler syntaksen ville være:

- legge til moduler java.xml.bind

For å bruke dette i Maven, kan vi legge det samme til maven-compiler-plugin:

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

9. Konklusjon

I denne omfattende veiledningen fokuserte vi på og dekket det grunnleggende i det nye Java 9 Module-systemet.

Vi startet med å snakke om hva en modul er.

Deretter snakket vi om hvordan vi skulle oppdage hvilke moduler som er inkludert i JDK.

Vi dekket også detaljerklæringsfilen.

Vi avrundet teorien ved å snakke om de forskjellige kommandolinjeargumentene vi trenger for å bygge modulene våre.

Til slutt omsatte vi all vår tidligere kunnskap og skapte en enkel applikasjon bygget på modulsystemet.

For å se denne koden og mer, må du sjekke den på Github.


$config[zx-auto] not found$config[zx-overlay] not found