欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

教你怎么實(shí)現(xiàn)java語(yǔ)言的在線編譯

 更新時(shí)間:2021年04月27日 09:21:30   作者:照水然犀  
這篇文章主要介紹了教你怎么實(shí)現(xiàn)java語(yǔ)言的在線編譯,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下

一、前言

  • 使用過(guò)leetcode或者類似在線編譯網(wǎng)站功能的人,或許會(huì)比較感興趣,關(guān)于在線編譯的實(shí)現(xiàn)原理,由于我比較頭鐵,所以一沖動(dòng)之下畢業(yè)設(shè)計(jì)的項(xiàng)目選擇制作一個(gè)類似于在線編譯的一個(gè)網(wǎng)站。
  • 在決定做這個(gè)之前,大概對(duì)這方面的東西一竅不通,網(wǎng)上的資料很多也是比較千篇一律,給我這種萌新帶來(lái)的難度不是一點(diǎn)半點(diǎn),當(dāng)然,最終收獲還是挺大的,所以想寫(xiě)一點(diǎn)東西,作為梳理,也給以后想學(xué)的人做一個(gè)參考作用(其實(shí)在寫(xiě)的過(guò)程中還是踩了一些坑的)。
  • 最終,其實(shí)成果挺水的,做出來(lái)的成品,就只是實(shí)現(xiàn)了一個(gè)簡(jiǎn)陋的Java語(yǔ)言的在線編譯功能,這里也想吐槽一下,其實(shí)leetcode,支持那么多語(yǔ)言的在線編譯真的挺厲害的。

二、前期準(zhǔn)備

首先在運(yùn)行java程序之前,肯定要想辦法把.java的文件使用編譯器,編譯成.class的字節(jié)碼文件。

運(yùn)氣好的是,強(qiáng)大的Java已經(jīng)具備類似的API,就是JavaCompiler類,下面做一點(diǎn)簡(jiǎn)單介紹:

JavaCompiler是java語(yǔ)言自帶的一個(gè)接口,大概是一個(gè)對(duì)Java編譯器的一個(gè)抽象,通過(guò)ToolProvider 類的靜態(tài)方法獲取其實(shí)現(xiàn)對(duì)象:

public interface JavaCompiler extends Tool, OptionChecker
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

稍微看一下源碼

private static final String defaultJavaCompilerName
        = "com.sun.tools.javac.api.JavacTool";
private static synchronized ToolProvider instance() {
        if (instance == null)
            instance = new ToolProvider();
        return instance;
    }
    /**
     * Gets the Java™ programming language compiler provided
     * with this platform.
     * @return the compiler provided with this platform or
     * {@code null} if no compiler is provided
     */
    public static JavaCompiler getSystemJavaCompiler() {
        return instance().getSystemTool(JavaCompiler.class, defaultJavaCompilerName);
    }

可以知道,返回的是一個(gè)JavacTool對(duì)象,是一個(gè)接口實(shí)現(xiàn)類

public final class JavacTool implements JavaCompiler {

這個(gè)類實(shí)現(xiàn)了run方法

public interface Tool {
  int run(InputStream in, OutputStream out, OutputStream err, String... arguments);
}

各個(gè)參數(shù)的意思分別是

  • in

java編譯器提供信息

  • out

用于獲取輸出信息

  • err

用于獲取錯(cuò)誤信息

  • arguments

編譯的文件(路徑)

前面三個(gè)參數(shù)如果,為null則會(huì)用默認(rèn)標(biāo)準(zhǔn)輸入輸出代替。網(wǎng)上到處都搜的到不做累述。

三、JavaCompiler V1.0

于是就有了第一種在線編譯運(yùn)行的實(shí)現(xiàn)思路,使用文件IO來(lái)動(dòng)態(tài)生成.java格式的文件與路徑,然后寫(xiě)入代碼內(nèi)容。

最初我便是打算姑且使用這種方式,由于數(shù)據(jù)封裝對(duì)象UserDto與Question都具有一個(gè)唯一的Id屬性,因此 xx.userId.questionId似乎挺適合用來(lái)做生成文件的類路徑的,類名就可以統(tǒng)一學(xué)習(xí)leetcode使用Solution ,于是一番努力后寫(xiě)出了我的Compilerv1.0

然而這種方式就給人感覺(jué)很low,“java動(dòng)態(tài)編譯”聽(tīng)起來(lái)還挺不錯(cuò)的,結(jié)果一細(xì)看,就這?

而且,這樣的實(shí)現(xiàn),每次前端給一個(gè)請(qǐng)求過(guò)來(lái)都要進(jìn)行文件讀寫(xiě)操作,如果之前沒(méi)有建立好相應(yīng)路徑與文件,還得重新新建,于是,當(dāng)用戶和題目多起來(lái)了以后那將是一個(gè)龐大的文件數(shù)量(最大值:用戶數(shù)X題目數(shù)X2),甚至并發(fā)量稍微有一點(diǎn)還不知道會(huì)出現(xiàn)什么問(wèn)題。

四、JavaCompiler V2.0

在線編譯最理想的情況是

前端表單傳給你需要編譯的java文件字符串內(nèi)容,然后將數(shù)據(jù)直接交給自定義編譯器,編譯器經(jīng)過(guò)編譯后返回Class對(duì)象,然后你再進(jìn)行相應(yīng)操作。

為了實(shí)現(xiàn)這個(gè)功能,除了JavaCompiler還需要去了解如下對(duì)象:

