今天编写了一个运行在服务端的java工具类,才发现自己以前很少关注运营方面的内容,导致在服务端部署一个java的工具变得异常困难,其实这也是自己对java的了解不够造成的。
首先,当代码编写完成之后,在主类中必须要有main函数,其中的参数非常重要。根据一位同事的说法,除正常的执行程序之外,其中至少要包含两种参数:-v(软件版本,以及作者等介绍信息),-h(软件的帮助信息,良好的帮助文档能够帮助使用本软件的人能够很容易地学会其基本用法)。
main函数编写完成后,需要在build的时候,指定对应的jar包中的主类型,本部分是用maven构建的,因此需要在pom文件中加入定义:
<build> <finalName>daily-report-transfer</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> <mainClass>Main Class Name</mainClass> </manifest> </archive> </configuration> </plugin> </plugins> </build>
此后,build完之后的MANIFEST.MF文件中就会包含描述main的一行数据:
Main-Class: Main Class Name
java命令运行时,可以在没有写入Main-Class的时候,使用加入ClassPath的方式运行(其中windows下分隔符用;Linux和mac下分隔符为:):
java –cp 类路径;要执行的主类 参数...
如果写入的Main-Class,就可以直接使用java –jar 命令运行jar包中的主类,后面接具体的参数。
但是如果使用java –jar的方式运行,也就意味着不能够加入额外的-cp,即加入不了其他的jar包,如果项目中使用了其他的jar包该怎么办?这就需要将所有的依赖项加入到打好的jar包中。
在maven中,同样可以添加plugin标签内容来生成包含依赖jar包中类型的方法,使用如下的手段:
<plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <archive> <manifest> <mainClass>Main Class Name</mainClass> </manifest> </archive> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin>
此时就会根据标签descriptorRef中的内容生成含依赖项的jar包,可以使用java –jar的方式运行。
在运行时,我们通常需要指定的运行文件路径为相对路径而非绝对路径,这就需要使用File的相关相对路径的方法,其实File对象本身就支持相对运行路径,但最好使用ClassLoader能够识别的文件路径,通过Class.getResourceAsStream的形式访问。
关于程序的输出,有三种形式,即system out, system err以及exit代码。在Linux系统中,程序的输出流有两种:正常输出以及异常输出,分别用1和2表示。即我们在执行一个linux命令时,如果没有指定对应的输出文件,就会将正常输出以及异常输出都输出至控制台Console中,可以使用>或>>的方式,将其输出至指定的文件,在输出时1>(或>>)会将java中system out的内容输出指定文件,2>(或>>)会将sytem err的内容输出至指定文件。
我们在java程序中当然可以System.setOut(...),System.setErr(...)的方式在程序中将输出流重置,这就相当于重置一个静态变量,这样所有的输出都会重置到这个流中。值得注意的是,一定要保留程序的异常堆栈,将其正常地输出至文件或者控制台中,以便查看问题。异常的输出方式不仅有e.printStackTrace()方法,这就相当于将错误堆栈输出到控制台中,可以通过e.printStackTrace(PrintStream)的方式定向到指定的输出流中。
我们都指定命令有返回值,通过$?的方式得到上个程序的返回值,在java中如何确定呢?这就要通过System.exit(返回值)的方式,注意这句话要在程序运行完成时才能调用,因为调用完成后,java虚拟机就会退出。
此外,尤其需要注意的是,我们的java程序可能会在多种平台下运行,了解一些关于系统的差异属性非常必要,这里System.getProperty就能够得到我们需要的一系列属性:
属性名称 |
含义 |
java.version |
运行时环境版本 |
java.vendor |
运行时环境供应商 |
java.vendor.url |
供应商url |
java.home |
java安装目录 |
java.vm.specification.version |
虚拟机规范版本 |
java.vm.specification.vendor |
虚拟机规范供应商 |
java.vm.specification.name |
虚拟机规范名称 |
java.vm.version |
虚拟机实现版本 |
java.vm.vendor |
虚拟机实现供应商 |
java.vm.name |
虚拟机实现名称 |
java.specification.version |
运行时环境版本 |
java.specification.vendor |
运行时环境规范供应商 |
java.specification.name |
运行时环境规范名称 |
java.class.version |
类格式版本号 |
java.class.path |
Java类路径 |
java.library.path |
加载库时搜索的路径列表 |
java.io.tmpdir |
默认的临时文件路径,在创建临时文件时会使用到 |
java.compiler |
使用的jit编译器名称 |
java.ext.dirs |
一个或多个扩展目录的路径 |
os.name |
操作系统名称 |
os.arch |
操作系统的架构 |
os.version |
操作系统版本 |
file.separator |
文件分隔符 |
path.separator |
路径分隔符(win是;linux是:) |
line.separator |
行分隔符 |
user.name |
用户的账户名称 |
user.home |
用户的主目录 |
user.dir |
用户当前的工作目录 |
这其中,又以最下面的几个属性最为有用,在编写跨平台的应用程序时,尤其注意控制文件分隔符,行分隔符等操作。下面是在本机(mac系统)中执行后的结果:
os_name:Mac OS X os_arch:x86_64 os_version:10.9.3 user_dir:/Users/xxx/develop/workspace/github/dailyreport-analyse java_vm_specification_version:1.0 java_vm_specification_vendor:Sun Microsystems Inc. java_vm_specification_name:Java Virtual Machine Specification java_vm_version:20.65-b04-462 java_vm_vendor:Apple Inc. java_vm_name:Java HotSpot(TM) 64-Bit Server VM java_ext_dirs:/Library/Java/Extensions:/System/Library/Java/Extensions:/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/lib/ext file_separator:/ path_separator::