写点什么

Java 是动态语言吗?JavaCompiler 实现动态编译,并通过反射赋值

用户头像
极客good
关注
发布于: 刚刚

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();


//JavaCompiler 中最核心的方法是 run()。通过这个方法能编译 java 源代码。


int run(InputStream in, OutputStream out, OutputStream err, String... arguments)


参数分别用来为:


  1. java 编译器提供参数

  2. 得到 Java 编译器的输出信息

  3. 接收编译器的错误信息,

  4. 一个或多个 Java 源程式文件


如果 run 编译成功,返回? 0。


如果前 3 个参数传入的是null,那么 run 方法将以标准的输入、输出代替,即System.inSystem.outSystem.err。如果我们要编译一个test.java文件,并将使用标准输入输出,run 的使用方法如下:


int results = tool.run(null, null, null, "D:\test\Student.java");


五、通过 URLClassLoader 加载程序外的 jar 包,并进行动态编译



1、实体类 Student

package com.guor.bean;


public class Student {


public Integer id;


public String name;


public void hello(String param) {


System.out.println(param);


}


public Integer getId() {


return id;


}


public void setId(Integer id) {


this.id = id;


}


public String getName() {


return name;


}


public void setName(String name) {


this.name = name;


}


@Override


public String toString() {


return "Student [id=" + id + ", name=" + name + "]";


}


}

2、Java 文件 -> class -> jar -> 动态编辑 -> 反射赋值

private void test01() throws Exception {


final String javaPath = "D:\test\java";


final String studentPath = javaPath + "\Student.java";


final String jarPath = "D:\test\jar\student-1.0.0.jar";


final String packageStudentPath = "com.guor.bean.Student";


// 将 java 源文件编译成.class 字节码文件


String cmd = "javac " + studentPath;


System.out.println(cmd);


boolean execSysCmd = execCmd(cmd);


System.out.println(execSysCmd);


// 打成 jar 包


cmd = "jar -cvf " + jarPath + " " + javaPath;


System.out.println(cmd);


execSysCmd = execCmd(cmd);


System.out.println(execSysCmd);


/**


  • URLClassLoader:继承自 SecureClassLoader,支持从 jar 文件和文件夹中获取 class,

  • 继承于 classload,加载时首先去 classload 里判断是否由 bootstrap classload 加载过


*/


URL url = new URL("file:" + jarPath);


URLClassLoader classLoader = new URLClassLoader(new URL[] { url },


Thread.currentThread().getContextClassLoader());


CusCompiler compiler = new CusCompiler(classLoader);


File file = new File(studentPath);


String beanTxt = FileUtils.readFileToString(file, "utf-8");


Map<String, byte[]> results = compiler.compile(packageStudentPath, beanTxt);


Class<?> clazz = compiler.loadClass(packageStudentPath, results);


Object object = clazz.newInstance();


Method method = clazz.getDeclaredMethod("setId", Integer.class);


method.invoke(object, 1);


method = clazz.getDeclaredMethod("setName", String.class);


method.invoke(object, "哪吒");


System.out.println(object);


method = clazz.getDeclaredMethod("hello", String.class);


method.invoke(object, "我命由我不由天");


}

3、执行 cmd 命令

private boolean execCmd(String cmd) {


try {


Runtime rt = Runtime.getRuntime();


Process proc = rt.exec(cmd);


InputStream es = proc.getErrorStream();


String line;


BufferedReader br;


br = new BufferedReader(new InputStreamReader(es, "GBK"));


StringBuffer buffer=new StringBuffer();


while ((line = br.readLine()) != null) {


buffer.append(line+"\n");


}


} catch (Exception e) {


return false;


}


return true;


}



六、编译非文件形式源代码



1、通过 JavaCompiler 动态编译

public static void test() {


try {


String beanName = "Student";


String appendTxt = "private String realName";


AppendBeanUtil.appendBean(beanName, appendTxt);


//动态编译


JavaCompiler javac = ToolProvider.getSystemJavaCompiler();


String className = "D:\workspace\yygh_parent\myTest\src\main\java\com\guor\bean\Student.java";


int status = javac.run(null, null, null, "-d", System.getProperty("user.dir")+"\target\classes",className);


if(status!=0){


System.out.println("没有编译成功!");


}


ClassLoader classLoader = Student.class.getClassLoader();


Class<?> classLoad = classLoader.loadClass(Student.class.getName());


Object newInstance = classLoad.newInstance();


Method setName = classLoad.getDeclaredMethod("setName", String.class);


setName.invoke(newInstance,"哪吒");


System.out.println("动态载入属性成功+++++"+newInstance);


} catch (Exception e) {


System.out.println(e);


}


}

2、springboot 中动态编译工程内存在的 bean

JDK 6 的编译器 API 的另外一个强大之处在于,它可以编译的源文件的形式并不局限于文本文件。JavaCompiler 类依靠文件管理服务可以编译多种形式的源文件。比如直接由内存中的字符串构造的文件,或者是从数据库中取出的文件。这种服务是由 JavaFileManager 类提供的。


在 Java SE6 中最佳的方法是使用 StandardJavaFileManager 类。这个类能非常好地控制输入、输出,并且能通过 DiagnosticListener 得到诊断信息,而 DiagnosticCollector 类就是 listener 的实现。新的 JDK 定义了 javax.tools.FileObject 和 javax.tools.JavaFileObject 接口。任何类,只要实现了这个接口,就可以被 JavaFileManager 识别。


使用 StandardJavaFileManager 步骤:


  1. 建立一个 DiagnosticCollector 实例

  2. 通过 JavaCompiler.getStandardFileManager()方法得到一个 StandardFileManager 对象。

  3. 使用 StandardFileManager 获取需要编译的源代码。从文件或者字符流中获取源代码。

  4. JavaCompiler.getTask()生成编译任务抽象。

  5. 通过 CompilationTask.call()方法编译源代码。

  6. 关闭 StandardFileManager。