  • JavaFileObject(大概就是java文件的抽象)
  • JavaFileManager(大概就是Java文件管理操作的封裝)

相關(guān)內(nèi)容是從上面博客鏈接學(xué)會(huì)的,我自己再做了些改動(dòng):

1.需要自定義一個(gè)JavaFileObject重寫(xiě)一些方法

2.需要自定義一個(gè)JavaFileManager重寫(xiě)一些方法

大致原理就是,由于Java封裝的特性,只要類的行為正確,可以關(guān)心類的內(nèi)部細(xì)節(jié),所以,獲取.java文件內(nèi)容,最初是從文件中獲取,如果我們重寫(xiě)相應(yīng)方法,意味著我們可以將要編譯的String內(nèi)容,直接返回給相應(yīng)處理程序,只要調(diào)用相應(yīng)方法,返回的內(nèi)容正確,其實(shí)并不用關(guān)心,數(shù)據(jù)到底是從哪來(lái)的。
下面是我的

五、JavaFileObject實(shí)現(xiàn)

public class JavaFileObjectBean extends SimpleJavaFileObject {
    /**
     * Construct a SimpleJavaFileObject of the given kind and with the
     * given URI.
     *
     * @param uri  the URI for this file object
     * @param kind the kind of this file object
     */
    private String javaCode;
    private ByteArrayOutputStream outputStream;
    public JavaFileObjectBean(String className, String javaCode) {
        super(URI.create("string:///"+className.replace(".","/")+Kind.SOURCE.extension), Kind.SOURCE);
        // System.out.println("string:///" + className.replace(".", "/") + Kind.SOURCE.extension);
        this.javaCode=javaCode;
        //this.outputStream=new ByteArrayOutputStream();
    }

    protected JavaFileObjectBean(String className, Kind kind)  {
        super(URI.create("string:///"+className.replace(".","/")+kind.extension), kind);
//        System.out.println("!!");
        this.outputStream=new ByteArrayOutputStream();
    }


    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
        return this.javaCode;
    }

    @Override
    public OutputStream openOutputStream() throws IOException {
        return this.outputStream;
    }
    public byte[] getBytes(){
        return this.outputStream.toByteArray();
    }
}

繼承自SimpleJavaFileObject

public class SimpleJavaFileObject implements JavaFileObject 

新加了幾個(gè)屬性

private String javaCode;
    private ByteArrayOutputStream outputStream;
  • javaCode:用來(lái)保存需要編譯的Java文件內(nèi)容
  • outputStream:用來(lái)保存編譯后,Class對(duì)象的二進(jìn)制流

重寫(xiě)了兩個(gè)構(gòu)造器方法:

public JavaFileObjectBean(String className, String javaCode) {
        super(URI.create("string:///"+className.replace(".","/")+Kind.SOURCE.extension), Kind.SOURCE);
        // System.out.println("string:///" + className.replace(".", "/") + Kind.SOURCE.extension);
        this.javaCode=javaCode;
        //this.outputStream=new ByteArrayOutputStream();
    }

    protected JavaFileObjectBean(String className, Kind kind)  {
        super(URI.create("string:///"+className.replace(".","/")+kind.extension), kind);
//        System.out.println("!!");
        this.outputStream=new ByteArrayOutputStream();
    }

第一個(gè)構(gòu)造方法:用于自己創(chuàng)建對(duì)象時(shí)使用,調(diào)用父類構(gòu)造方法的同時(shí)初始化屬性:javaCode

完成初始化以后,相關(guān)對(duì)象會(huì)調(diào)用重寫(xiě)的方法

 @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
        return this.javaCode;
    }

然后進(jìn)行編譯。

第二個(gè)構(gòu)造方法是給相關(guān)API調(diào)用,然后會(huì)調(diào)用重寫(xiě)的方法

@Override
   public OutputStream openOutputStream() throws IOException {
       return this.outputStream;
   }

將編譯結(jié)果寫(xiě)入提供的IO流

重寫(xiě)好的JavaFileObject類配合自定義JavaFileManager使用

public class JavaFileManagerBean extends ForwardingJavaFileManager {
    private JavaFileObjectBean javaFileObjectBean;
    /**
     * Creates a new instance of ForwardingJavaFileManager.
     *
     * @param fileManager delegate to this file manager
     */
    protected JavaFileManagerBean(JavaFileManager fileManager) {
        super(fileManager);
        //this.javaFileObjectBean= new JavaFileObjectBean();
    }

