Een CLI met Spring Shell

1. Overzicht

Simpel gezegd, het Spring Shell-project biedt een interactieve shell voor het verwerken van opdrachten en het bouwen van een complete CLI met behulp van het Spring-programmeermodel.

In dit artikel zullen we de functies, sleutelklassen en annotaties verkennen en verschillende aangepaste opdrachten en aanpassingen implementeren.

2. Maven Afhankelijkheid

Eerst moeten we de spring-shell afhankelijkheid van onze pom.xml:

 org.springframework.shell spring-shell 1.2.0.RELEASE 

De nieuwste versie van dit artefact is hier te vinden.

3. Toegang tot de Shell

Er zijn twee belangrijke manieren om toegang te krijgen tot de shell in onze applicaties.

De eerste is om de shell te booten in het toegangspunt van onze applicatie en de gebruiker de commando's te laten invoeren:

public static void main (String [] args) gooit IOException {Bootstrap.main (args); }

De tweede is om een JLineShellComponent en voer de opdrachten programmatisch uit:

Bootstrap bootstrap = nieuwe Bootstrap (); JLineShellComponent shell = bootstrap.getJLineShellComponent (); shell.executeCommand ("help");

We gaan de eerste benadering gebruiken omdat deze het meest geschikt is voor de voorbeelden in dit artikel, maar in de broncode kun je testcases vinden die de tweede vorm gebruiken.

4. Commando's

Er zijn al verschillende ingebouwde commando's in de shell, zoals Doorzichtig, helpen, Uitgang, enz., die de standaardfunctionaliteit van elke CLI bieden.

Aangepaste opdrachten kunnen worden weergegeven door methoden toe te voegen die zijn gemarkeerd met de @CliCommand annotatie in een Spring-component die de CommandMarker koppel.

Elk argument van die methode moet worden gemarkeerd met een @CliOption annotatie, als we dit niet doen, zullen we verschillende fouten tegenkomen bij het uitvoeren van de opdracht.

4.1. Commando's aan de shell toevoegen

Eerst moeten we de shell laten weten waar onze commando's zijn. Hiervoor is het bestand nodig META-INF / spring / spring-shell-plugin.xml om daar aanwezig te zijn in ons project, kunnen we de component scanfunctionaliteit van Spring gebruiken:

Zodra de componenten zijn geregistreerd en geïnstantieerd door Spring, worden ze geregistreerd bij de shell-parser en worden hun annotaties verwerkt.

Laten we twee eenvoudige opdrachten maken, een om de inhoud van een URL te pakken en weer te geven, en een andere om die inhoud in een bestand op te slaan:

@Component public class SimpleCLI implementeert CommandMarker {@CliCommand (value = {"web-get", "wg"}) public String webGet (@CliOption (key = "url") String url) {return getContentsOfUrlAsString (url); } @CliCommand (waarde = {"web-save", "ws"}) public String webSave (@CliOption (key = "url") String url, @CliOption (key = {"out", "file"}) String file) {String inhoud = getContentsOfUrlAsString (url); probeer (PrintWriter out = nieuwe PrintWriter (bestand)) {out.write (inhoud); } retourneer "Gereed."; }}

Merk op dat we meer dan één string kunnen doorgeven aan de waarde en sleutel attributen van @CliCommand en @CliOption Dit stelt ons in staat om verschillende commando's en argumenten te tonen die zich hetzelfde gedragen.

Laten we nu eens kijken of alles werkt zoals verwacht:

spring-shell> web-get --url //www.google.com web-save --url //www.google.com - uit content.txt Klaar.

4.2. Beschikbaarheid van opdrachten

We kunnen de @CliAvailabilityIndicator annotatie op een methode die een boolean om tijdens runtime te wijzigen of een commando aan de shell moet worden blootgesteld.

Laten we eerst een methode maken om de beschikbaarheid van het web-opslaan opdracht:

private boolean adminEnableExecuted = false; @CliAvailabilityIndicator (waarde = "web-save") openbare boolean isAdminEnabled () {return adminEnableExecuted; }

Laten we nu een commando maken om het adminEnableUitgevoerd variabele:

@CliCommand (value = "admin-enable") openbare String adminEnable () {adminEnableExecuted = true; retourneer "Admin-opdrachten ingeschakeld."; }

Laten we het tot slot verifiëren:

spring-shell> web-save --url //www.google.com --out content.txt Commando 'web-save --url //www.google.com --out content.txt' is gevonden maar is niet momenteel beschikbaar (typ 'help' en vervolgens ENTER om meer te weten te komen over dit commando) spring-shell> admin-enable Admin-commando's ingeschakeld. spring-shell> web-save --url //www.google.com --uit content.txt Klaar.

4.3. Vereiste argumenten

Standaard zijn alle opdrachtargumenten optioneel. We kunnen ze echter verplicht maken met de verplicht attribuut van de @CliOption annotatie:

@CliOption (key = {"out", "file"}, verplicht = waar)

Nu kunnen we testen dat als we het niet introduceren, dit resulteert in een fout:

spring-shell> web-save --url //www.google.com Je moet de optie (--out) specificeren voor dit commando

4.4. Standaardargumenten

