Lag et Java Command Line-program med Picocli

1. Introduksjon

I denne opplæringen vil vi nærme oss picocli bibliotek, som lar oss enkelt lage kommandolinjeprogrammer i Java.

Vi kommer først i gang med å lage en Hello World-kommando. Deretter tar vi et dypdykk i hovedtrekkene i biblioteket ved å reprodusere, delvis git kommando.

2. Hei verdens kommando

La oss begynne med noe enkelt: en Hello World-kommando!

Første ting først må vi legge til avhengighet til picocli prosjekt:

 info.picocli picocli 3.9.6 

Som vi kan se, bruker vi 3.9.6 versjon av biblioteket, skjønt en 4.0.0 versjonen er under konstruksjon (for øyeblikket tilgjengelig i alfatest).

Nå som avhengigheten er satt opp, la oss opprette Hello World-kommandoen. For å gjøre det, vi bruker @Kommando kommentar fra biblioteket:

@Command (name = "hallo", description = "Sier hallo") offentlig klasse HelloWorldCommand {}

Som vi kan se, kan merknaden ta parametere. Vi bruker bare to av dem her. Hensikten er å gi informasjon om gjeldende kommando og tekst for den automatiske hjelpemeldingen.

For øyeblikket er det ikke mye vi kan gjøre med denne kommandoen. For å få det til å gjøre noe, må vi legge til en hoved- metoden kall bekvemmeligheten CommandLine.run (Runnable, String []) metode. Dette tar to parametere: en forekomst av kommandoen vår, som dermed må implementere Kjørbar grensesnitt, og en String array som representerer kommandoargumentene (alternativer, parametere og underkommandoer):

offentlig klasse HelloWorldCommand implementerer Runnable {public static void main (String [] args) {CommandLine.run (new HelloWorldCommand (), args); } @ Override public void run () {System.out.println ("Hello World!"); }}

Nå, når vi kjører hoved- metode, ser vi at konsollutgangene "Hei Verden!"

Når vi er pakket i en krukke, kan vi kjøre Hello World-kommandoen vår med java kommando:

java -cp "pathToPicocliJar; pathToCommandJar" com.baeldung.picoli.helloworld.HelloWorldCommand

Uten overraskelse gir det også ut "Hei Verden!" streng til konsollen.

3. En konkret brukssak

Nå som vi har sett det grunnleggende, vil vi dykke inn i picocli bibliotek. For å gjøre det, skal vi delvis gjengi en populær kommando: git.

Selvfølgelig vil ikke formålet være å implementere git befale atferd, men å gjengi mulighetene til git kommando - hvilke underkommandoer som finnes og hvilke alternativer som er tilgjengelige for en spesiell underkommando.

Først må vi lage en GitCommand klasse som vi gjorde for vår Hello World-kommando:

@Command offentlig klasse GitCommand implementerer Runnable {public static void main (String [] args) {CommandLine.run (new GitCommand (), args); } @ Override public void run () {System.out.println ("Den populære git-kommandoen"); }}

4. Legge til underkommandoer

De git kommandoen tilbyr mange underkommandoer - legge til, forplikte, fjerne, og mange flere. Vi vil fokusere her på legge til og begå.

Så vårt mål her vil være å erklære de to underkommandoene til hovedkommandoen. Picocli tilbyr tre måter å oppnå dette på.

4.1. Bruker @Kommando Kommentar om klasser

De @Kommando kommentar gir muligheten til å registrere underkommandoer gjennom underkommandoer parameter:

@Command (underkommandoer = {GitAddCommand.class, GitCommitCommand.class})

I vårt tilfelle legger vi til to nye klasser: GitAddCommand og GitCommitCommand. Begge er merket med @Kommando og implementere Kjørbar. Det er viktig å gi dem et navn, da navnene vil bli brukt av picocli å gjenkjenne hvilken underkommando (er) du skal utføre:

@Command (name = "add") offentlig klasse GitAddCommand implementerer Runnable {@Override public void run () {System.out.println ("Legge til noen filer i iscenesettelsesområdet"); }}
@Command (name = "commit") offentlig klasse GitCommitCommand implementerer Runnable {@Override public void run () {System.out.println ("Forpliktelse av filer i iscenesettelsesområdet, hvor fantastisk?"); }}