    @Override
    public ClassLoader getClassLoader(Location location) {
        return new SecureClassLoader(){
            @Override
            protected Class<?> findClass(String name) throws ClassNotFoundException {
                byte[] bytes = javaFileObjectBean.getBytes();
                return super.defineClass(name,bytes,0,bytes.length);
            }
        };
    }

    @Override
    public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
        this.javaFileObjectBean = new JavaFileObjectBean(className,kind);
        return this.javaFileObjectBean;
    }
}

相關(guān)API會(huì)調(diào)用

public JavaFileObject getJavaFileForOutput

此時(shí),內(nèi)置IO流屬性會(huì)被初始化,然后寫(xiě)入編譯的Class對(duì)象的二進(jìn)制流信息,最后自定義一下類加載器的findClass方法,利用loadClass方法獲取編譯后得到的結(jié)果

cls=manager.getClassLoader(null).loadClass(className);

由于沒(méi)有實(shí)際文件,最后會(huì)由下面的代碼,尋找到需要加載到的類信息就會(huì)調(diào)用之前的重寫(xiě)的findClass方法得到Class 對(duì)象(defineClass方法用來(lái)將二進(jìn)制流信息還原為Class對(duì)象)

if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }

暫時(shí)就介紹這么多,剩下的內(nèi)容以后有時(shí)間再整理,如果又沒(méi)說(shuō)清楚的地方歡迎指正。

到此這篇關(guān)于教你怎么實(shí)現(xiàn)java語(yǔ)言的在線編譯的文章就介紹到這了,更多相關(guān)實(shí)現(xiàn)java語(yǔ)言的在線編譯內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot實(shí)現(xiàn)IP地址解析的示例代碼

    SpringBoot實(shí)現(xiàn)IP地址解析的示例代碼

    本篇帶大家實(shí)踐在springboot項(xiàng)目中獲取請(qǐng)求的ip與詳細(xì)地址,我們的很多網(wǎng)站app中都已經(jīng)新增了ip地址顯示,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01
  • mybatis if標(biāo)簽使用總結(jié)

    mybatis if標(biāo)簽使用總結(jié)

    這篇文章主要介紹了mybatis if標(biāo)簽使用總結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • Java工作隊(duì)列代碼詳解

    Java工作隊(duì)列代碼詳解

    這篇文章主要介紹了Java工作隊(duì)列代碼詳解,涉及Round-robin 轉(zhuǎn)發(fā),消息應(yīng)答(messageacknowledgments),消息持久化(Messagedurability)等相關(guān)內(nèi)容,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • SpringCache框架加載/攔截原理詳解

    SpringCache框架加載/攔截原理詳解

    這篇文章主要介紹了SpringCache框架加載/攔截原理詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • 詳解如何在低版本的Spring中快速實(shí)現(xiàn)類似自動(dòng)配置的功能

    詳解如何在低版本的Spring中快速實(shí)現(xiàn)類似自動(dòng)配置的功能

    這篇文章主要介紹了詳解如何在低版本的Spring中快速實(shí)現(xiàn)類似自動(dòng)配置的功能,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-05-05
  • Java以命令模式設(shè)計(jì)模式

    Java以命令模式設(shè)計(jì)模式

    這篇文章主要詳細(xì)的介紹Java以命令的模式設(shè)計(jì)模式,是用場(chǎng)景、優(yōu)缺點(diǎn)等都作有詳細(xì)介紹,需要的朋友請(qǐng)具體參考下面文章內(nèi)容
    2021-09-09
  • java實(shí)現(xiàn)時(shí)鐘效果

    java實(shí)現(xiàn)時(shí)鐘效果

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)時(shí)鐘效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-03-03
  • Spring Security 密碼驗(yàn)證動(dòng)態(tài)加鹽的驗(yàn)證處理方法

    Spring Security 密碼驗(yàn)證動(dòng)態(tài)加鹽的驗(yàn)證處理方法

    小編最近在改造項(xiàng)目,需要將gateway整合security在一起進(jìn)行認(rèn)證和鑒權(quán),今天小編給大家分享Spring Security 密碼驗(yàn)證動(dòng)態(tài)加鹽的驗(yàn)證處理方法,感興趣的朋友一起看看吧
    2021-06-06
  • Java多線程異步調(diào)用性能調(diào)優(yōu)方法詳解

    Java多線程異步調(diào)用性能調(diào)優(yōu)方法詳解

    這篇文章主要為大家詳細(xì)介紹了Java多線程異步調(diào)用性能調(diào)優(yōu),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-03-03
  • IDEA引MAVEN項(xiàng)目jar包依賴導(dǎo)入問(wèn)題解決方法

    IDEA引MAVEN項(xiàng)目jar包依賴導(dǎo)入問(wèn)題解決方法

    這篇文章主要介紹了IDEA引MAVEN項(xiàng)目jar包依賴導(dǎo)入問(wèn)題解決,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-11-11

最新評(píng)論