java动态编译

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u011955252/article/details/89819534

我曾经见过一个“规则引擎”,是在应用系统web界面直接编写java代码,然后保存后,规则即生效,我一直很是奇怪,这是如何实现的呢?实际这就好像jsp,被中间件动态的编译成java文件,有被动态的编译成class,同时又动态的加载到classloader中。所以,本质上,纯java得规则引擎,是100%可以实现的。

 

1、动态生成java源代码。这个过程太过简单,直接略过。

2、动态编译。

我看我们自己的规则引擎也有动态编译,就是在生成BOM模型的时候。但是是调用Process执行javac。但这种方式坦白来说不好。因为javac,的命令参数写法和操作系统有关,也就是windows和linux的写法有少量不同。后来发现jdk提供一个动态编译的类。

JavaCompiler javac;

javac = ToolProvider.getSystemJavaCompiler();

int compilationResult = javac.run(null,null,null, "-g","-verbose",javaFile);

这样就可以动态进行编译。前两个参数是输入参数、输出参数,我觉得没有什么用,第三个参数是编译输出信息,默认输出到System.out.err里面。从第四个参数开始,就是javac的参数,可以用数组,也可以直接逗号分割。

3、动态加载。

动态加载实际就是调用ClassLoader。当然需要反射机制调用其中的一个内部分方法,使之变成外部可调用的方法。

File file = new File("/Users/yangming/Work/DevWorkSpace/ssac/gx_hx/test/"); URLClassLoader classloader = (URLClassLoader) ClassLoader.getSystemClassLoader();

Method add = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class});

add.setAccessible(true);

add.invoke(classloader, new Object[]{file.toURI().toURL()});

Class c = classloader.loadClass("Test");

Object o = c.newInstance();

Method m = c.getDeclaredMethod("getString");

m.invoke(o, null);

这样就完成了类的动态加载。

package javacomplie;

public interface MyJavaCompiler {

    Class<?> compile(String code);
}
package javacomplie;

import org.springframework.util.StringUtils;

