java 动态编译.java

在java项目内获取整个电脑任意位置的.class文件进行实例化,执行以下案例时,需要从jdk中引入tool.jar 文件

第一种:使用urlclassloader进行

package test;

import java.net.URL;
import java.net.URLClassLoader;

/**
 * @ClassName Test
 * @Description
 * @Date 2021/11/26 11:06
 **/
public class TestURLClassLoader {

    public static void main(String[] args) throws Exception{
        //.class 路径地址
        URL[] urls = new URL[] {new URL("file:/"+"F:\\CODE\\STC-SLIT\\STC-tbook\\sc-app-stc-quant-sdk\\filed\\TestStrategy.class")};
        //load 到内存中
        URLClassLoader loader = new URLClassLoader(urls);
        //进行反射获取
        Class c = loader.loadClass("com.stc.quant.TestStrategy");
        //实例化
        Object o = c.newInstance();
        System.out.println("c = " + c);
        //获取当前实例对象的方法,invoke(o, null) ,o为实例对象
        Object getQuantId = c.getMethod("getQuantId").invoke(o, null);
        System.out.println("getQuantId = " + getQuantId);
    }
}

第二种:使用二进制对class文件实例

package test;

import sun.misc.IOUtils;

import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;

/**
 * 二进制转换,继承classloader
 * @Description
 * @Date 2021/11/24 10:38
 **/
public class TestClassLoader extends ClassLoader {

    private  String className;
    //转换byte后的字节码
    private  byte[] classBytes;

    private TestClassLoader(String className, String localPath) throws IOException {
        this.className = className;
        this.classBytes = this.getByteClass(localPath);
    }


    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        //只处理classloader.test类
        if (name.equals(className)) {
            //调用definClass将一个字节流定义为一个类。
            return defineClass(className, classBytes, 0, classBytes.length);
        }
        return super.findClass(name);
    }

    public static void main(String[] args) throws Exception {
        String className = "com.stc.quant.TestStrategy";
        String localPath = "F:\\CODE\\STC-SLIT\\STC-tbook\\sc-app-stc-quant-sdk\\filed\\TestStrategy.class";
        //创建加载器
        TestClassLoader clt = new TestClassLoader(className,localPath);
        //使用我们自定义的类去加载className
        Class clazz = clt.loadClass(className);
        //反射创建test类对象
        Object test = clazz.newInstance();
        //反射获取方法
        Method method = test.getClass().getMethod("getQuantId");
        //反射去调用执行方法
        String str = (String) method.invoke(test);
        System.out.println(str);

    }

    public byte[] getByteClass(String localPath) throws IOException {
        FileInputStream fis = new FileInputStream(localPath);
        byte[] classBytes = IOUtils.readFully(fis, -1, false);
        return classBytes;

    }
}

第三种:将字符串实例化为一个对象,业务方面需要把java类上传到数据库中,由数据库取出对字符串进行实例化。因没有进行数据库操作,则定义一个java类进行读取,然后传入JavaCompiler.CompilationTask中进行编译操作

首先先看一下源码里面的讲解,里面的参数都是什么

为具有给定组件和参数的编译任务创建未来。编译可能没有按照CompilationTask界面中的描述完成。

如果提供了文件管理器,它必须能够处理StandardLocation中定义的所有位置。

请注意,注释处理既可以处理要编译的源代码的编译单元(通过compilationUnits参数传递),也可以处理类文件(其名称通过classes参数传递)。

参数:

out–用于从编译器获得额外输出的写入程序;如果为空,请使用System.err

fileManager–文件管理器;如果为null,则使用编译器的标准文件管理器

diagnosticListener–诊断侦听器;如果为null,则使用编译器的默认方法报告诊断

选项–编译器选项,null表示没有选项

classes–注释处理要处理的类的名称,null表示没有类名

compilationUnits–要编译的编译单元,null表示没有编译单元

返回:

表示编译的对象

抛出:

RuntimeException–如果用户提供的组件中发生不可恢复的错误。原因将是用户代码中的错误。

IllegalArgumentException–如果任何选项无效,或者如果任何给定编译单元不是源代码类型
CompilationTask getTask(Writer out,
                            JavaFileManager fileManager,
                            DiagnosticListener<? super JavaFileObject> diagnosticListener,
                            Iterable<String> options,
                            Iterable<String> classes,
                            Iterable<? extends JavaFileObject> compilationUnits);

下面是读取字符串并进行动态编译的过程

package test;

import sc.app.stc.quant.sdk.utils.javatool.CharSequenceJavaFileObject;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.util.Arrays;

/**
 * @ClassName StrToJavaCompile
 * @Description
 * @Date 2021/11/26 15:14
 **/
public class TestStrToJavaCompile {

    public static void main(String[] args) throws Exception {
        String name = "com.stc.quant.TestStrategy";
        String path = "F:\\GitTest1\\TestStrategy.java";

        String s = readFile(path);
        Class<?> compile = compile(name, s);
        Object o = compile.newInstance();
        Object getQuantId = compile.getMethod("getQuantId").invoke(o, null);
        System.out.println("getQuantId = " + getQuantId);
    }


