写这篇文章,一来、记录自己的项目。二、也为了方便,碰到相同问题的朋友,让他们少走一些弯路。
本文借鉴了许多文章,由于时间太久,部分引用没找到链接。请谅解
项目需求:实现通过程序更新服务器中指定 的 可以独自执行.java 代码。
需求分析:写个功能 :实现模拟jvm 将.java文件 生成.class文件,再将 生成.class 再替换原.class文件,
从而实现服务器自动更新代码。
1.0版本
思路:通过类似dos窗口的cmd命令来进行编译,让windos 系统帮我完成编译。
技术:批处理,来自动运行 cmd 的命令
遇到的问题:
1,如何java中调用dos窗口,将文件编译成.java的文件?
解决:process 利用批处理执行
Process p=Runtime.getRuntime().exec("cmd /c d:\\test\\tt.bat");
p.waitFor();
2,编译文件时,dos窗口,程序类找不到?
解决:程序运行环境的配置: 一、 为jdk的环境,二、为jar包的环境,三、为项目的环境
3,执行时,找不到加载的类。
解决:.class文件的位置没有放对,或者上述环境没有配对。
批处理:
模拟dos窗口,进行编译的批处理文件
cd D:\javac --->切换目录代码存放的位置
set classpath=%classpath%; .;bin\lib\lib.jar; .;bin\classes --->classpath=%classpath%; 为jdk的环境,;bin\lib\lib.jar; 为jar包的环 境,.;bin\classes 为项目的环境
javac ./xx/xxx.java &pause; -->编译代码 ,&pause的作用:让dos窗口不会一闪而过,会继续存在
模拟dos窗口,运行编译好.class文件 的批处理文件
cd D:\javac --->切换目录代码存放的位置
set classpath=%classpath%; .;bin\lib\lib.jar;bin\lib\jsoup-1.10.2.jar;bin\lib\json-lib-1.1-jdk15.jar; .;bin\classes --->环境的配置
java com.xxx &pause; -->执行代码
批处理运行时,获取dos 窗口的错误 。本文只是在控制台打印出来。
Process p=Runtime.getRuntime().exec("cmd /c d:\\test\\ttt.bat"); //ttt.bat 是 存放 运行.class文件的批处理文件
Thread.sleep(5000);
InputStream in=p.getInputStream();
//有错误时
InputStream err=p.getErrorStream();
byte[]barry=new byte[in.available()];
byte[]errarry=new byte[err.available()];
in.read(barry);
in.close();
err.read(errarry);
err.close();
String str=new String(barry);
String errstr=new String(errarry);
System.out.println("info");
System.out.println(str);
System.out.println("errorinfo");
System.out.println(errstr);
总结:上述方法基本可以实现需求。但是,这种思路,仅仅适用于windows环境。不通用。所以,不通过。
2.0版本 (2.X的版本都是基于这个思路)
思路:java 中有封装的编译的方法,根据java 的API 进行开发
技术: java中的类 JavaCompiler
遇到的问题
事件:
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
int compilationResult = compiler.run(null, null, null, ‘/path/to/Test.java’);
代码执行,报空指针
原因:少包,将jdk下的tools包copy到jre下
环境的问题1,
事件:本地可以执行,在tomcat执行时,出错
原因:模拟编译的时候需要导入jar,和环境:
参考:
环境的问题2,
事件:tomcat中运行通过反射执行.class文件时,出现ClassNotFoundException
原因:模拟执行的时候需要环境,
参考: ,
2.1遇到的问题:
1,zip解压,
问题:目录下解压文件时,当压缩文件中为目录时,在解压的环境中没有这样的目录,不能识别出来,会将它按照文件来生成。
参考: ,解决的方法,评论中写了
注意:多个人访问的时候,若是为zip文件的时候,应该以当前时间毫秒值来创建文件夹,不会出错。
2,日志记录:不能用全局变量去记录。多个人访问出现问题
3,复制文件出现乱码的问题。
a,全局乱码:设置读写时候的编码
* FileReader是使用默认码表读取文件, 如果需要使用指定码表读取, 那么可以使用InputStreamReader(字节流,编码表)
* FileWriter是使用默认码表写出文件, 如果需要使用指定码表写出, 那么可以使用OutputStreamWriter(字节流,编码表)
*
BufferedReader br = //高效的用指定的编码表读
new BufferedReader(new InputStreamReader(new FileInputStream("UTF-8.txt"), "UTF-8"));
BufferedWriter bw = //高效的用指定的编码表写
new BufferedWriter(new OutputStreamWriter(new FileOutputStream("GBK.txt"), "GBK"));
int ch;
while((ch = br.read()) != -1) {
bw.write(ch);
}
br.close();
bw.close();
b,个别文字的乱码(一般为最后一个字的乱码)
文件复制的时候不能用字节流:fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
reader = new BufferedReader (new InputStreamReader(bis));
//之所以用BufferedReader,而不是直接用BufferedInputStream读取,是因为BufferedInputStream是InputStream的间接子类,
//InputStream的read方法读取的是一个byte,而一个中文占两个byte,所以可能会出现读到半个汉字的情况,就是乱码.
//BufferedReader继承自Reader,该类的read方法读取的是char,所以无论如何不会出现读个半个汉字的.
StringBuffer result = new StringBuffer();
while (reader.ready()) {
result.append((char)reader.read());
}
4,propeties文件,java执行与tomcat执行不同 https://shitou521.iteye.com/blog/867695
结论:这样写在java源代码中可以,发布到tomcat中页可以执行
Properties prop = new Properties();
InputStream inputStream = UpdateSpider.class.getResourceAsStream("/editEnvironmentPath.properties");
prop.load(new InputStreamReader(inputStream, DEFAULT_ENCODING)); //加载格式化后的流
5,将控制台的错误弄到日志中:
错误日志存储起来:ExceptionUtil
还有公司的处理异常的工具类,不好展示。
2.2 项目上传后,代码的问题:
1,模拟编译.class文件,
URL[] urls = new URL[] {new URL(“file:” +targetDir)};
URLClassLoader loader = new URLClassLoader(urls, Thread.currentThread().getContextClassLoader());
Class<?> clazz = null;
原因:本地有同样包名类名的文件,URLClassLoader 会默认调用就近本地的环境,
解决方案:改下包名
2,jar查看(GUI反编译程序)
使用压缩工具可以查看jar中的结构,使用gui查看源代码。链接:https://pan.baidu.com/s/1MMr3v6m1pySBLo4eTJZ7ww 提取码:honm
3,如何判断一个字符串某个字符出现的次数 :https://zhidao.baidu.com/question/1694551757131440788.html 包:org.apache.commons.lang.StringUtils 方法:StringUtils.countMatches(“aaa”,“a”);
4:java.lang.NoSuchMethodError ttps://www.oschina.net/question/241255_161211
事件:NoSuchMethodError 没有找到方法
原因:此程序中有两个环境,运行环境,模拟编译的环境,当运行环境中有与模拟环境中一样的工具类,但是代码不同。程序优先调用的时当前环境,就近原则。若是,调用的是当前环境中没有的方法,就会出现NoSuchMethodError。
解决:将两个环境中的工具类,代码一致。
5,java.lang.ClassFormatError: Unknown constant tag 80 in class file
原因:copy .class文件时,用的io流,导致文件出错
总结:一般为运行编译好的.class文件出错。有的是jdk版本不一致,导致的。我这里的错误为,复制.class文件使用io流,导致.class文件改变。
方案:一个非常好用的java copy文件的方法
beginFilename:复制的文件 endFilename:复制的路径 两个都是文件的权路径
File compileClassFile=new File(beginFilename);
if(!compileClassFile.exists()){
list.add("未能找到ava文件");
return list;
}
File outClassFile=new File(endFilename);
/**option:
ATOMIC_MOVE 原子性的复制
COPY_ATTRIBUTES 将源文件的文件属性信息复制到目标文件中
REPLACE_EXISTING 替换已存在的文件 */
Files.copy(compileClassFile.toPath(),
outClassFile .toPath(),
StandardCopyOption.REPLACE_EXISTING);