Een lege sleutel waarde voor een @CliOption maakt dat argument de standaard. Daar ontvangen we de waarden die in de shell zijn geïntroduceerd en die geen deel uitmaken van een genoemd argument:

@CliOption (key = {"", "url"})

Laten we nu eens kijken of het werkt zoals verwacht:

spring-shell> web-get //www.google.com 

4.5. Gebruikers helpen

@CliCommand en @CliOption annotaties bieden een helpen attribuut waarmee we onze gebruikers kunnen begeleiden bij het gebruik van het ingebouwde helpen commando of bij het tabben om automatische aanvulling te krijgen.

Laten we onze web-get om aangepaste helpberichten toe te voegen:

@CliCommand (// ... help = "Toont de inhoud van een URL") public String webGet (@CliOption (// ... help = "URL waarvan de inhoud wordt weergegeven.") String url) {//. ..}

Nu kan de gebruiker precies weten wat onze opdracht doet:

spring-shell> help web-get Trefwoord: web-get Trefwoord: wg Beschrijving: Geeft de inhoud van een URL weer. Trefwoord: ** standaard ** Trefwoord: url Help: URL waarvan de inhoud wordt weergegeven. Verplicht: false Standaard indien gespecificeerd: '__NULL__' Standaard indien niet gespecificeerd: '__NULL__' * web-get - Toont de inhoud van een URL. * wg - Geeft de inhoud van een URL weer.

5. Maatwerk

Er zijn drie manieren om de shell aan te passen door het BannerProvider, PromptProvider en HistoryFileNameProvider interfaces, allemaal met reeds geleverde standaardimplementaties.

We moeten ook de @Bestellen annotatie om onze providers voorrang te geven op die implementaties.

Laten we een nieuwe banner maken om met onze aanpassing te beginnen:

@Component @Order (Ordered.HIGHEST_PRECEDENCE) openbare klasse SimpleBannerProvider breidt DefaultBannerProvider uit {openbare String getBanner () {StringBuffer buf = nieuwe StringBuffer (); buf.append ("=========================================") .append (OsUtils .LINE_SEPARATOR); buf.append ("* Baeldung Shell *") .append (OsUtils.LINE_SEPARATOR); buf.append ("=========================================") .append (OsUtils .LINE_SEPARATOR); buf.append ("Versie:") .append (this.getVersion ()); retourneer buf.toString (); } public String getVersion () {return "1.0.1"; } public String getWelcomeMessage () {return "Welcome to Baeldung CLI"; } public String getProviderName () {retourneer "Baeldung Banner"; }}

Merk op dat we ook het versienummer en welkomstbericht kunnen wijzigen.

Laten we nu de prompt wijzigen:

@Component @Order (Ordered.HIGHEST_PRECEDENCE) openbare klasse SimplePromptProvider breidt DefaultPromptProvider uit {openbare String getPrompt () {retourneer "baeldung-shell"; } public String getProviderName () {retourneer "Baeldung Prompt"; }}

Laten we tot slot de naam van het geschiedenisbestand wijzigen:

@Component @Order (Ordered.HIGHEST_PRECEDENCE) openbare klasse SimpleHistoryFileNameProvider breidt DefaultHistoryFileNameProvider uit {openbare String getHistoryFileName () {retourneer "baeldung-shell.log"; } public String getProviderName () {retourneer "Baeldung History"; }}

Het geschiedenisbestand zal alle commando's opnemen die in de shell zijn uitgevoerd en zal naast onze applicatie worden geplaatst.

Met alles op zijn plaats, kunnen we onze schil bellen en hem in actie zien:

======================================= * Baeldung Shell * ======= ================================ Versie: 1.0.1 Welkom bij Baeldung CLI baeldung-shell>

6. Omvormers

Tot nu toe hebben we alleen eenvoudige typen gebruikt als argumenten voor onze opdrachten. Veel voorkomende typen zoals Geheel getal, Datum, Enum, het dossier, enz., hebben al een standaardconverter geregistreerd.

Door het Converter interface, kunnen we ook onze converters toevoegen om aangepaste objecten te ontvangen.

Laten we een converter maken die een Draad in een URL:

@Component public class SimpleURLConverter implementeert Converter {openbare URL convertFromText (String-waarde, Class requiredType, String optionContext) {return nieuwe URL (waarde); } openbare boolean getAllPossibleValues ​​(Lijstaanvullingen, Class requiredType, String bestaandeData, String optionContext, MethodTarget target) {return false; } openbare booleaanse ondersteuning (Class requiredType, String optionContext) {return URL.class.isAssignableFrom (requiredType); }}

Laten we tot slot onze web-get en web opslaan commando's:

public String webSave (... URL url) {// ...} public String webSave (... URL url) {// ...}

Zoals je misschien al geraden hebt, gedragen de commando's zich hetzelfde.

7. Conclusie

In dit artikel hebben we kort ingegaan op de kernfuncties van het Spring Shell-project. We waren in staat om onze commando's bij te dragen en de shell aan te passen met onze providers, we hebben de beschikbaarheid van commando's aangepast aan verschillende runtime-omstandigheden en een eenvoudige typeconverter gemaakt.

De volledige broncode voor dit artikel is te vinden op GitHub.