Java命令行开发工具-JCommander 使用

前言(有点长)

工作中开发需求,需要设计一个引擎包,将内部工具封装起来,为插件或者 desktop 应用提供核心功能。项目开启时间较为紧迫,从熟悉的 java 着手研究……
首先,应该思考 java 的 main 方法,程序的入口,idea 开发习惯了,老是 psvm 快捷方式创建,但是其中的原理,对于每个关键词的理解,是否只能以 main 方法为程序入口……思考的就远了

public static void main(String[] args){
	/**
	*	public 权限修饰符,公共的,在任何地方都有调用的权限,没有包、类、继承关系的限制
	*	static 静态 关键字,被 static 修饰的成员或方法不需要创建对象就能通过类名调用,存在生命周期较长
	*	void  关键字,没有返回值
	*	String[] args  参数,字符串数组
	*/
}

之前在最开始学 c语言开发的时候,提出过一个问题,程序的入口必须是 main 方法吗?我可以自定义程序的入口吗?…… 学过汇编语言的朋友应该会知道,方法(函数)的调用,就是通过 call 指令,跳转到指定的内存地址,c 代码经过编译之后,转成 汇编语言,汇编程序设定的 main 函数的入口就是指定的内存地址, 如果我们修改 内存地址的值为自定义的函数起始地址,那就可以指定函数入口了…… 说的有点远了

回到 main 方法本身,谁来调用它,并且给它传参?毫无疑问 是Java虚拟机
String[] args 参数,来接收执行java命令时所运行的类的参数

java -jar test.jar --help

“–help” 就是作为参数,由String[] args接收

技术选型 – JCommander

官网:http://jcommander.org/ 具体使用参考 官网,且已有很多中文版翻译,可参考等,网上挺多基本解释的

Java集成使用

依赖

<dependency>
            <groupId>com.beust</groupId>
            <artifactId>jcommander</artifactId>
            <version>1.71</version>
        </dependency>

以下是我在使用过程中做的一些封装,自己使用,做记录的
MyJcmd 主类,程序入口类

package org.example;

public class MyJcmd {

    public static void main(String[] args) {
        StatusCode statusCode;
        if (args.length > 0){
            MyJcmdParserResult result = MyJcmdParserResult.extractParameter(new MyJcmdParameter(),args );
            if (result.isHelp()){
                MyJcmdHelp.showHelpInfo(result.getParameter());
                statusCode = StatusCode.OK;
            }else if (result.isVersion()){
                System.out.println(MyJcmdVersion.VERSION);
                statusCode = StatusCode.OK;
            }else if (!result.isError()){
                // todo 就可以执行设定的命令操作了,操作返回 状态码表示命令 action 的运行成功或者失败
                statusCode = StatusCode.OK;
            }else {
                System.err.println("Please use --help for help");
                statusCode = StatusCode.ERROR;
            }
            if (statusCode == StatusCode.OK){
                // 正常结束
                System.exit(0);
            }
        }
        // 异常结束
        System.exit(1);
    }

    enum  StatusCode{
        OK,
        ERROR,
        ;
    }
}

对 结果的封装类

package org.example;

import com.beust.jcommander.JCommander;
import com.beust.jcommander.ParameterException;
import lombok.Data;

@Data
public class MyJcmdParserResult {

    private MyJcmdParameter parameter;

    private ParameterException exception;

    public MyJcmdParserResult(MyJcmdParameter parameter){
        this.parameter = parameter;
    }

    public MyJcmdParserResult(ParameterException e){
        this.exception = e;
    }

    public boolean isError(){
        return exception != null;
    }

    public boolean isHelp(){
        return !isError() && parameter.isHelp();
    }

    public boolean isVersion(){
        return !isError() && parameter.isVersion();
    }

    public boolean isDebug(){
        return !isError() && parameter.isDebug();
    }

    /**
     *  解析命令行输入的参数
     * @param parameter  参数对象,将输入的命令数组转为对象
     * @param args 命令数组
     * @return
     */
    public static MyJcmdParserResult extractParameter(MyJcmdParameter parameter,String... args){
        JCommander jCommander = new JCommander();
        jCommander.addCommand(parameter);
        jCommander.setProgramName("example");
        try {
            jCommander.parse(args);
            return new MyJcmdParserResult(parameter);
        } catch (ParameterException e) {
            e.printStackTrace();
            return new MyJcmdParserResult(e);
        }

    }

}

处理 help 信息

package org.example;

import com.beust.jcommander.JCommander;

import java.util.List;

public class MyJcmdHelp {

    public static void showHelpInfo(MyJcmdParameter parameter){
        List<String> parameters = parameter.getParameters();
        String info;
        if (!parameters.isEmpty()){
            JCommander jCommander = new JCommander();
            jCommander.addCommand(parameter);
            jCommander.usage();
            return;
        }else {
            info = "No application operate,Please --help";
        }
        System.err.println(info);

    }
}

处理 version

package org.example;

import lombok.Data;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

@Data
public final class MyJcmdVersion {


    public static final String VERSION;

    public static final String UNKNOWN_VERSION = "unknown";

    static {
        String version = UNKNOWN_VERSION;
        try (InputStream stream = MyJcmdVersion.class.getResourceAsStream("/META-INF/maven/org.example/**/pom.properties")){
        // 此处的 ** 是 你的包 pom 文件中 artifactId 的内容,可以打包后解压展开查看
            if (stream != null){
                Properties properties = new Properties();
                properties.load(stream);
                version = properties.getProperty("version");
            }
        } catch (IOException e) {
            System.err.println("cannot get the version");

        }
        VERSION = version;
    }

    private MyJcmdVersion(){
        throw new AssertionError("cannot get the version");
    }
}

参数类

package org.example;

import com.beust.jcommander.Parameter;
import lombok.Data;

import java.util.ArrayList;
import java.util.List;

@Data
public class MyJcmdParameter {

//	todo 写在前边,参数可支持的类型有很多,可以到官网查看要使用的类型,一般 String 较多

    @Parameter(description = "命令前缀")
    List<String> parameters = new ArrayList<>();


    @Parameter(names = {"--help","-h"},
        description = "帮助"
    )
    private boolean help;
    @Parameter(names = {"--version","-v"},
        description = "版本"
    )
    private boolean version;

    @Parameter(names = {"--debug","-debug"},
        description = "调试模式"
    )
    private boolean debug;

    //todo  这里可以定义属性,接收参数的形式
    // 或者,定义其他各个操作的类接收的参数,继承此类,各个类有自己的参数管理

}

这是打包后,写的 mycmd.bat 批处理文件内容,

@echo off
set TOPDIR="%~dp0.."
set EXCUDIR="%~dp0"
set MAIN_CLASS=org.example.MyJcmd
set path=%TOPDIR%\jdk\bin;%EXCUDIR%;%PATH%

java -classpath %TOPDIR%\lib\* %OPTS% %MAIN_CLASS% %*

可以直接在控制台执行

mycmd --help

就可以使用了
注意,bat 文件和 jar 包的路径,我是这样的路径:

  • bin
  • mycmd.bat
  • lib
  • mycmd.jar

注意写脚本的相对路径位置,jar包的名称无所谓可以随便取,不一定非要和 bat 文件名称相同

下面提供一种在 idea 中可以直接调试使用的方式

java命令行工具类库 java编写命令行工具_java


ok,结束!