Dermed, hvis vi kjører vår hovedkommando med legge til som et argument vil konsollen sende ut “Legger til noen filer i iscenesettelsesområdet”.

4.2. Bruker @Kommando Kommentar om metoder

En annen måte å erklære underkommandoer på er å skape @Kommando-anmerkede metoder som representerer disse kommandoene i GitCommand klasse:

@Command (name = "add") public void addCommand () {System.out.println ("Legge til noen filer i iscenesettingsområdet"); } @Command (name = "commit") public void commitCommand () {System.out.println ("Committing files in the staging area, how wonderful?"); }

På den måten kan vi implementere forretningslogikken vår direkte i metodene og ikke opprette separate klasser for å håndtere den.

4.3. Legge til underkommandoer programmatisk

Endelig, picocli gir oss muligheten til å registrere underkommandoer programmatisk. Denne er litt vanskeligere, ettersom vi må lage en Kommandolinje objekt som pakker inn kommandoen vår, og legg deretter til underkommandoer i den:

CommandLine commandLine = ny CommandLine (ny GitCommand ()); commandLine.addSubcommand ("add", ny GitAddCommand ()); commandLine.addSubcommand ("commit", ny GitCommitCommand ());

Etter det må vi fremdeles kjøre kommandoen vår, men vi kan ikke gjøre bruk av CommandLine.run () metoden lenger. Nå må vi ringe parseWithHandler () metode på vår nyopprettede CommandLine gjenstand:

commandLine.parseWithHandler (ny RunLast (), args);

Vi bør merke oss bruken av RunLast klasse, som forteller picocli for å kjøre den mest spesifikke underkommandoen. Det er to andre kommandobehandlere levert av picocli: RunFirst og RunAll. Førstnevnte kjører den øverste kommandoen, mens sistnevnte kjører dem alle.

Når du bruker bekvemmelighetsmetoden CommandLine.run (), den RunLast handler brukes som standard.

5. Administrere alternativer ved hjelp av @Alternativ Kommentar

5.1. Alternativ uten argument

La oss nå se hvordan du legger til noen alternativer i kommandoene våre. Vi vil faktisk fortelle det legge til kommandoen om at den skal legge til alle endrede filer. For å oppnå det, vi legger til et felt som er merket med @Alternativ kommentar til vår GitAddCommand klasse:

@Option (names = {"-A", "--all"}) private boolske allFiles; @ Override public void run () {if (allFiles) {System.out.println ("Legge til alle filer i iscenesettelsesområdet"); } annet {System.out.println ("Legge til noen filer i iscenesettelsesområdet"); }}

Som vi ser, tar merknaden a navn parameter, som gir de forskjellige navnene på alternativet. Derfor kaller du legge til kommando med begge -EN eller -alle vil stille inn alle filer felt til ekte. Så hvis vi kjører kommandoen med alternativet, vil konsollen vises “Legger til alle filer i iscenesettelsesområdet”.

5.2. Alternativ med argument

Som vi nettopp så, for alternativer uten argumenter, blir deres tilstedeværelse eller fravær alltid vurdert til a boolsk verdi.

Det er imidlertid mulig å registrere alternativer som tar argumenter. Vi kan gjøre dette ganske enkelt ved å erklære at feltet vårt er av en annen type. La oss legge til en beskjed alternativ til vår begå kommando:

@Option (names = {"-m", "--message"}) privat strengmelding; @ Override public void run () {System.out.println ("Forpliktelse av filer i iscenesettelsesområdet, hvor flott?"); hvis (melding! = null) {System.out.println ("Forpliktingsmeldingen er" + melding); }}

Ikke overraskende når du får den beskjed alternativet, vil kommandoen vise kommittemeldingen på konsollen. Senere i artikkelen vil vi dekke hvilke typer som håndteres av biblioteket og hvordan andre typer håndteres.

5.3. Alternativ med flere argumenter

Men nå, hva om vi vil at kommandoen vår skal ta flere meldinger, slik det er gjort med det virkelige git begå kommando? Ingen bekymringer, la oss gjøre feltet vårt til et array eller a Samling, og vi er ganske mye ferdige:

@Option (names = {"-m", "--message"}) private String [] meldinger; @ Override public void run () {System.out.println ("Forpliktelse av filer i iscenesettelsesområdet, hvor flott?"); hvis (meldinger! = null) {System.out.println ("Forpliktingsmeldingen er"); for (Strengmelding: meldinger) {System.out.println (melding); }}}

