字串值提取工具-04-java 呼叫 java? Janino 編譯工具

老马啸西风發表於2024-08-16

值提取系列

值提取系列

字串值提取工具-01-概覽

字串值提取工具-02-java 呼叫 js

字串值提取工具-03-java 呼叫 groovy

字串值提取工具-04-java 呼叫 java? Janino 編譯工具

字串值提取工具-05-java 呼叫 shell

字串值提取工具-06-java 呼叫 python

字串值提取工具-07-java 呼叫 go

程式碼地址

value-extraction 值提取核心

場景

我們希望透過 java 執行 java,如何實現呢?

入門例子

程式碼

package org.example;

import javax.tools.*;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;

public class DynamicJavaExecutor {

    public static void main(String[] args) {
        // Java 程式碼字串
        String javaCode =
                "public class HelloWorld { " +
                        "    public static void main(String[] args) { " +
                        "        System.out.println(\"Hello, World!\"); " +
                        "    } " +
                        "} ";

        // 編譯 Java 程式碼
        boolean success = compileJavaCode("HelloWorld", javaCode);
        if (success) {
            try {
                // 使用 URLClassLoader 載入編譯後的類
                File file = new File("./"); // 獲取當前目錄
                URL url = file.toURI().toURL(); // 轉換為 URL
                URLClassLoader classLoader = new URLClassLoader(new URL[]{url});
                Class<?> clazz = classLoader.loadClass("HelloWorld");

                // 呼叫類的 main 方法
                Method mainMethod = clazz.getMethod("main", String[].class);
                String[] params = null; // 傳遞給 main 方法的引數
                mainMethod.invoke(null, (Object) params);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            System.out.println("Compilation failed.");
        }
    }

    public static boolean compileJavaCode(String className, String javaCode) {
        // 建立自定義的 JavaFileObject
        JavaFileObject fileObject = new InMemoryJavaFileObject(className, javaCode);

        // 獲取系統 Java 編譯器
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);

        // 設定輸出目錄
        Iterable<String> options = Arrays.asList("-d", "./");

        // 編譯 Java 程式碼
        JavaCompiler.CompilationTask task = compiler.getTask(
                null,
                fileManager,
                null,
                options,
                null,
                Arrays.asList(fileObject)
        );

        // 進行編譯
        return task.call();
    }

    // 內部類,用於在記憶體中表示 Java 原始檔
    static class InMemoryJavaFileObject extends SimpleJavaFileObject {
        private final String code;

        protected InMemoryJavaFileObject(String name, String code) {
            super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
            this.code = code;
        }

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

測試效果:

Hello, World!

Janino 例子

maven 引入

<dependency>
    <groupId>org.codehaus.janino</groupId>
    <artifactId>janino</artifactId>
    <version>3.1.9</version>
</dependency>

程式碼

package org.example;

import org.codehaus.janino.SimpleCompiler;

public class JaninoExample {

    public static void main(String[] args) throws Exception {
        // Java 程式碼字串
        String javaCode =
                "public class HelloWorld { " +
                        "    public void run() { " +
                        "        System.out.println(\"Hello, World!\"); " +
                        "    } " +
                        "} ";

        // 建立編譯器例項
        SimpleCompiler compiler = new SimpleCompiler();

        // 編譯 Java 程式碼
        compiler.cook(javaCode);

        // 獲取編譯後的類
        Class<?> clazz = compiler.getClassLoader().loadClass("HelloWorld");

        // 建立類的例項
        Object instance = clazz.getDeclaredConstructor().newInstance();

        // 呼叫 run 方法
        clazz.getMethod("run").invoke(instance);
    }

}

直接執行該方法

package com.github.houbb.value.extraction.test;

import org.codehaus.janino.ScriptEvaluator;

public class JavaDemoTest {

    public static void main(String[] args) throws Exception {
        // Java 指令碼字串
        String script =
                "System.out.println(\"Hello, World!\");";

        // 建立指令碼求值器例項
        ScriptEvaluator scriptEvaluator = new ScriptEvaluator();

        // 編譯並執行指令碼
        scriptEvaluator.cook(script);
        scriptEvaluator.evaluate(null);
    }

}

傳入引數,直接執行

package com.github.houbb.value.extraction.test;

import org.codehaus.janino.ScriptEvaluator;

import java.util.HashMap;
import java.util.Map;

public class JaninoExample {

    public static void main(String[] args) throws Exception {
        // 建立一個包含引數的 Map
        Map<String, Object> bindings = new HashMap<>();
        bindings.put("a", 10);
        bindings.put("b", 20);

        // 定義要執行的指令碼
        String script = "System.out.println(\"Result: \" + (a + b));";

        // 呼叫方法來執行指令碼
        executeScriptWithBindings(script, bindings);
    }

    public static void executeScriptWithBindings(String script, Map<String, Object> bindings) throws Exception {
        // 提取 Map 中的鍵(引數名)和值(引數值)
        String[] parameterNames = bindings.keySet().toArray(new String[0]);
        Class<?>[] parameterTypes = new Class<?>[parameterNames.length];

        // 假設所有引數的型別都是 Object,可以根據需要修改型別推斷邏輯
        for (int i = 0; i < parameterNames.length; i++) {
            parameterTypes[i] = bindings.get(parameterNames[i]).getClass();
        }

        // 建立 ScriptEvaluator 例項
        ScriptEvaluator scriptEvaluator = new ScriptEvaluator();

        // 設定指令碼的引數名稱和型別
        scriptEvaluator.setParameters(parameterNames, parameterTypes);

        // 編譯指令碼
        scriptEvaluator.cook(script);

        // 提取 Map 中的值作為引數
        Object[] parameterValues = bindings.values().toArray();

        // 執行指令碼
        scriptEvaluator.evaluate(parameterValues);
    }
}

但是感覺這個很麻煩,而且有問題

另一種執行的方式

package com.github.houbb.value.extraction.test.javas;

import org.codehaus.commons.compiler.CompileException;
import org.codehaus.janino.ScriptEvaluator;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

public class JaninoScriptMapExample {

    public static void main(String[] args) throws CompileException, InvocationTargetException {
        // 示例指令碼,使用 Map 引數
        String script =
                "return map.get(\"greeting\") + \", \" + map.get(\"name\") + \"!\";";

        // 建立 ScriptEvaluator 例項,指定返回型別、引數名和引數型別
        ScriptEvaluator se = new ScriptEvaluator(
                script,                      // 指令碼程式碼
                Object.class,                // 返回值型別
                new String[]{"map"},         // 引數名列表
                new Class<?>[]{Map.class}    // 引數型別列表
        );

        // 準備傳入的 Map 引數
        Map<String, Object> params = new HashMap<>();
        params.put("greeting", "Hello");
        params.put("name", "Janino");

        // 執行指令碼,傳入 Map 引數
        Object result = se.evaluate(new Object[]{params});

        // 輸出結果
        System.out.println(result);
    }

}

相關文章