代码实例:


(1)动态编译工程内存在的 bean


private void test02() {


try {


String beanName = "Student";


String appendTxt = "private String realName;";


AppendBeanUtil.appendBean(beanName, appendTxt);


String class_name = "com.guor.bean.Student";


URLClassLoader contextClassLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader();


CusCompiler compiler = new CusCompiler(contextClassLoader);


String script = FileUtils.readFileToString(new File("D:\workspace\yygh_parent\myTest\src\main\java\com\guor\bean\Student.java"),"utf-8");


Map<String, byte[]> results = compiler.compile(class_name, script);


Class<?> clazz = compiler.loadClass(class_name, results);


System.out.println("+++++++"+clazz);


Object newInstance = clazz.newInstance();


Method setName = clazz.getDeclaredMethod("setRealName", String.class);


setName.invoke(newInstance,"哪吒");


System.out.println("动态载入属性成功+++++"+newInstance);


} catch (Exception e) {


System.out.println(e);


}


}


(2)Compiler 工具类


import javax.lang.model.element.Modifier;


import javax.lang.model.element.NestingKind;


import javax.tools.*;


import org.slf4j.Logger;


import org.slf4j.LoggerFactory;


import java.io.*;


import java.net.*;


import java.nio.CharBuffer;


import java.util.*;


import java.util.jar.JarEntry;


public class CusCompiler {


private final static Logger log = LoggerFactory.getLogger(CusCompiler.class);


static class CustomJavaFileObject implements JavaFileObject {


private String binaryName;


private URI uri;


private String name;


public String binaryName() {


return binaryName;


}


public CustomJavaFileObject(String binaryName, URI uri) {


this.uri = uri;


this.binaryName = binaryName;


name = uri.getPath() == null ? uri.getSchemeSpecificPart() : uri.getPath();


}


@Override


public Kind getKind() {


return Kind.CLASS;


}


@Override


public boolean isNameCompatible(String simpleName, Kind kind) {


String baseName = simpleName + kind.extension;


return kind.equals(getKind()) && (baseName.equals(getName()) || getName().endsWith("/" + baseName));


}


@Override


public NestingKind getNestingKind() {


throw new UnsupportedOperationException();


}


@Override


public Modifier getAccessLevel() {


throw new UnsupportedOperationException();


}


@Override


public URI toUri() {


return uri;


}


@Override


public String getName() {


return name;


}


@Override


public InputStream openInputStream() throws IOException {


return uri.toURL().openStream();


}


@Override


public OutputStream openOutputStream() throws IOException {


throw new UnsupportedOperationException();


}


@Override


public Reader openReader(boolean ignoreEncodingErrors) throws IOException {


throw new UnsupportedOperationException();


}


@Override


public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {


throw new UnsupportedOperationException();


}


@Override


public Writer openWriter() throws IOException {


throw new UnsupportedOperationException();


}


@Override


public long getLastModified() {


return 0;


}


@Override


public boolean d


【一线大厂Java面试题解析+核心总结学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


elete() {


throw new UnsupportedOperationException();


}


}


static class MemoryInputJavaFileObject extends SimpleJavaFileObject {


final String code;


MemoryInputJavaFileObject(String name, String code) {


super(URI.create(name.replaceAll("\.", "/") + Kind.SOURCE.extension), Kind.SOURCE);


this.code = code;


}


@Override


public CharBuffer getCharContent(boolean ignoreEncodingErrors) {


return CharBuffer.wrap(code);


}


}


static class MemoryOutputJavaFileObject extends SimpleJavaFileObject {


final String name;


Map<String, byte[]> class_out;


MemoryOutputJavaFileObject(String name, Map<String, byte[]> out) {


super(URI.create(name.replaceAll("\.", "/") + Kind.SOURCE.extension), Kind.CLASS);


this.name = name;


this.class_out = out;


}


@Override


public OutputStream openOutputStream() {


return new FilterOutputStream(new ByteArrayOutputStream()) {


@Override


public void close() throws IOException {


out.close();


ByteArrayOutputStream bos = (ByteArrayOutputStream) out;


class_out.put(name, bos.toByteArray());


}


};


}


}


static class SpringBootJarFileManager implements JavaFileManager {


private URLClassLoader classLoader;


private StandardJavaFileManager standardJavaFileManager;


final Map<String, byte[]> classBytes = new HashMap<>();


SpringBootJarFileManager(StandardJavaFileManager standardJavaFileManager, URLClassLoader systemLoader) {


this.classLoader = new URLClassLoader(systemLoader.getURLs(), systemLoader);


this.standardJavaFileManager = standardJavaFileManager;


}


@Override


public ClassLoader getClassLoader(Location location) {


return classLoader;


}


private List<JavaFileObject> find(String packageName) {


List<JavaFileObject> result = new ArrayList<>();


String javaPackageName = packageName.replaceAll("\.", "/");


try {


Enumeration<URL> urls = classLoader.findResources(javaPackageName);


while (urls.hasMoreElements()) {


URL ll = urls.nextElement();


String ext_form = ll.toExternalForm();


String jar = ext_form.substring(0, ext_form.lastIndexOf("!"));


String pkg = ext_form.substring(ext_form.lastIndexOf("!") + 1);


JarURLConnection conn = (JarURLConnection) ll.openConnection();


conn.connect();


Enumeration<JarEntry> jar_items = conn.getJarFile().entries();

用户头像

极客good

关注

还未添加个人签名 2021.03.18 加入

还未添加个人简介

评论

发布
暂无评论
Java是动态语言吗?JavaCompiler实现动态编译,并通过反射赋值