最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java代碼實(shí)現(xiàn)編譯源文件

 更新時(shí)間:2025年01月20日 11:02:40   作者:倚欄聽風(fēng)雨  
這篇文章主要為大家詳細(xì)介紹了Java通過?JavaCompiler?實(shí)現(xiàn)編譯源文件的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下

原理

從Java 6開始,引入了Java代碼重寫過的編譯器接口,使得我們可以在運(yùn)行時(shí)編譯Java源代碼,然后再通過類加載器將編譯好的類加載進(jìn)JVM,這種在運(yùn)行時(shí)編譯代碼的操作就叫做動(dòng)態(tài)編譯。

主要類庫(kù)

JavaCompiler -表示java編譯器, run方法執(zhí)行編譯操作. 還有一種編譯方式是先生成編譯任務(wù)(CompilationTask), 讓后調(diào)用CompilationTask的call方法執(zhí)行編譯任務(wù)

JavaFileObject -表示一個(gè)java源文件對(duì)象

JavaFileManager - Java源文件管理類, 管理一系列JavaFileObject

Diagnostic -表示一個(gè)診斷信息

DiagnosticListener -診斷信息監(jiān)聽器, 編譯過程觸發(fā). 生成編譯task(JavaCompiler#getTask())或獲取FileManager(JavaCompiler#getStandardFileManager())時(shí)需要傳遞DiagnosticListener以便收集診斷信息

流程圖

源碼文件 -> 字節(jié)碼文件

    public static void fromJavaFile() {
        // 獲取Javac編譯器對(duì)象
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        // 獲取文件管理器:負(fù)責(zé)管理類文件的輸入輸出
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
        // 獲取要被編譯的Java源文件
        File file = new File("/Users//github/perfect/perfect-javassist/src/main/java/com/jc/javassist/compiler/TestHello.java");
        // 通過源文件獲取到要編譯的Java類源碼迭代器,包括所有內(nèi)部類,其中每個(gè)類都是一個(gè)JavaFileObject
        Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects(file);
        // 生成編譯任務(wù)
        JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, compilationUnits);
        // 執(zhí)行編譯任務(wù)
        task.call();
    }

我們這里準(zhǔn)備了TestHello.java

public class TestHello {
    public static void main(String[] args) {
        System.out.println("this is a test");
    }
}

我們?cè)囍謩?dòng)加載該class文件,使用類加載器的defineClass方法,可以直接加載字節(jié)碼文件。

public static Class<?> loadClassFromDisk(String path) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
   // defineClass 為 ClassLoader 類的一個(gè)方法,用于加載類
   // 但是這個(gè)方法是 protected 的,所以需要通過反射的方式獲取這個(gè)方法的權(quán)限
   Class<ClassLoader> classLoaderClass = ClassLoader.class;
    Method defineClass = classLoaderClass.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
    defineClass.setAccessible(true);
   // 讀取文件系統(tǒng)的 file 為 byte 數(shù)組
   File file = new File(path);
   byte[] bytes = new byte[(int) file.length()];
   try (FileInputStream fileInputStream = new FileInputStream(file)) {
        fileInputStream.read(bytes);
    } catch (IOException e) {
        e.printStackTrace();
    }
   // 執(zhí)行 defineClass 方法 返回 Class 對(duì)象
   return (Class<?>) defineClass.invoke(Thread.currentThread().getContextClassLoader(), bytes, 0, bytes.length);
}

由于TestHello中的方法為靜態(tài)方法,使用class反射機(jī)制執(zhí)行方法

// 執(zhí)行編譯任務(wù)
Boolean call = task.call();
if (call) {
    Class<?> o = loadClassFromDisk("/Users/oneyoung/oneyoung/project/my/code/src/main/java/top/oneyoung/dynamic/TestHello.class");
    Method main = o.getMethod("main", String[].class);
    main.invoke(null, new Object[]{new String[]{}});
}

執(zhí)行結(jié)果

| this is a test

源碼字符串 -> 字節(jié)碼文件

在流程圖中,getTask().call()會(huì)通過調(diào)用作為參數(shù)傳入的JavaFileObject對(duì)象的getCharContent()方法獲得字符串序列,即源碼的讀取是通過 JavaFileObject的 getCharContent()方法,那我們只需要重寫getCharContent()方法,即可將我們的字符串源碼裝進(jìn)JavaFileObject了。構(gòu)造SourceJavaFileObject實(shí)現(xiàn)定制的JavaFileObject對(duì)象,用于存儲(chǔ)字符串源碼