Nå kan vi bruke beskjed alternativ flere ganger:

forplikte -m "Mitt forpliktelse er flott" -m "Mitt forpliktelse er vakkert"

Imidlertid vil vi kanskje også gi alternativet bare én gang og skille de forskjellige parametrene med en regex-avgrenser. Derfor kan vi bruke dele parameteren til @Alternativ kommentar:

@Option (names = {"-m", "--message"}, split = ",") private String [] meldinger;

Nå kan vi passere -m "Mitt forpliktelse er stort", "Mitt forpliktelse er vakkert" for å oppnå samme resultat som ovenfor.

5.4. Nødvendig alternativ

Noen ganger har vi kanskje et alternativ som kreves. De kreves argument, som er standard til falsk, lar oss gjøre det:

@Option (names = {"-m", "--message"}, required = true) private String [] meldinger;

Nå er det umulig å ringe begå kommandoen uten å spesifisere beskjed alternativ. Hvis vi prøver å gjøre det, picocli vil skrive ut en feil:

Mangler nødvendig alternativ '--message =' Bruk: git commit -m = [-m =] ... -m, --message =

6. Administrere posisjonsparametere

6.1. Capture Positional Parameters

La oss nå fokusere på vår legge til kommandoen fordi den ikke er veldig kraftig ennå. Vi kan bare bestemme oss for å legge til alle filer, men hva om vi ønsker å legge til spesifikke filer?

Vi kan bruke et annet alternativ for å gjøre det, men et bedre valg her ville være å bruke posisjonsparametere. Faktisk, posisjonsparametere er ment å fange kommandoargumenter som opptar bestemte posisjoner og verken er underkommandoer eller alternativer.

I vårt eksempel vil dette gjøre oss i stand til å gjøre noe sånt som:

legg til fil1 fil2

For å fange posisjonsparametere, vi vil bruke @Parametere kommentar:

@Parameters private Listefiler; @Override public void run () {if (allFiles) {System.out.println ("Legge til alle filer i iscenesettelsesområdet"); } hvis (filer! = null) {files.forEach (sti -> System.out.println ("Legger til" + sti + "til oppstillingsområdet")); }}

Nå vil vår kommando fra tidligere skrive ut:

Legge til file1 i stagingområdet Legge til file2 i stagingområdet

6.2. Ta et delsett av posisjonsparametere

Det er mulig å være mer detaljert om hvilke posisjonsparametere som skal fanges, takket være indeks kommentarens parameter. Indeksen er nullbasert. Således, hvis vi definerer:

@Parameters (index = "2 .. *")

Dette vil fange argumenter som ikke samsvarer med alternativer eller underkommandoer, fra den tredje til slutten.

Indeksen kan enten være et område eller et enkelt tall, som representerer en enkelt posisjon.

7. Et ord om typekonvertering

Som vi har sett tidligere i denne opplæringen, picocli håndterer en eller annen type konvertering av seg selv. For eksempel tilordner den flere verdier til arrays eller Samlinger, men det kan også kartlegge argumenter til bestemte typer som når vi bruker Sti klasse for legge til kommando.

Faktisk, picocli kommer med en haug med forhåndshåndterte typer. Dette betyr at vi kan bruke disse typene direkte uten å måtte tenke på å konvertere dem selv.

Imidlertid kan det hende vi må kartlegge kommandoargumentene våre til andre typer enn de som allerede er håndtert. Heldigvis for oss, dette er mulig takket være ITypeConverter grensesnitt og CommandLine # registerConverter metode, som knytter en type til en omformer.

La oss forestille oss at vi vil legge til config underkommando til vår git kommando, men vi vil ikke at brukere skal endre et konfigurasjonselement som ikke eksisterer. Så vi bestemmer oss for å kartlegge elementene til et enum:

offentlig enum ConfigElement {USERNAME ("user.name"), EMAIL ("user.email"); privat slutt Strengverdi; ConfigElement (strengverdi) {this.value = verdi; } offentlig strengverdi () {returverdi; } offentlig statisk ConfigElement fra (strengverdi) {retur Arrays.stream (verdier ()) .filter (element -> element.value.equals (verdi)) .findFirst () .orElseThrow (() -> ny IllegalArgumentException ("The argumentet "+ verdi +" samsvarer ikke med noen ConfigElement ")); }}

