动态编译Java代码并反射运行

  1 public class CustomJavaCompiler {
  2     //源码
  3     private String sourceCode;
  4     //类全名
  5     private String fullClassName;
  6     //获取java的编译器
  7     private JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
  8     //存放编译之后的字节码(key:类全名,value:编译之后输出的字节码)
  9     private Map<String, ByteJavaFileObject> javaFileObjectMap = new ConcurrentHashMap<>();
 10     //存放编译过程中输出的信息
 11     private DiagnosticCollector<JavaFileObject> diagnosticsCollector = new DiagnosticCollector<>();
 12     //编译耗时(单位ms)
 13     private long compilerTime;
 14 
 15     public CustomJavaCompiler(String sourceCode) {
 16         this.sourceCode = sourceCode;
 17         this.fullClassName = getFullClassName(sourceCode);
 18     }
 19 
 20     /**
 21      * 编译字符串源代码,编译失败在 diagnosticsCollector 中获取提示信息
 22      *
 23      * @return true:编译成功 false:编译失败
 24      */
 25     public boolean compiler() {
 26         if(compiler == null) 
 27             return false;
 28         
 29         long startTime = System.currentTimeMillis();
 30         //标准的内容管理器,更换成自己的实现,覆盖部分方法
 31         StandardJavaFileManager standardFileManager = compiler.getStandardFileManager(diagnosticsCollector, null, null);
 32         JavaFileManager javaFileManager = new StringJavaFileManage(standardFileManager);
 33         //构造源代码对象
 34         JavaFileObject javaFileObject = new StringJavaFileObject(fullClassName, sourceCode);
 35         //获取一个编译任务
 36         JavaCompiler.CompilationTask task = compiler.getTask(null, javaFileManager, diagnosticsCollector, null, null, Arrays.asList(javaFileObject));
 37         //设置编译耗时
 38         compilerTime = System.currentTimeMillis() - startTime;
 39         return task.call();
 40     }
 41     
 42     /**
 43      * 获取编译后的Class
 44      * @return
 45      */
 46     public Class<?> getCompilerClass() {
 47         StringClassLoader scl = new StringClassLoader();
 48         Class<?> clz = null;
 49         try {
 50             clz = scl.findClass(fullClassName);
 51         } catch (Exception e) {
 52             e.printStackTrace();
 53         }
 54         return clz;
 55     }
 56     
 57     /**
 58      * 获取编译时产生的信息
 59      * @return 编译信息(错误 警告)
 60      */
 61     public String getCompilerMessage() {
 62         if(compiler == null) 
 63             return "JRE环境未配置(请复制JDK路径下lib目录内的tools.jar到JRE路径下lib目录里)";
 64         
 65         StringBuilder sb = new StringBuilder();
 66         List<Diagnostic<? extends JavaFileObject>> diagnostics = diagnosticsCollector.getDiagnostics();
 67         for (Diagnostic diagnostic : diagnostics) {
 68             sb.append(diagnostic.toString()).append("\r\n");
 69         }
 70         return sb.toString();
 71     }
 72 
 73     public long getCompilerTime() {
 74         return compilerTime;
 75     }
 76 
 77     /**
 78      * 获取类的全名称
 79      * @param sourceCode 源码
 80      * @return 类的全名称
 81      */
 82     public static String getFullClassName(String sourceCode) {
 83         String className = "";
 84         Pattern pattern = Pattern.compile("package\\s+\\S+\\s*;");
 85         Matcher matcher = pattern.matcher(sourceCode);
 86         if (matcher.find()) {
 87             className = matcher.group().replaceFirst("package", "").replace(";", "").trim() + ".";
 88         }
 89 
 90         pattern = Pattern.compile("class\\s+\\S+\\s+\\{");
 91         matcher = pattern.matcher(sourceCode);
 92         if (matcher.find()) {
 93             className += matcher.group().replaceFirst("class", "").replace("{", "").trim();
 94         }
 95         return className;
 96     }
 97 
 98     /**
 99      * 自定义一个字符串的源码对象
100      */
101     private class StringJavaFileObject extends SimpleJavaFileObject {
102         //等待编译的源码字段
103         private String contents;
104 
105         //java源代码 => StringJavaFileObject对象 的时候使用
106         public StringJavaFileObject(String className, String contents) {
107             super(URI.create("string:///" + className.replaceAll("\\.", "/") + Kind.SOURCE.extension), Kind.SOURCE);
108             this.contents = contents;
109         }
110 
111         //字符串源码会调用该方法
112         @Override
113         public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
114             return contents;
115         }
116 
117     }
118 
119     /**
120      * 自定义一个编译之后的字节码对象
121      */
122     private class ByteJavaFileObject extends SimpleJavaFileObject {
123         //存放编译后的字节码
124         private ByteArrayOutputStream outPutStream;
125 
126         public ByteJavaFileObject(String className, Kind kind) {
127             super(URI.create("string:///" + className.replaceAll("\\.", "/") + Kind.SOURCE.extension), kind);
128         }
129 
130         //StringJavaFileManage 编译之后的字节码输出会调用该方法(把字节码输出到outputStream)
131         @Override
132         public OutputStream openOutputStream() {
133             outPutStream = new ByteArrayOutputStream();
134             return outPutStream;
135         }
136 
137         //在类加载器加载的时候需要用到
138         public byte[] getCompiledBytes() {
139             return outPutStream.toByteArray();
140         }
141     }
142 
143     /**
144      * 自定义一个JavaFileManage来控制编译之后字节码的输出位置
145      */
146     private class StringJavaFileManage extends ForwardingJavaFileManager {
147         StringJavaFileManage(JavaFileManager fileManager) {
148             super(fileManager);
149         }
150 
151         //获取输出的文件对象,它表示给定位置处指定类型的指定类
152         @Override
153         public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
154             ByteJavaFileObject javaFileObject = new ByteJavaFileObject(className, kind);
155             javaFileObjectMap.put(className, javaFileObject);
156             return javaFileObject;
157         }
158     }
159 
160     /**
161      * 自定义类加载器, 用来加载动态的字节码
162      */
163     private class StringClassLoader extends ClassLoader {
164         @Override
165         protected Class<?> findClass(String name) throws ClassNotFoundException {
166             ByteJavaFileObject fileObject = javaFileObjectMap.get(name);
167             if (fileObject != null) {
168                 byte[] bytes = fileObject.getCompiledBytes();
169                 return defineClass(name, bytes, 0, bytes.length);
170             }
171             try {
172                 return ClassLoader.getSystemClassLoader().loadClass(name);
173             } catch (Exception e) {
174                 return super.findClass(name);
175             }
176         }
177     }
178     
179     public static <T> T invokeMethod(Object object, String methodName, Class<?>[] classes, Object... args) 
180             throws Exception {
181         Method method = object.getClass().getMethod(methodName, classes);
182         return (T) method.invoke(object, args);
183     }
184     
185     public static void main(String[] args) {
186         String code = "public class Test {\n" +
187                 "    public static int runEveryTick() {\n" +
188                 "\t\tfor(int i=0; i < 2; i++){\n" +
189                 "\t\t\t       System.out.println(10);\n" +
190                 "\t\t}\n" +
191                 "\t\treturn 1;" + 
192                 "    }\n" +
193                 "}";
194         CustomJavaCompiler compiler = new CustomJavaCompiler(code);
195         boolean res = compiler.compiler();
196         if (res) {
197             System.out.println("compilerSuccess:" + compiler.getCompilerMessage());
198             System.out.println("compilerTime:" + compiler.getCompilerTime());
199             try {
200                 Class<?> clz = compiler.getCompilerClass();
201                 int ret = invokeMethod(clz.newInstance(), "runEveryTick", null);
202                 System.out.println("ret:" + ret);
203             } catch (Exception e) {
204                 e.printStackTrace();
205             }
206         } else {
207             System.out.println("compilerFailed:" + compiler.getCompilerMessage());
208         }
209     }
210     
211 }

 

转载于:https://www.cnblogs.com/zyun/p/10438082.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值