public class SourceJavaFileObject extends SimpleJavaFileObject {
   /**
     * The source code of this "file".
     */
   private final String code;
    SourceJavaFileObject(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;
    }
}

則創(chuàng)建JavaFileObject對(duì)象時(shí),變?yōu)榱耍?/p>

// 通過源代碼字符串獲取到要編譯的Java類源碼迭代器,包括所有內(nèi)部類,其中每個(gè)類都是一個(gè)JavaFileObject
SourceJavaFileObject javaFileObject = new SourceJavaFileObject("TestHello", "public class TestHello { public static void main(String[] args) { System.out.println("Hello World"); } }");
// 生成編譯任務(wù)
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, Collections.singleton(javaFileObject));

執(zhí)行后,同樣編譯出了class文件,不過由于沒有指定編譯的class輸出路徑,他會(huì)默認(rèn)放在源文件的根目錄下

源碼字符串 -> 字節(jié)碼數(shù)組

如果我們進(jìn)行動(dòng)態(tài)編譯時(shí),想要直接輸入源碼字符串并且輸出的是字節(jié)碼數(shù)組,而不是輸出字節(jié)碼文件,又該如何實(shí)現(xiàn)?實(shí)際上,這是從內(nèi)存中得到源碼,再輸出到內(nèi)存的方式。

在getTask().call()源代碼執(zhí)行流程圖中,我們可以發(fā)現(xiàn)JavaFileObject 的 openOutputStream()方法控制了編譯后字節(jié)碼的輸出行為,編譯完成后會(huì)調(diào)用openOutputStream獲取輸出流,并寫數(shù)據(jù)(字節(jié)碼)。所以我們需要重寫JavaFileObject 的 openOutputStream()方法。

同時(shí)在執(zhí)行流程圖中,我們還發(fā)現(xiàn)用于輸出的JavaFileObject 對(duì)象是JavaFileManager的getJavaFileForOutput()方法提供的,所以為了讓編譯器編譯完成后,將編譯得到的字節(jié)碼輸出到我們自己構(gòu)造的JavaFileObject 對(duì)象,我們還需要自定義JavaFileManager。

這里我使用類委托的方式,把大部分功能委托給了傳入的StandardJavaFileManager,主要是重寫了getJavaFileForOutput,使輸出編譯完成的字節(jié)碼文件為字節(jié)數(shù)組。

然后增加了方法getBytesByClassName獲取編譯完成的字節(jié)碼字節(jié)數(shù)組