    /**
     *
     * @param className
     * @param javaCodes
     * @return
     */
    private static Class<?> compile(String className, String javaCodes) {
        //获取系统编译器
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        //此文件管理器创建一个代表普通文件对象files , zip file entries ,或类似的基于文件系统的容器中的条目。
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null,                 null, null);
        //字符串转移拼接
        CharSequenceJavaFileObject srcObject = new CharSequenceJavaFileObject(className, javaCodes);
        //放入JavaFileObject类中,源码中继承自FileObject类
        Iterable<? extends JavaFileObject> fileObjects = Arrays.asList(srcObject);
        //-d选项指定的目录下(保留包名)
        String flag = "-d";
        String outDir = "";
        try {
            //路径获取
            File classPath = new File(Thread.currentThread().getContextClassLoader().getResource("").toURI());
            outDir = classPath.getAbsolutePath() + File.separator;
        } catch (URISyntaxException e1) {
            e1.printStackTrace();
        }
        //使用编译选项可以改变默认编译行为。编译选项是一个元素为String类型的Iterable集合
        Iterable<String> options = Arrays.asList(flag, outDir);
        // 建立用于保存被编译文件名的对象
        // 每个文件被保存在一个从JavaFileObject继承的类中
        JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, options, null, fileObjects);
        // 编译源程序
        boolean result = task.call();
        if (result == true) {
            try {
                //如果编译成功,用类加载器加载该类
                return Class.forName(className);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 读取文件,一行行读取拼接为字符串
     * @param path
     * @return
     * @throws Exception
     */
    public static String readFile(String path) throws Exception{
        File file = new File(path);
        StringBuffer stringBuffer = new StringBuffer();
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
        String s = null;
        while ((s = br.readLine()) != null) {
            System.out.println(System.lineSeparator()+s);
            stringBuffer.append(System.lineSeparator() + s);
        }
        System.out.println("stringBuffer.toString() = " + stringBuffer.toString());
        return stringBuffer.toString();
    }
}

使用到的文件转义类

package sc.app.stc.quant.sdk.utils.javatool;

import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import java.net.URI;

public class CharSequenceJavaFileObject extends SimpleJavaFileObject {

    private CharSequence content;


    public CharSequenceJavaFileObject(String className,
                                      CharSequence content) {
        super(URI.create("string:///" + className.replace('.', '/')
                + JavaFileObject.Kind.SOURCE.extension), JavaFileObject.Kind.SOURCE);
        this.content = content;
    }

    @Override
    public CharSequence getCharContent(
            boolean ignoreEncodingErrors) {
        return content;
    }
}

还可以自定义文件管理器监听类,从监听类中获取实例

package sc.app.stc.quant.sdk.utils.javatool;

import java.io.IOException;

import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;

@SuppressWarnings({ "unchecked", "rawtypes" })
public class ClassFileManager extends ForwardingJavaFileManager {
	public JavaClassObject getJavaClassObject() {
		return jclassObject;
	}

	private JavaClassObject jclassObject;

	public ClassFileManager(StandardJavaFileManager standardManager) {
		super(standardManager);
	}

	public JavaFileObject getJavaFileForOutput(Location location,
			String className, JavaFileObject.Kind kind, FileObject sibling)
			throws IOException {
		jclassObject = new JavaClassObject(className, kind);
		return jclassObject;
	}
}

对上面的测试案例进行补充的,关注新添加的ClassFileManager类,实现一个监听

	/**
	 * 动态编译实例
	 * @param fullClassName  报名加类名
	 * @param javaCode  代码字符串
	 * @return
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 */
public Object javaCodeToObject(String fullClassName, String javaCode)
			throws IllegalAccessException, InstantiationException {

		Object instance = null;

		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

		DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
		ClassFileManager fileManager = new ClassFileManager(compiler.getStandardFileManager(diagnostics, null, null));

		List<JavaFileObject> jfiles = new ArrayList<JavaFileObject>();
		jfiles.add(new CharSequenceJavaFileObject(fullClassName, javaCode));

		List<String> options = new ArrayList<String>();
		options.add("-encoding");
		options.add("UTF-8");
		options.add("-classpath");
		options.add(this.classpath);

		JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, options, null, jfiles);
		boolean success = task.call();

		if (success) {
			JavaClassObject jco  = fileManager.getJavaClassObject();
			@SuppressWarnings("resource")
			DynamicClassLoader dynamicClassLoader = new DynamicClassLoader(this.parentClassLoader);
			Class<?> clazz = dynamicClassLoader.loadClass(fullClassName, jco);
			instance = clazz.newInstance();
		} else {
			String error = "";
			for (Diagnostic<?> diagnostic : diagnostics.getDiagnostics()) {
				error = error + compilePrint(diagnostic);
			}
			logger.info("buildMessage:{}", error);
		}
		return instance;
	}

也没写过这类东西,第一次写,都是从各大网站看到的,最后自己测试通过的,特此来进行记录一下

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无奈的码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值