java 模拟jvm 将.java文件生成可执行.class文件

写这篇文章,一来、记录自己的项目。二、也为了方便,碰到相同问题的朋友,让他们少走一些弯路。
本文借鉴了许多文章,由于时间太久,部分引用没找到链接。请谅解
项目需求:实现通过程序更新服务器中指定 的 可以独自执行.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,和环境:
参考:https://blog.csdn.net/weixin_34000916/article/details/86331809

环境的问题2,
事件:tomcat中运行通过反射执行.class文件时,出现ClassNotFoundException
原因:模拟执行的时候需要环境,
参考:https://www.cnblogs.com/zfyouxi/p/4803072.html ,https://www.cnblogs.com/zfyouxi/p/4803072.html

2.1遇到的问题:
1,zip解压,https://www.cnblogs.com/tonghun/p/7189227.html
问题:目录下解压文件时,当压缩文件中为目录时,在解压的环境中没有这样的目录,不能识别出来,会将它按照文件来生成。

参考:https://www.cnblogs.com/tonghun/p/7189227.html  ,解决的方法,评论中写了

注意:多个人访问的时候,若是为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
				https://www.cnblogs.com/sebastian-tyd/p/7895182.html

结论:这样写在java源代码中可以,发布到tomcat中页可以执行
	Properties prop = new Properties();
	InputStream inputStream = UpdateSpider.class.getResourceAsStream("/editEnvironmentPath.properties"); 
	prop.load(new InputStreamReader(inputStream, DEFAULT_ENCODING)); //加载格式化后的流

5,将控制台的错误弄到日志中:https://blog.csdn.net/u012060033/article/details/81410992
	错误日志存储起来: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);
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值