public class ByteArrayJavaFileManager implements JavaFileManager {
   private static final Logger LOG = LoggerFactory.getLogger(ByteArrayJavaFileManager.class);
   private final StandardJavaFileManager fileManager;
   /**
     * synchronizing due to ConcurrentModificationException
     */
   private final Map<String, ByteArrayOutputStream> buffers = Collections.synchronizedMap(new LinkedHashMap<>());
   public ByteArrayJavaFileManager(StandardJavaFileManager fileManager) {
       this.fileManager = fileManager;
    }
   @Override
   public ClassLoader getClassLoader(Location location) {
       return fileManager.getClassLoader(location);
    }
   @Override
   public synchronized Iterable<JavaFileObject> list(Location location, String packageName, Set<Kind> kinds, boolean recurse) throws IOException {
       return fileManager.list(location, packageName, kinds, recurse);
    }
   @Override
   public String inferBinaryName(Location location, JavaFileObject file) {
       return fileManager.inferBinaryName(location, file);
    }
   @Override
   public boolean isSameFile(FileObject a, FileObject b) {
       return fileManager.isSameFile(a, b);
    }
   @Override
   public synchronized boolean handleOption(String current, Iterator<String> remaining) {
       return fileManager.handleOption(current, remaining);
    }
   @Override
   public boolean hasLocation(Location location) {
       return fileManager.hasLocation(location);
    }
   @Override
   public JavaFileObject getJavaFileForInput(Location location, String className, Kind kind) throws IOException {
       if (location == StandardLocation.CLASS_OUTPUT) {
           boolean success;
           final byte[] bytes;
           synchronized (buffers) {
                success = buffers.containsKey(className) && kind == Kind.CLASS;
                bytes = buffers.get(className).toByteArray();
            }
           if (success) {
               return new SimpleJavaFileObject(URI.create(className), kind) {
                   @Override
                   public InputStream openInputStream() {
                       return new ByteArrayInputStream(bytes);
                    }
                };
            }
        }
       return fileManager.getJavaFileForInput(location, className, kind);
    }
   @Override
   public JavaFileObject getJavaFileForOutput(Location location, final String className, Kind kind, FileObject sibling) {
       return new SimpleJavaFileObject(URI.create(className), kind) {
           @Override
           public OutputStream openOutputStream() {
               // 字節(jié)輸出流用與FileManager輸出編譯完成的字節(jié)碼文件為字節(jié)數(shù)組
               ByteArrayOutputStream bos = new ByteArrayOutputStream();
               // 將每個(gè)需要加載的類的輸出流進(jìn)行緩存
               buffers.putIfAbsent(className, bos);
               return bos;
            }
        };
    }
   @Override
   public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
       return fileManager.getFileForInput(location, packageName, relativeName);
    }
   @Override
   public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) throws IOException {
       return fileManager.getFileForOutput(location, packageName, relativeName, sibling);
    }
   @Override
   public void flush() {
       // Do nothing
   }
   @Override
   public void close() throws IOException {
       fileManager.close();
    }
   @Override
   public int isSupportedOption(String option) {
       return fileManager.isSupportedOption(option);
    }
   public void clearBuffers() {
       buffers.clear();
    }
   public Map<String, byte[]> getAllBuffers() {
        Map<String, byte[]> ret = new LinkedHashMap<>(buffers.size() * 2);
        Map<String, ByteArrayOutputStream> compiledClasses = new LinkedHashMap<>(ret.size());
       synchronized (buffers) {
            compiledClasses.putAll(buffers);
        }
        compiledClasses.forEach((k, v) -> ret.put(k, v.toByteArray()));
       return ret;
    }
   public byte[] getBytesByClassName(String className) {
       return buffers.get(className).toByteArray();
    }
}

然后我們修改下之前的執(zhí)行流程

public static void fromJavaSourceToByteArray1() throws Exception {
   // 獲取Javac編譯器對(duì)象
   JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
   // 獲取文件管理器:負(fù)責(zé)管理類文件的輸入輸出
   StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
   // 創(chuàng)建自定義的FileManager
   ByteArrayJavaFileManager byteArrayJavaFileManager = new ByteArrayJavaFileManager(fileManager);
   // 通過源代碼字符串獲取到要編譯的Java類源碼迭代器,包括所有內(nèi)部類,其中每個(gè)類都是一個(gè)JavaFileObject
   JavaFileObject javaFileObject = new SourceJavaFileObject("TestHello", "public class TestHello { public static void say(String args) { System.out.println(args); } }");
    JavaCompiler.CompilationTask task = compiler.getTask(null, byteArrayJavaFileManager, null, null, null, Collections.singletonList(javaFileObject));
   // 執(zhí)行編譯任務(wù)
   Boolean call = task.call();
   if (Boolean.TRUE.equals(call)) {
       byte[] testHellos = byteArrayJavaFileManager.getBytesByClassName("TestHello");
        Class<ClassLoader> classLoaderClass = ClassLoader.class;
        Method defineClass = classLoaderClass.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
        defineClass.setAccessible(true);
        Object invoke = defineClass.invoke(TestHello.class.getClassLoader(), testHellos, 0, testHellos.length);
        Class clazz = (Class) invoke;
        clazz.getMethod("say", String.class).invoke(null, "你好");
    }
}

