无论开发人员运维人员,都需要具备相当的shell脚本编写技能,但是由于windows平台不是部署的首选平台,我们很多人都忽视bat脚本的编写。正因为如此,工作中偶尔会踩个大坑。

最近工作原因,需要为程序部署包配置windows下bat启动脚本,本身前同事留下了linux版本的启动命令,以及windows的启动脚本,然而,在win环境下部署,发现其脚本不可用,主要是-classpath参数有问题。先上代码:

title spider-node(Java)
@echo off
cd %~dp0..
echo spider-node(Java)
setLocal EnableDelayedExpansion
set CLASSPATH="conf
for /f "tokens=* delims=" %%a in ('dir "*.jar" /b') do (
   set CLASSPATH=!CLASSPATH!;%%a
)
set CLASSPATH=!CLASSPATH!"

java -server -Dcom.sun.management.jmxremote.port=20002 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Xms1024m -Xmx8192m -Djava.ext.dirs=lib -cp %CLASSPATH% com.spider.SpiderNodeStarter

这段代码执行时,发现CLASSPATH变量在遍历lib目录的时候,并没有将所有jar文件的文件名都囊括,我估计是由于dos环境对于字符串的长度是有限制的,但是查了很久,没有查到具体限制是多少,也没查出如何解决这个限制的问题。

在解决这个问题的过程中,查阅的很多资料,博客,让我对于classpath参数的设置有了一些认识,总结下来有如下几种方式:

1. 我上述脚本中的方式,遍历lib目录,拼接出依赖的jar作为classpath

2. -Djava.ext.dirs=lib 指定依赖库所在目录

3.  -cp %CLASSPATH%  用cp参数指定依赖库

4.  javac -cp .;../lib/* yourJavaCodeDir/YourJavaFile.java cp的另一种用法,指定jar所在目录

5. java -classpath %~dp0/bin;%~dp0/lib/* ClassName %1  classpath参数可直接指定依赖库的目录

对于上述5种方法,我一一进行了尝试,其中,方,3跟方法1一样,难以避免CLASSPATH的长度限制;方法2,4在使用的时候,类路径发生了变化,依赖包中的类使用相对路径无法访问conf目录下的资源文件,导致程序执行过程中报出各种资源加载失败的异常;第五种方法解决了我遇到的问题,既不受长度限制,也没有路径访问失败的问题。

其实,如果你的依赖一直没有变化,还有一种最笨的办法,就是直接拼装出CLASSPATH写死在java启动命令里面:

batch运行java文件 bat运行java class_batch运行java文件

只是这种方法可以应急,不适合依赖经常变化的项目,也非常不优雅。

时间问题,对于方法2/4导致资源加载有问题的原因,没有深究,也期望通过本文抛砖引玉,有大神能够指点一二。

最终脚本:

title spider-node(Java)

@echo off
cd %~dp0..
echo spider-node(Java)
setLocal EnableDelayedExpansion

java -Dfile.encoding=utf-8 -server -XX:-UseGCOverheadLimit -Dcom.sun.management.jmxremote.port=20002 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -classpath .;%~dp0/../lib/* -Xms3g -Xmx6g com.spider.SpiderNodeStarter ../conf