I tillegg, i vår nyopprettede GitConfigCommand klasse, la oss legge til to posisjonsparametere:

@Parameters (index = "0") privat ConfigElement-element; @Parameters (index = "1") privat strengverdi; @ Override public void run () {System.out.println ("Setting" + element.value () + "to" + value); }

På denne måten sørger vi for at brukerne ikke vil kunne endre ikke-eksisterende konfigurasjonselementer.

Til slutt må vi registrere omformeren vår. Det som er vakkert er at hvis vi bruker Java 8 eller høyere, trenger vi ikke en gang å lage en klasse som implementerer ITypeConverter grensesnitt. Vi kan bare sende en lambda eller metodehenvisning til registerConverter () metode:

CommandLine commandLine = ny CommandLine (ny GitCommand ()); commandLine.registerConverter (ConfigElement.class, ConfigElement :: fra); commandLine.parseWithHandler (ny RunLast (), args);

Dette skjer i GitCommand hoved() metode. Merk at vi måtte gi slipp på bekvemmeligheten CommandLine.run () metode.

Når den brukes med et ubehandlet konfigurasjonselement, vil kommandoen vise hjelpemeldingen pluss et stykke informasjon som forteller oss at det ikke var mulig å konvertere parameteren til en ConfigElement:

Ugyldig verdi for posisjonsparameter ved indeks 0 (): kan ikke konvertere 'user.phone' til ConfigElement (java.lang.IllegalArgumentException: Argumentet user.phone samsvarer ikke med noen ConfigElement) Bruk: git config 

8. Integrering med Spring Boot

Til slutt, la oss se hvordan Springify alt det!

Faktisk jobber vi kanskje i et Spring Boot-miljø og ønsker å dra nytte av det i vårt kommandolinjeprogram. For å gjøre det, vi må lage et SpringBootApplicationimplementering av CommandLineRunner grensesnitt:

@SpringBootApplication public class Application implementerer CommandLineRunner {public static void main (String [] args) {SpringApplication.run (Application.class, args); } @ Override public void run (String ... args) {}}

I tillegg til, la oss kommentere alle våre kommandoer og underkommandoer med våren @Komponent kommentar og autowire alt det i vår applikasjon:

privat GitCommand gitCommand; privat GitAddCommand addCommand; privat GitCommitCommand commitCommand; privat GitConfigCommand configCommand; public Application (GitCommand gitCommand, GitAddCommand addCommand, GitCommitCommand commitCommand, GitConfigCommand configCommand) {this.gitCommand = gitCommand; this.addCommand = addCommand; this.commitCommand = commitCommand; this.configCommand = configCommand; }

Merk at vi måtte autoledre hver underkommando. Dessverre er dette fordi, for nå, picocli er ennå ikke i stand til å hente underkommandoer fra vårkonteksten når deklareres erklærende (med merknader). Dermed blir vi nødt til å gjøre den ledningen selv, på en programmatisk måte:

@ Override public void run (String ... args) {CommandLine commandLine = ny CommandLine (gitCommand); commandLine.addSubcommand ("add", addCommand); commandLine.addSubcommand ("commit", commitCommand); commandLine.addSubcommand ("config", configCommand); commandLine.parseWithHandler (ny CommandLine.RunLast (), args); }

Og nå fungerer vårt kommandolinjeprogram som en sjarm med vårkomponenter. Derfor kunne vi lage noen serviceklasser og bruke dem i kommandoene våre, og la Spring ta seg av avhengighetsinjeksjonen.

9. Konklusjon

I denne artikkelen har vi sett noen viktige funksjoner i picocli bibliotek. Vi har lært hvordan du oppretter en ny kommando og legger til noen underkommandoer i den. Vi har sett mange måter å håndtere alternativer og posisjonsparametere på. I tillegg har vi lært hvordan vi implementerer vår egen type omformer for å gjøre kommandoene våre sterkt skrevet. Til slutt har vi sett hvordan du kan bringe Spring Boot inn i kommandoene våre.

Selvfølgelig er det mange ting mer å oppdage om det. Biblioteket gir komplett dokumentasjon.

Når det gjelder den fulle koden i denne artikkelen, kan du finne den på GitHub.


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