以上就是java代碼實(shí)現(xiàn)編譯源文件的詳細(xì)內(nèi)容,更多關(guān)于java編譯源文件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java Json字符串的雙引號(hào)(

    Java Json字符串的雙引號(hào)("")括號(hào)如何去掉

    這篇文章主要介紹了Java Json字符串的雙引號(hào)("")括號(hào)如何去掉?具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • spring boot多數(shù)據(jù)源動(dòng)態(tài)切換代碼實(shí)例

    spring boot多數(shù)據(jù)源動(dòng)態(tài)切換代碼實(shí)例

    這篇文章主要介紹了spring boot多數(shù)據(jù)源動(dòng)態(tài)切換代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • Springboot3+Redis實(shí)現(xiàn)消息隊(duì)列的多種方法小結(jié)

    Springboot3+Redis實(shí)現(xiàn)消息隊(duì)列的多種方法小結(jié)

    本文主要介紹了Springboot3+Redis實(shí)現(xiàn)消息隊(duì)列的多種方法小結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-03-03
  • Spring Boot對(duì)Future模式的支持詳解

    Spring Boot對(duì)Future模式的支持詳解

    這篇文章主要給大家介紹了關(guān)于Spring Boot對(duì)Future模式的支持的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用spring boot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧
    2019-01-01
  • Java的“Goto”與標(biāo)簽及使用詳解

    Java的“Goto”與標(biāo)簽及使用詳解

    goto在Java中是一個(gè)保留字,但在語言中并沒有用到它;Java沒有g(shù)oto。接下來通過本文給大家介紹Java的“Goto”與標(biāo)簽,感興趣的朋友跟隨小編一起看看吧
    2018-10-10
  • SpringCloud實(shí)現(xiàn)基于RabbitMQ消息隊(duì)列的詳細(xì)步驟

    SpringCloud實(shí)現(xiàn)基于RabbitMQ消息隊(duì)列的詳細(xì)步驟

    在Spring Cloud框架中,我們可以利用RabbitMQ實(shí)現(xiàn)強(qiáng)大而可靠的消息隊(duì)列系統(tǒng),本篇將詳細(xì)介紹如何在Spring Cloud項(xiàng)目中集成RabbitMQ,并創(chuàng)建一個(gè)簡(jiǎn)單的消息隊(duì)列,感興趣的朋友一起看看吧
    2024-03-03
  • Java Collection 體系與使用場(chǎng)景多角度分析

    Java Collection 體系與使用場(chǎng)景多角度分析

    本文從抽象層級(jí)、接口設(shè)計(jì)和實(shí)際使用場(chǎng)景三個(gè)角度,系統(tǒng)介紹了Java Collection框架,文章詳細(xì)說明了Collection的基本概念、Collection在體系中的位置、為什么要有Collection接口以及在不同場(chǎng)景下的使用建議,感興趣的朋友跟隨小編一起看看吧
    2026-01-01
  • Java優(yōu)先隊(duì)列?priority?queue

    Java優(yōu)先隊(duì)列?priority?queue

    本文主要介紹了Java優(yōu)先隊(duì)列?priority?queue,優(yōu)先隊(duì)列是一種特殊的數(shù)據(jù)結(jié)構(gòu)隊(duì)列中每一個(gè)元素都被分配到一個(gè)優(yōu)先權(quán)值,出隊(duì)順序按照優(yōu)先權(quán)值來劃分。一般有兩種出隊(duì)順序高優(yōu)先權(quán)出隊(duì)或低優(yōu)先權(quán)出隊(duì),想了解具體內(nèi)容的小伙伴可以參考下文內(nèi)容,希望對(duì)你有所幫助
    2021-12-12
  • 關(guān)于Future機(jī)制原理及解析

    關(guān)于Future機(jī)制原理及解析

    這篇文章主要介紹了關(guān)于Future機(jī)制原理,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • Java咖啡館(1)——嘆咖啡

    Java咖啡館(1)——嘆咖啡

    這篇文章主要給大家介紹了關(guān)于Java咖啡館之嘆咖啡,需要的朋友可以參考下
    2006-12-12

最新評(píng)論

保山市| 宁武县| 于都县| 昌宁县| 莱阳市| 资兴市| 梅河口市| 沂南县| 哈尔滨市| 贵南县| 新田县| 临洮县| 汶川县| 金坛市| 宜丰县| 玛曲县| 定远县| 巧家县| 福泉市| 巨鹿县| 曲阜市| 宝鸡市| 化德县| 肇庆市| 云龙县| 林西县| 霞浦县| 岑溪市| 锡林浩特市| 两当县| 福贡县| 襄樊市| 天全县| 呈贡县| 扶沟县| 清水县| 称多县| 松滋市| 东平县| 潞城市| 镇沅|