家用电脑系统-新建论坛
2023年4月6日发(作者:epson打印机驱动)
java在线编译器的实现_java动态编译(java在线执⾏代码后端
实现原理)(⼆)...
在上⼀篇java动态编译(java在线执⾏代码后端实现原理(⼀))⽂章中实现了字符串编译成字节码,然后通过反射来运⾏代码的demo。这⼀
篇⽂章提供⼀个如何防⽌死循环的代码占⽤cpu的问题。
思路:由于CustomStringJavaCompiler中重定向了的输出位置,肯定不能有多线程并发的情况,否则会照成输
出内容错乱,所以我⽤了edThreadPool(1),通过Future模式来获取结果,我⾃定义了⼀个CustomCallable来处理
核⼼逻辑,在call⽅法中重新new了⼀个Thread来编译并执⾏代码,然后通过join等待N秒之后强制stop掉正在运⾏的线程。这样就能及时
的kill掉动态运⾏的代码。
CustomStringJavaCompiler编译核⼼类
;
stic;
sticCollector;
ject;
dingJavaFileManager;
mpiler;
leManager;
leObject;
JavaFileObject;
rdJavaFileManager;
ovider;
rayOutputStream;
ption;
Stream;
tream;
ortedEncodingException;
tionTargetException;
;
;
;
;
;
rentHashMap;
r;
n;
/**
*Createbyandyon2018-12-0621:25
*/
publicclassCustomStringJavaCompiler{
//类全名
privateStringfullClassName;
privateStringsourceCode;
//存放编译之后的字节码(key:类全名,value:编译之后输出的字节码)
privateMapjavaFileObjectMap=newConcurrentHashMap<>();
//获取java的编译器
privateJavaCompilercompiler=temJavaCompiler();
//存放编译过程中输出的信息
privateDiagnosticCollectordiagnosticsCollector=newDiagnosticCollector<>();
//执⾏结果(控制台输出的内容)
privateStringrunResult;
//编译耗时(单位ms)
privatelongcompilerTakeTime;
//运⾏耗时(单位ms)
privatelongrunTakeTime;
publicCustomStringJavaCompiler(StringsourceCode){
Code=sourceCode;
assName=getFullClassName(sourceCode);
}
/**
*编译字符串源代码,编译失败在diagnosticsCollector中获取提⽰信息
*
*@returntrue:编译成功false:编译失败
*/
publicbooleancompiler(){
longstartTime=tTimeMillis();
//标准的内容管理器,更换成⾃⼰的实现,覆盖部分⽅法
StandardJavaFileManagerstandardFileManager=ndardFileManager(diagnosticsCollector,null,null);
JavaFileManagerjavaFileManager=newStringJavaFileManage(standardFileManager);
//构造源代码对象
JavaFileObjectjavaFileObject=newStringJavaFileObject(fullClassName,sourceCode);
//获取⼀个编译任务
ationTasktask=k(null,javaFileManager,diagnosticsCollector,null,null,
(javaFileObject));
//设置编译耗时
compilerTakeTime=tTimeMillis()-startTime;
();
}
/**
*执⾏main⽅法,重定向
*/
publicvoidrunMainMethod()throwsClassNotFoundException,NoSuchMethodException,InvocationTargetException,
IllegalAccessException,UnsupportedEncodingException{
PrintStreamout=;
try{
longstartTime=tTimeMillis();
ByteArrayOutputStreamoutputStream=newByteArrayOutputStream();
PrintStreamprintStream=newPrintStream(outputStream);
//PrintStreamPrintStream=newPrintStream("/Users/andy/Desktop/");//输出到⽂件
(printStream);//测试kill线程暂时屏蔽
StringClassLoaderscl=newStringClassLoader();
Class>aClass=ass(fullClassName);
Methodmain=hod("main",String[].class);
Object[]pars=newObject[]{1};
pars[0]=newString[]{};
(null,pars);//调⽤main⽅法
//设置运⾏耗时
runTakeTime=tTimeMillis()-startTime;
//设置打印输出的内容
runResult=newString(Array(),"utf-8");
}finally{
//还原默认打印的对象
(out);
}
}
/**
*@return编译信息(错误警告)
*/
publicStringgetCompilerMessage(){
StringBuildersb=newStringBuilder();
List>diagnostics=gnostics();
for(Diagnosticdiagnostic:diagnostics){
(ng()).append("rn");
}
ng();
}
/**
*@return控制台打印的信息
*/
publicStringgetRunResult(){
returnrunResult;
}
publiclonggetCompilerTakeTime(){
returncompilerTakeTime;
}
publiclonggetRunTakeTime(){
returnrunTakeTime;
}
/**
*获取类的全名称
*
*@paramsourceCode源码
*@return类的全名称
*/
publicstaticStringgetFullClassName(StringsourceCode){
StringclassName="";
Patternpattern=e("packages+S+s*;");
Matchermatcher=r(sourceCode);
if(()){
className=().replaceFirst("package","").replace(";","").trim()+".";
}
pattern=e("classs+S+s+{");
matcher=r(sourceCode);
if(()){
className+=().replaceFirst("class","").replace("{","").trim();
}
returnclassName;
}
/**
*⾃定义⼀个字符串的源码对象
*/
privateclassStringJavaFileObjectextendsSimpleJavaFileObject{
//等待编译的源码字段
privateStringcontents;
//java源代码=>StringJavaFileObject对象的时候使⽤
publicStringJavaFileObject(StringclassName,Stringcontents){
super(("string:///"+eAll(".","/")+ion),);
ts=contents;
}
//字符串源码会调⽤该⽅法
@Override
publicCharSequencegetCharContent(booleanignoreEncodingErrors)throwsIOException{
returncontents;
}
}
/**
*⾃定义⼀个编译之后的字节码对象
*/
privateclassByteJavaFileObjectextendsSimpleJavaFileObject{
//存放编译后的字节码
privateByteArrayOutputStreamoutPutStream;
publicByteJavaFileObject(StringclassName,Kindkind){
super(("string:///"+eAll(".","/")+ion),kind);
}
//StringJavaFileManage编译之后的字节码输出会调⽤该⽅法(把字节码输出到outputStream)
@Override
publicOutputStreamopenOutputStream(){
outPutStream=newByteArrayOutputStream();
returnoutPutStream;
}
//在类加载器加载的时候需要⽤到
publicbyte[]getCompiledBytes(){
Array();
}
}
/**
*⾃定义⼀个JavaFileManage来控制编译之后字节码的输出位置
*/
privateclassStringJavaFileManageextendsForwardingJavaFileManager{
StringJavaFileManage(JavaFileManagerfileManager){
super(fileManager);
}
//获取输出的⽂件对象,它表⽰给定位置处指定类型的指定类。
@Override
publicJavaFileObjectgetJavaFileForOutput(Locationlocation,StringclassName,nd,FileObjectsibling)
throwsIOException{
ByteJavaFileObjectjavaFileObject=newByteJavaFileObject(className,kind);
(className,javaFileObject);
returnjavaFileObject;
}
}
/**
*⾃定义类加载器,⽤来加载动态的字节码
*/
privateclassStringClassLoaderextendsClassLoader{
@Override
protectedClass>findClass(Stringname)throwsClassNotFoundException{
ByteJavaFileObjectfileObject=(name);
if(fileObject!=null){
byte[]bytes=piledBytes();
returndefineClass(name,bytes,0,);
}
try{
temClassLoader().loadClass(name);
}catch(Exceptione){
ass(name);
}
}
}
}
CustomCallable调⽤编译并运⾏,设置超时时间
;
tionTargetException;
le;
/**
*Createbyandyon2018-12-0713:10
*/
publicclassCustomCallableimplementsCallable{
privateStringsourceCode;
publicCustomCallable(StringsourceCode){
Code=sourceCode;
}
//⽅案1
//@Override
//publicRunInfocall()throwsException{
//n("开始执⾏call"+());
//RunInforunInfo=newRunInfo();
//CustomStringJavaCompilercompiler=newCustomStringJavaCompiler(sourceCode);
//if(er()){
//pilerSuccess(true);
//try{
//nMethod();
//Success(true);
//TakeTime(TakeTime());
//Message(Result());//获取运⾏的时候输出内容
//}catch(Exceptione){
//tackTrace();
//Success(false);
//Message(sage());
//}
//}else{
////编译失败
//pilerSuccess(false);
//}
//pilerTakeTime(pilerTakeTime());
//pilerMessage(pilerMessage());
//n("callover"+());
//returnrunInfo;
//}
//⽅案2
@Override
publicRunInfocall()throwsException{
RunInforunInfo=newRunInfo();
Threadt1=newThread(()->realCall(runInfo));
();
try{
(3000);//等待3秒
}catch(InterruptedExceptione){
tackTrace();
}
//不管有没有正常执⾏完成,强制停⽌t1
();
returnrunInfo;
}
privatevoidrealCall(RunInforunInfo){
CustomStringJavaCompilercompiler=newCustomStringJavaCompiler(sourceCode);
if(er()){
pilerSuccess(true);
try{
nMethod();
Success(true);
TakeTime(TakeTime());
Message(Result());//获取运⾏的时候输出内容
}catch(InvocationTargetExceptione){
//反射调⽤异常了,是因为超时的线程被强制stop了
if("Death".equalsIgnoreCase(se().toString())){
return;
}
}catch(Exceptione){
tackTrace();
Success(false);
Message(sage());
}
}else{
//编译失败
pilerSuccess(false);
}
pilerTakeTime(pilerTakeTime());
pilerMessage(pilerMessage());
eOut(false);//⾛到这⼀步代表没有超时
}
}
RunInfo动态编译、运⾏信息的bean
publicclassRunInfo{
//true:代表超时
privateBooleantimeOut;
privateLongcompilerTakeTime;
privateStringcompilerMessage;
privateBooleancompilerSuccess;
privateLongrunTakeTime;
privateStringrunMessage;
privateBooleanrunSuccess;
//省略get和set⽅法
}
CompilerUtil把⼀整套流程封装了⼀个⼯具类
;
orService;
ors;
;
/**
*Createbyandyon2018-12-0716:32
*/
publicclassCompilerUtil{
//这⾥⽤⼀个线程是因为防⽌输出内容错乱
privatestaticExecutorServicepool=edThreadPool(1);
publicstaticRunInfogetRunInfo(StringjavaSourceCode){
RunInforunInfo;
CustomCallablecompilerAndRun=newCustomCallable(javaSourceCode);
Futurefuture=(compilerAndRun);
//⽅案1
try{
runInfo=();
}catch(Exceptione){
tackTrace();
//代码编译或者运⾏超时
runInfo=newRunInfo();
eOut(true);
}
//⽅案2:不可⾏的原因:超时会有问题,由于线程池只有1个线程,同时提交10个任务,当前⾯⼏个任务执⾏时间很长,后⾯
调⽤get就会⽴马失败,也就是说get的超时时间是从调⽤get开始算的,并不是线程真正执⾏时间开始计算的
//try{
//runInfo=(5,S);
//returnrunInfo;
//}catch(InterruptedExceptione){
//n("future在睡着时被打断");
//tackTrace();
//}catch(ExecutionExceptione){
//n("future在尝试取得任务结果时出错");
//tackTrace();
//}catch(TimeoutExceptione){
//n("future时间超时");
//tackTrace();
//(true);
//}
//runInfo=newRunInfo();
//eOut(true);
returnrunInfo;
}
}
测试类:
;
/**
*Createbyandyon2018-12-1010:43
*/
publicclassTest3{
publicstaticvoidmain(String[]args)throwsInterruptedException{
Stringloop="publicclassHelloWorld{n"+
"publicstaticvoidmain(String[]args){n"+
"while(true){n"+
//"n("HelloWorld!");n"+
"}n"+
"n"+
"}n"+
"}";
Stringsleep_loop="publicclassHelloWorld{n"+
"publicstaticvoidmain(String[]args){n"+
"try{n"+
"(6000);n"+
"}catch(InterruptedExceptione){n"+
"tackTrace();n"+
"}n"+
"n("HelloWorld!");n"+
"while(true){n"+
//"n("HelloWorld!");n"+
"}n"+
"}n"+
"}";
Stringok="publicclassHelloWorld{n"+
"publicstaticvoidmain(String[]args){n"+
"n("HelloWorld!");n"+
"}n"+
"}";
TestRunt=newTestRun(ok,"thread:ok");
();
TestRunt1=newTestRun(loop,"thread:loop:");
();
//
TestRunt2=newTestRun(sleep_loop,"thread:sleep_loop:");
();
}
}
classTestRunextendsThread{
Stringcode;
TestRun(Stringcode,Stringname){
=code;
e(name);
}
@Override
publicvoidrun(){
n(Info(code));
}
}
更多推荐
java编译器
发布评论