import javax.tools.*;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MyJavaCompilerImpl implements MyJavaCompiler {

    private static final Pattern PACKAGE_PATTERN=Pattern.compile("package\\s+([_a-zA-z][_a-zA-Z0-9\\.]*);");

    private static final Pattern CLASS_PATTERN=Pattern.compile("class\\s+([_a-zA-z][_a-zA-Z0-9]*)\\s+");

    private final JavaCompiler javaCompiler;

    private URLClassLoader urlClassLoader;

    private String classPath;

    protected String endcoding="UTF-8";

    public MyJavaCompilerImpl() {
        javaCompiler= ToolProvider.getSystemJavaCompiler();
        if(javaCompiler==null){
            throw new IllegalStateException("获取不到java编译器");
        }
        this.urlClassLoader=(URLClassLoader)this.getClass().getClassLoader();

        buildClassPath();
    }

    private void buildClassPath(){
        StringBuffer stringBuffer=new StringBuffer();
        for(URL url:this.urlClassLoader.getURLs()){
            String path=url.getFile();
            stringBuffer.append(path).append(File.pathSeparator);
        }
        this.classPath=stringBuffer.toString();
    }



    @Override
    public Class<?> compile(String code) {
        Matcher matcher=PACKAGE_PATTERN.matcher(code);
        String packageName=null;
        if(matcher.find()){
            packageName=matcher.group(1);
        }
        matcher=CLASS_PATTERN.matcher(code);
        String className;
        if(matcher.find()){
            className=matcher.group(1);
        }else{
            throw new IllegalArgumentException("class Name not find");
        }
        String fullClassName=null;
        if(StringUtils.isEmpty(packageName)){
            fullClassName=packageName+"."+className;
        }else{
            fullClassName=className;
        }
        return doCompile(fullClassName,code);
    }

    public Class<?> doCompile(String fullClassName,String code){
        DiagnosticCollector<JavaFileObject> diagnosticCollector=new DiagnosticCollector<JavaFileObject>();
        StandardJavaFileManager standardJavaFileManager=javaCompiler.getStandardFileManager(diagnosticCollector,null,null);

        ClassFileManager classFileManager=new ClassFileManager(standardJavaFileManager);

        CharJavaFileObject charJavaFileObject=new CharJavaFileObject(fullClassName,code);
        try{
            List<JavaFileObject> jfiles=new ArrayList<>();
            jfiles.add(charJavaFileObject);
            List<String> options=new ArrayList<>();
            options.add("-encoding");
            options.add(endcoding);
            options.add("-classpath");
            options.add(classPath);
            JavaCompiler.CompilationTask task=javaCompiler.getTask(null,classFileManager,diagnosticCollector,options,null,jfiles);
            boolean success=task.call();
            for(Diagnostic<? extends JavaFileObject> diagnostic : diagnosticCollector.getDiagnostics()){
                if(diagnostic.getKind()==Diagnostic.Kind.ERROR){
                    String errorCode=diagnostic.getLineNumber()+"--"+charJavaFileObject.getLineCode(diagnostic.getLineNumber());
                    throw new java.lang.Error(errorCode);
                }
            }
            MyClassLoader myClassLoader=new MyClassLoader(this.urlClassLoader);
            for(ByteJavaFileObject byteJavaFileObject:classFileManager.getInnerClassJavaClassObejct()){
                myClassLoader.defineClass(byteJavaFileObject);
            }
            ByteJavaFileObject bjfo=classFileManager.getByteJavaFileObject();
            Class<?> clazz=myClassLoader.defineClass(bjfo);
            try{
                myClassLoader.close();
            }catch (Exception e ){

            }
            return clazz;

        } finally {
             charJavaFileObject.delete();

            try {
                classFileManager.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    @Override
    public String toString() {
        return "MyJavaCompilerImpl{" +
                "javaCompiler=" + javaCompiler +
                ", urlClassLoader=" + urlClassLoader +
                ", classPath='" + classPath + '\'' +
                ", endcoding='" + endcoding + '\'' +
                '}';
    }
}
package javacomplie;


import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;

public class MyClassLoader extends URLClassLoader {

    public MyClassLoader(ClassLoader classLoader){
        super(new URL[0],classLoader);
    }

    public  String getClassName(ByteJavaFileObject byteJavaFileObject){
        String name=byteJavaFileObject.getName();
        name=name.substring(1,name.length()-6);
        name=name.replace("/",".");
        return name;
    }

    public  Class<?> defineClass(ByteJavaFileObject byteJavaFileObject)  {
        String name=getClassName(byteJavaFileObject);
        byte[] classData=byteJavaFileObject.getBytes();
        return super.defineClass(name,classData,0,classData.length);
    }

}
package javacomplie;

import org.springframework.util.CollectionUtils;

import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ClassFileManager extends ForwardingJavaFileManager<JavaFileManager> {

    private ByteJavaFileObject byteJavaFileObject;

    private List<ByteJavaFileObject> innerObjects=new ArrayList<ByteJavaFileObject>();

    protected ClassFileManager(JavaFileManager javaFileManager){
        super(javaFileManager);
    }

    public ByteJavaFileObject getByteJavaFileObject() {
        return byteJavaFileObject;
    }
    public byte[] getJavaClass(){
        return byteJavaFileObject.getBytes();
    }

    public List<ByteJavaFileObject> getInnerClassJavaClassObejct(){
        if(CollectionUtils.isEmpty(innerObjects)){
            int size=innerObjects.size();
            if(size==1){
                return Collections.emptyList();
            }
            return this.innerObjects.subList(0,size-1);
        }
        return Collections.emptyList();

    }
    @Override
    public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
        this.byteJavaFileObject=new ByteJavaFileObject(className,kind);
        innerObjects.add(byteJavaFileObject);
        return byteJavaFileObject;
    }

    @Override
    public void close() throws IOException {
        if(byteJavaFileObject!=null){
            byteJavaFileObject.delete();
        }
        super.close();
    }
}
package javacomplie;

import javax.tools.SimpleJavaFileObject;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.StringReader;
import java.net.URI;

public class CharJavaFileObject extends SimpleJavaFileObject {
    //等待编译的源码字段
    private String contents;

    //java源代码 => CharJavaFileObject 的时候使用
    public CharJavaFileObject(String className, String contents) {
        super(URI.create("string:///" + className.replace(".", "/") + Kind.SOURCE.extension), Kind.SOURCE);
        this.contents = contents;
    }

    //字符串源码会调用该方法
    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
        return contents;
    }

    /**
     * 获取某个位置的代码
     * @param line
     * @return
     */
    public String getLineCode(long line){
        LineNumberReader reader=new LineNumberReader(new StringReader(contents));
        int num=0;
        String codeLine=null;
        try {
            while ((codeLine = reader.readLine()) != null) {
                num++;
                if(num==line){
                    break;
                }
            }
        }catch (Exception e){

        }finally {
            if(reader!=null){
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return codeLine;
    }
}
package javacomplie;

import javax.tools.SimpleJavaFileObject;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.net.URI;

/**
 * 自定义一个编译之后的字节码对象
 */
public class ByteJavaFileObject extends SimpleJavaFileObject {

    //存放编译后的字节码
    private ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();

    public ByteJavaFileObject(String className, Kind kind){
        super(URI.create("string:///" + className.replace(".", "/") + kind.extension), kind);
    }

    //StringJavaFileManage 编译之后的字节码输出会调用该方法(把字节码输出到outputStream)
    @Override
    public OutputStream openOutputStream() {
        return byteArrayOutputStream;
    }

    public byte[] getBytes(){
        return byteArrayOutputStream.toByteArray();
    }
}
package javacomplie;

public class Test {

    public static void main(String[] args){

        String ok = "public class HelloWorld {\n" +
                "    public static void main(String[] args) {\n" +
                "       System.out.println(\"Hello World!\");\n" +
                "    }\n" +
                "}";
       MyJavaCompilerImpl compiler= new MyJavaCompilerImpl();

        System.out.println(compiler);
        Class<?> clazz=compiler.compile(ok);
        System.out.println(clazz);
        try {
            Object obj=clazz.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

}

 

展开阅读全文

java动态编译加载类问题

12-27

今天看到这个http://www.infoq.com/cn/articles/cf-java-byte-codern想试一试,但是编译通过之后的,想执行结果那,总是不成功。现在将代码贴上,望大神指点!rn[code="java"]rnpackage com.deep.byteOpera;rnrnimport java.io.IOException;rnimport java.lang.reflect.Method;rnimport java.net.URI;rnimport java.net.URISyntaxException;rnimport java.util.Arrays;rnrnimport javax.tools.JavaCompiler;rnimport javax.tools.JavaCompiler.CompilationTask;rnimport javax.tools.JavaFileObject;rnimport javax.tools.SimpleJavaFileObject;rnimport javax.tools.StandardJavaFileManager;rnimport javax.tools.ToolProvider;rnrnpublic class CompilerTest rn static class StringSourceJavaObject extends SimpleJavaFileObject rnrn private String content = null;rn public StringSourceJavaObject(String name, String content) throws URISyntaxException rn super(URI.create("string:///" + name.replace('.','/') + Kind.SOURCE.extension), Kind.SOURCE);rn this.content = content;rn rnrn public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException rn return content;rn rn rnrn public static void main(String[] args) throws Exception rn rn String className = "MainTest";rn String methodName = "main";rn rn String source = "public class "+className+" public static String "+methodName+" () System.out.println(\"Hello World!\"); return \"haha\"; ";rn JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();rn rn //动态编译rn StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);rn StringSourceJavaObject sourceObject = new CompilerTest.StringSourceJavaObject(className, source);rn Iterable fileObjects = Arrays.asList(sourceObject);rn CompilationTask task = compiler.getTask(null, fileManager, null, null, null, fileObjects);rn boolean result = task.call();rn rn System.out.println(result);rn rn if (result) rn //编译成功,进行加载执行rn// ClassLoader loader = fileManager.getClass().getClassLoader();rn// ClassLoader loader = fileObjects.getClass().getClassLoader();rn// ClassLoader loader = task.getClass().getClassLoader();rn// ClassLoader loader = compiler.getClass().getClassLoader();rn ClassLoader loader = CompilerTest.class.getClassLoader(); rn// ClassLoader loader = ToolProvider.getSystemToolClassLoader();rn try rn Class clazz = loader.loadClass(className);rn Method method = clazz.getMethod(methodName, new Class[] );rn Object value = method.invoke(null, new Object[] );rn System.out.println(value);rn catch (Exception e) rn e.printStackTrace();rn rn rn rn rnrn[/code] 问答

java动态编译找不到类文件

01-01

代码如下:rnrn[code=Java]package wmw.run.view;rnrnimport java.io.*;rnimport java.lang.reflect.InvocationTargetException;rnimport java.lang.reflect.Method;rnrn/** rn * @author Every E-mail/MSN:mwgjkf@hotmail.comrn * QQ:30130942rn * @version 创建时间:Dec 30, 2008 3:34:57 PM rn * 类说明:rn *rn */rnpublic class RuntimeCodern /**编译器*/rn private static com.sun.tools.javac.Main javac=new com.sun.tools.javac.Main();rn rn /**等待用户输入JavaCode,然后编译、执行*/rn public static void main(String[] args) throws Exceptionrn String code = "for(int i=0;i<10;i++)System.out.println(i);";rn// DataInputStream bd = new DataInputStream(System.in);rn// byte[] brray= new byte[200];rn// int i = bd.read(brray);rn// code = new String(brray,0,i);rn run(compile(code));rn System.out.println(code);rn rn rn /**编译JavaCode,返回临时文件对象*/rn private synchronized static File compile(String code)throws IOException,Exceptionrn rn File file;rn rn //在用户当前文件目录创建一个临时代码文件rn file=File.createTempFile("JavaCode",".java",new File(System.getProperty("user.dir")));rn rn //当虚拟机退出时,删除此临时java源文件rn file.deleteOnExit();rn rn //获得文件名和类名字rn String filename=file.getName();rn String classname=getClassName(filename);rn System.out.println(classname);rn //将代码输出到文件rn PrintWriter out=new PrintWriter(new FileOutputStream(file));rn out.write("class "+classname+""+"public static void main(String[] args)"+"");rn out.write(code);rn out.write("");rn rn //关闭文件流rn out.flush();rn out.close();rn rn //编译代码文件rn String[] args=new String[]"-d",System.getProperty("user.dir"),filename;rn rn //返回编译的状态代码rn int status=javac.compile(args);rn rn //处理编译状态rn System.out.println(status);rn return file;rn rn rn /**执行刚刚编译的类文件*/rn private static synchronized void run(File file)rn //当虚拟机退出时,删除此临时编译的类文件rn //获得文件名和类名字rn String filename = file.getName(); rn String classname = getClassName(filename);rn new File(file.getParent(),classname+".class").deleteOnExit();rn try rn // 访问这个类 rn Class cls = Class.forName(classname); rn //调用main方法 rn Method main = cls.getMethod("main", new Class[] String[].class ); rn main.invoke(null, new Object[] new String[0] ); rn catch (SecurityException se) rn debug("access to the information is denied:" + se.toString()); rn catch (NoSuchMethodException nme) rn debug("a matching method is not found or if then name is or : " + nme.toString()); rn catch (InvocationTargetException ite) rn debug("Exception in main: " + ite.getTargetException()); rn catch (Exception e) rn debug(e.toString()); rn rn rn rn /**打印调试信息*/rn private static void debug(String msg)rn System.err.println(msg);rn rn rn /**根据一个java源文件名获得类名*/rn private static String getClassName(String filename)rn return filename.substring(0,filename.length()-5);rn rnrn[/code]rn在myeclipse中运行输出如下内容rnJavaCode2376rn0java.lang.ClassNotFoundException: JavaCode2376rnfor(int i=0;i<10;i++)System.out.println(i);rn找不到类文件rn这是怎么回事?rn谁能教我怎么解决?rn 论坛

没有更多推荐了,返回首页