从Commons CLI迁移到picocli
最初于2002年發布的Apache Commons CLI可能是使用最廣泛的Java命令行解析器,但是它的API顯示了它的年齡。 尋找具有最少樣板代碼的現代方法的應用可能對picocli感興趣。 為什么要花麻煩的錢進行遷移,以及如何將基于Commons CLI的應用程序遷移到picocli? Picocli提供了一種流暢的API,具有強大的輸入功能,ANSI顏色的使用幫助,自動補全功能以及許多其他功能。 讓我們以Checkstyle為例。
為什么要遷移?
從Commons CLI遷移到picocli是否值得解決? 從一個命令行解析器轉到另一個命令行解析器有什么好處? 這不僅僅是重新裝修我們應用程序的客廳嗎?
最終用戶體驗
對最終用戶有什么好處?
命令行完成 。 基于Picocli的應用程序可以在bash和zsh shell以及基于JLine的交互式shell應用程序中具有命令行完成功能 。
美麗,易讀的用法幫助消息。 Commons CLI生成的使用幫助有點簡單。 picocli開箱即用地生成幫助,該幫助使用ANSI樣式和顏色進行對比以強調命令,選項和參數等重要信息。 使用注釋可以輕松自定義幫助消息的布局。 此外,如果您需要其他幫助,還有一個幫助API。 有關一些示例屏幕截圖,請參見picocli 自述文件 。
通過@ -files或“ argument files”支持非常大的命令行 。 有時用戶需要指定比操作系統或外殼程序支持的命令行更長的命令行。 當picocli遇到以字符@開頭的參數時,它將文件的內容擴展到參數列表中。 這使應用程序可以處理任意長度的命令行。
開發人員經驗
作為開發人員,對您有什么好處?
通常,picocli應用程序的代碼要比Commons CLI的代碼少得多。 picocli批注允許應用程序以聲明性的方式定義選項和位置參數,所有信息都放在一個位置。 此外,picocli還提供了許多便利,例如類型轉換和自動幫助,這些便利照顧了一些機制,因此應用程序可以將更多精力放在業務邏輯上。 本文的其余部分將更詳細地說明這一點。
文檔 :picocli具有廣泛的用戶手冊和詳細的javadoc 。
故障排除 。 Picocli具有內置的跟蹤工具,以方便進行故障排除。 最終用戶可以使用系統屬性picocli.trace來控制跟蹤級別。 支持的級別為OFF , WARN , INFO和DEBUG 。 默認跟蹤級別為WARN 。
未來擴展
最后,除了立即獲得回報之外,從Commons CLI遷移到picocli還可以獲得任何未來的好處嗎?
Picocli具有許多高級功能 。 您的應用程序可能尚未使用這些功能,但是如果您想將來擴展應用程序,picocli支持嵌套子命令 (以及子子命令到任何深度),可重復使用的mixins ,可輕松與 Dependency Injection容器集成以及一個不斷發展的工具框架,可從picocli CommandSpec模型生成源代碼,文檔和配置文件。
最終,picocli得到了積極維護 ,而Commons CLI似乎在16年內發布了6個版本,幾乎處于停滯狀態。
遷移示例:CheckStyle
命令行應用程序需要做三件事:
讓我們以CheckStyle的com.puppycrawl.tools.checkstyle.Main命令行實用程序為例,比較在Commons CLI和picocli中如何完成此操作。
完整的源代碼之前和之后的遷移是在GitHub上。
定義選項和位置參數
使用Commons CLI定義選項
Commons CLI有多種定義選項的方式: Options.addOption ,構造一個new Options(…?)并在此對象,不建議使用的OptionBuilder類和推薦的Option.Builder類上調用方法。
Checkstyle Main類使用Options.addOption方法。 首先為選項名稱定義一些常量:
/** Name for the option 's'. */ private static final String OPTION_S_NAME = "s";/** Name for the option 't'. */ private static final String OPTION_T_NAME = "t";/** Name for the option '--tree'. */ private static final String OPTION_TREE_NAME = "tree";... // and more. Checkstyle Main has 26 options in total.Main.buildOptions方法使用以下常量來構造和返回定義了支持的選項的Commons CLI Options對象:
private static Options buildOptions() {final Options options = new Options();options.addOption(OPTION_C_NAME, true, "Sets the check configuration file to use.");options.addOption(OPTION_O_NAME, true, "Sets the output file. Defaults to stdout");...options.addOption(OPTION_V_NAME, false, "Print product version and exit");options.addOption(OPTION_T_NAME, OPTION_TREE_NAME, false,"Print Abstract Syntax Tree(AST) of the file");...return options; }使用Picocli定義選項
在picocli中,您可以使用類似于Commons CLI方法的構建器以編程方式定義支持的選項,也可以使用注釋以聲明方式定義支持的選項。
對于并非事先知道所有選項的動態應用程序,Picocli的編程API可能會有用。 如果您對編程方法感興趣,請查看CommandSpec , OptionSpec和PositionalParamSpec類。 另請參閱Programmatic API 。
在本文中,我們將使用picocli批注。 對于CheckStyle示例,這看起來類似于以下內容:
@Option(names = "-c", description = "Sets the check configuration file to use.") private File configurationFile;@Option(names = "-o", description = "Sets the output file. Defaults to stdout") private File outputFile;@Option(names = "-v", versionHelp = true, description = "Print product version and exit") private boolean versionHelpRequested;@Option(names = {"-t", "--tree"}, description = "Print Abstract Syntax Tree(AST) of the file") private boolean printAST;比較方式
陳述式
使用Commons CLI,您可以通過調用具有String值的方法來構建規范。 此類API的一個缺點是,良好的樣式會迫使客戶端代碼定義常量以避免“不可思議的值”,就像Checkstyle Main類盡職盡責。
使用picocli,所有信息都集中在一個地方。 注釋僅接受String文字,因此定義和用法會自動放在一起,而無需聲明常量。 這樣可以使代碼更簡潔,更少。
強類型
Commons CLI使用布爾標志來表示該選項是否帶有參數。
Picocli使您可以直接使用類型。 根據類型,picocli“知道”該選項需要多少個參數: boolean字段沒有參數, Collection , Map和array字段可以有零個到任意數量的參數,任何其他類型意味著這些選項只需要一個參數論點。 可以對其進行自定義(請參閱arity ),但是大多數情況下,默認值就足夠了。
Picocli鼓勵您將enum類型用于帶有有限有效值集的選項或位置參數。 picocli不僅會為您驗證輸入,還可以使用@Option(description = "Valid values: ${COMPLETION-CANDIDATES}")在使用幫助消息中顯示所有值 。 枚舉還允許命令行補全功能為選項值建議補全候選值。
更少的代碼
Picocli 將選項參數String值轉換為字段類型。 它不僅可以節省應用程序的工作量,而且還可以對用戶輸入進行最小程度的驗證。 如果轉換失敗,則會引發ParameterException并顯示用戶友好的錯誤消息。
讓我們看一個例子,看看這有多有用。 Checkstyle Main類定義了-x , --exclude-regexp exclude --exclude-regexp選項,該選項允許用于指定要排除的目錄的多個正則表達式。
使用Commons CLI,您需要將在命令行上匹配的String值轉換為應用程序java.util.regex.Pattern對象:
/*** Gets the list of exclusions from the parse results.* @param commandLine object representing the result of parsing the command line* @return List of exclusion patterns.*/ private static List<Pattern> getExclusions(CommandLine commandLine) {final List<Pattern> result = new ArrayList<>();if (commandLine.hasOption(OPTION_X_NAME)) {for (String value : commandLine.getOptionValues(OPTION_X_NAME)) {result.add(Pattern.compile(value));}}return result; }根據合同,在picocli中,您只需在List<Pattern> (或Pattern[]數組)字段上聲明該選項。 由于picocli具有java.util.regex.Pattern的內置轉換器,因此只需聲明該選項即可。 轉換代碼完全消失了。 如果在命令行上指定了一個或多個-x選項,Picocli將實例化并填充列表。
/** Option that allows users to specify a regex of paths to exclude. */ @Option(names = {"-x", "--exclude-regexp"},description = "Regular expression of directory to exclude from CheckStyle") private List<Pattern> excludeRegex;選項名稱
Commons CLI支持“短”和“長”選項,例如-t和--tree 。 這并不總是您想要的。
Picocli允許選項具有任意數量的名稱和任意前綴。 例如,這在picocli中是完全可以的:
@Option(names = {"-cp", "-classpath", "--class-path"})位置參數
在Commons CLI中,您無法預先定義位置參數。 相反,其CommandLine解析結果類具有方法getArgs ,該方法以字符串數組形式返回位置參數。 Checkstyle Main類使用它來創建要處理的File對象的列表。
在picocli中, 位置參數是一等公民,例如命名選項。 不僅可以強類型化它們,而且位于不同位置的參數可以具有不同的類型,并且每個參數將在用法幫助消息中列出單獨的條目和描述。
例如,Checkstyle Main類需要處理的文件列表,因此我們聲明一個字段并使用@Parameters對其進行@Parameters 。 arity = "1..*"屬性意味著必須至少指定一個文件,否則picocli將顯示有關缺少參數的錯誤消息。
@Parameters(paramLabel = "file", arity = "1..*", description = "The files to process") private List<File> filesToProcess;幫助選項
在Commons CLI中,用必需的選項創建一個具有--help選項的應用程序是非常困難的。 Commons CLI對幫助選項沒有特殊處理,當用戶指定<command> --help時,它將抱怨缺少必需的選項。
Picocli具有對常見(和自定義) 幫助選項的內置支持。
解析命令行參數
Commons CLI具有一個CommandLineParser接口,該接口帶有parse方法,該方法返回代表解析結果的CommandLine 。 然后,應用程序調用CommandLine.hasOption(String)來查看是否設置了標志,或者調用CommandLine.hasOption(String) CommandLine.getOptionValue(String)來獲取選項值。
Picocli在分析命令行參數時填充帶注釋的字段。 Picocli的parse…?方法也返回ParseResult可以上指定的選項并且什么的價值他們有,但大多數應用程序實際上并不需要使用查詢ParseResult類,因為它們可以簡單地檢查該注入的注釋值解析期間的字段。
處理結果
經營理念上白色隔離
解析器完成后,應用程序需要運行其業務邏輯,但首先要檢查一些事情:
- 是否需要版本信息或使用幫助? 如果是這樣,請打印出所需的信息并退出。
- 用戶輸入是否無效? 打印出一條包含詳細信息的錯誤消息,打印使用幫助消息并退出。
- 最終運行業務邏輯–處理業務邏輯引發的錯誤。
使用Commons CLI,這看起來像這樣:
int exitStatus; try {CommandLine commandLine = new DefaultParser().parse(buildOptions(), args);if (commandLine.hasOption(OPTION_VERSION)) { // --versionSystem.out.println("Checkstyle version: " + version());exitStatus = 0;} else if (commandLine.hasOption(OPTION_HELP)) { // --helpprintUsage(System.out);exitStatus = 0;} else {exitStatus = runBusinessLogic(); // business logic} } catch (ParseException pex) { // invalid inputexitStatus = EXIT_WITH_CLI_VIOLATION;System.err.println(pex.getMessage());printUsage(System.err); } catch (CheckstyleException ex) { // business logic exceptionexitStatus = EXIT_WITH_CHECKSTYLE_EXCEPTION_CODE;ex.printStackTrace(); } System.exit(exitStatus);Picocli提供了一些方便的方法來解決以上大部分問題。 通過使您的命令實現Runnable或Callable ,應用程序可以專注于業務邏輯。 最簡單地說,它可能看起來像這樣:
public class Main implements Callable<Integer> {public static void main(String[] args) {CommandLine.call(new Main(), args);}public Integer call() throws CheckstyleException {// business logic here} }Checkstyle Main類需要控制退出代碼,并且對錯誤處理有一些嚴格的內部要求,因此我們最終沒有使用便捷方法,并且使解析結果處理與Commons CLI極為相似。 在picocli待辦事項清單上可以改善這個領域。
使用幫助信息
Picocli在支持的平臺上的使用幫助消息中使用ANSI顏色和樣式。 這不僅看起來不錯,而且還減輕了用戶的認知負擔 :對比度使重要的信息(如命令,選項和參數)從周圍的文本中脫穎而出。
應用程序還可以在使用幫助消息的描述或其他部分中使用帶有簡單標記的ANSI顏色和樣式,例如@|bg(red) text with red background|@ 。 請參閱用戶手冊的相關部分 。
對于CheckStyle,我們將其保持在最低限度,而CheckStyle的結果輸出如下所示:
總結:最后的提示
請注意,即使使用幫助消息僅顯示帶有雙連字符的選項,Commons CLI的默認解析器也可以識別單連字符( - )和雙連字符( -- )長選項。 您需要確定是否繼續支持此操作。
在picocli中@Option(names = "-xxx", hidden = true)如果您想模仿與Commons CLI完全相同的行為,則可以使用@Option(names = "-xxx", hidden = true)聲明帶有單個連字符的長選項:用法中未顯示 picocli 中的隱藏選項幫助信息。
結論
從Commons CLI遷移到picocli可以為最終用戶提供更好的用戶體驗,并且可以為開發人員帶來顯著的好處,即提高其可維護性和未來擴展的潛力。 遷移是手動過程,但相對簡單。
更新:CheckStyle項目接受了本文更改中的拉取請求。 從CheckStyle 8.15開始,其命令行工具將使用picocli。 CheckStyle維護人員對結果感到滿意:
Checkstyle從Apache CLI遷移到@picocli(將在8.15中發布),最后,CLI參數的文檔現在已經以聲明性的方式在代碼中井井有條地組織起來,而Checkstyle的CLI遵循CLI最佳實踐。
 — CheckStyle維護者Roman Ivanov 
翻譯自: https://www.javacodegeeks.com/2018/11/migrating-commons-cli-picocli.html
總結
以上是生活随笔為你收集整理的从Commons CLI迁移到picocli的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: java运行构建期间出错_构建和运行Ja
- 下一篇: 腾讯云阿里云华为云的区别(腾讯云阿里云d
