@TOCjava 之动态生成类
最近遇到一个需求,要求属性字段是可以前端动态编辑,然后要求导入导出的数据列要同步属性字段。导入导出功能不想自己写,想要集成好的现成工具。可是他是通过类注解获取属性的, 然鹅我的属性是动态的,不可能提前写好,这可怎么办呢。。。!于是就开始研究是不是可以动态创建类文件,于是乎经过一番战痘,查阅大量网上前备们的文献资资料,总结测试。。。好像可以也(^-^)V
取之与群众,传之与群众
package test;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Objects;
/**
* desc:自定义的类加载器,用于实现类的动态加载
*/
public class MyClassLoader extends ClassLoader {
//回车加换行符
public String rt = "\r\n";
//生成类的源文件,写成字符串的形式
public String src =
"package test;"+
"public class HelloWorld {" + rt +
" public String name;" + rt +
" public static void main(String[] args) {" + rt +
" System.out.println(\"Hello world!\");" + rt +
" }" + rt +
" public String getName() {" + rt +
" return this.name; " + rt +
" }" + rt +
" public void setName(String name) {" + rt +
" this.name = name;" + rt +
" }" + rt +
"}";
//类路径 F:/java/code/j2es/out/production/j2es/
private String classPath;
//类名 类的全限定包名 不带后缀 例如com.test.Notice
private String className = "test.HelloWorld";
//class文件路径 F:/java/code/j2es/out/production/j2es/test/HelloWorld.class
private final String classPackageName;
//java文件路径 F:/java/code/j2es/out/production/j2es/test/HelloWorld.java
private final String javaPackageName;
public MyClassLoader(){
classPath = Objects.requireNonNull(MyClassLoader.class.getClassLoader().getResource("")).getPath();//获取编译后class文件所在的路径
classPath = !classPath.startsWith("/")?classPath:classPath.substring(1);//获取出来的前面会有一个/,去掉前面的/
System.out.println(classPath);
String packageName = classPath + className.replace(".",File.separator);//类名转换路径
classPackageName = packageName + ".class";
javaPackageName = packageName + ".java";
}
public MyClassLoader(String src,String classPath,String className){
this.src = src;
this.classPath = classPath;
this.className = className;
String packageName = classPath + className.replace(".",File.separator);//类名转换路径
classPackageName = packageName + ".class";
javaPackageName = packageName + ".java";
}
/**
* 指定的类的class文件是否存在
* @return boolean
*/
private boolean isClassExist() {
return FileUtil.isFileExist(classPackageName);
}
/**
* 指定的类java文件是否存在
* @return boolean
*/
private boolean isJavaExist(){
return FileUtil.isFileExist(javaPackageName);
}
/**
* 写文件
*/
private void writerFile() throws IOException {
File file = new File (javaPackageName);
FileWriter fw = new FileWriter (file);
fw.write(src);
fw.flush();
fw.close();
}
/**
* 编译java类
* 使用rt.jar中的javax.tools包提供的编译器
*/
private void compiler(){
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager sjfm = compiler.getStandardFileManager (null, null, null);
Iterable<? extends JavaFileObject> units = sjfm.getJavaFileObjects (javaPackageName);
JavaCompiler.CompilationTask ct = compiler.getTask (null, sjfm, null, null, null, units);
ct.call();
}
/**
* 复写父类的类加载方法
* @param name 类的全限定包名 不带后缀 例如com.test.Notice 而不要写成com.test.Notice.java
* @return Class
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
//加载类的字节码
byte[] classBytes;
try {
classBytes = Files.readAllBytes(Paths.get(classPackageName));
} catch (IOException e) {
throw new RuntimeException(e);
}
Class<?> clazz;
//将字节码交给JVM
clazz = defineClass(name,classBytes,0,classBytes.length);
if(clazz == null){
throw new ClassNotFoundException(name);
}
return clazz;
}
/**
* 动态编译一个java源文件并加载编译生成的class
*/
public Class<?> dynamicLoadClass() throws ClassNotFoundException, IOException {
//1、先将字符串源码写入java文件
writerFile();
//2、将java文件编译成class文件
compiler();
//3、把编译好的的交给JVM
Class<?> clazz = findClass(className);
//得到类后把java源码文件删掉
if(isJavaExist()){
if(!FileUtil.deleteFile(javaPackageName)){
System.out.println("删除源文件失败!");
}
}
return clazz;
}
public static void main (String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
MyClassLoader myClassLoader = new MyClassLoader();
Class<?> clazz = myClassLoader.dynamicLoadClass();
System.out.println(clazz);
// 生成对象
Object obj = clazz.newInstance();
System.out.println(obj);
// 调用main方法
Method m = clazz.getMethod("main",String[].class);
Object invoke = m.invoke(obj, new Object[] { new String[] {} });
System.out.println(invoke);
//调用setName
Method m2 = clazz.getMethod("setName",String.class);
Object invoke2 = m2.invoke(obj,"zhangsan");
System.out.println(invoke2);
//getName
Method m1 = clazz.getMethod("getName");
Object invoke1 = m1.invoke(obj);
System.out.println(invoke1);
}
}
何为传承
前面的人学会了,就要传授给后面的人,否则学而不传矢道也,知识得到传承才能开花和升华,名族才能强大
本文可以随意转载