Java動態指令碼Groovy,高階啊!

南國以南i發表於2021-12-14

前言:請各大網友尊重本人原創知識分享,謹記本人部落格:南國以南i

簡介:

Groovy是用於Java虛擬機器的一種敏捷的動態語言,它是一種成熟的物件導向程式語言,既可以用於物件導向程式設計,又可以用作純粹的指令碼語言。使用該種語言不必編寫過多的程式碼,同時又具有閉包和動態語言中的其他特性。

Groovy特性:

可將java程式碼在Groovy指令碼動態編碼、程式碼被修改達到不重啟服務的目的(類似於熱部署)

核心涉及:

ClassLoader:就是類的裝載器,它使JVM可以動態的載入Java類,JVM並不需要知道從什麼地方(本地檔案、網路等)載入Java類,這些都由ClassLoader完成。

GroovyClassLoader:動態地載入一個指令碼並執行它的行為。GroovyClassLoader是一個定製的類裝載器,負責解釋載入Java類中用到的Groovy類。

Java與Groovy轉換

第一步:引入Groovy依賴

    <!--Groovy指令碼依賴-->
        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy</artifactId>
            <version>2.5.14</version>
        </dependency>

第二步:建立interface介面宣告方法

public interface CallAnalysis {
     default void load() {
    }
}

第三步:在resources目錄下建立.groovy檔案

package groovy

import com.example.groovy.testgroovy.task.CallAnalysis
import groovy.util.logging.Slf4j

@Slf4j
class CallAnalysisImpl implements CallAnalysis{

    @Override
    void load() {
        log.info("我被Groovy指令碼載入...")
    }
}

第四步:建立Groovy指令碼裝載類,動態解析指令碼為Class

package com.example.groovy.testgroovy.task;

import groovy.lang.GroovyClassLoader;

public class GroovyUtils {

    private final static ClassLoader classLoader = GroovyUtils.class.getClassLoader();//獲取當前類裝載器
    //ClassLoader:就是類的裝載器,它使JVM可以動態的載入Java類,JVM並不需要知道從什麼地方(本地檔案、網路等)載入Java類,這些都由ClassLoader完成。

    public final static GroovyClassLoader groovyClassLoader = new GroovyClassLoader(classLoader);
    //GroovyClassLoader:負責在執行時編譯groovy原始碼為Class的工作,從而使Groovy實現了將groovy原始碼動態載入為Class的功能。

    /**
     * .
     * 獲取例項化物件
     * @param script groovy指令碼內容
     * @param <T>
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public static <T> T instanceTaskGroovyScript(String script) throws IllegalAccessException, InstantiationException {
        Class taskClz = groovyClassLoader.parseClass(script);
        T instance = (T) taskClz.newInstance();
        return instance;
    }
}

第五步:讀取指令碼內容,執行指令碼

package com.example.groovy.testgroovy.task;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;


@Slf4j
@Component
public class CallAnalysisGroovyTask {


    /**
     * .
     * 讀取指令碼內容
     *
     * @return
     */
    public static String getGroovy() {
        String context = "";
        try {
            String path = "E:\\IDEAFile\\testgroovy\\src\\main\\resources\\groovy\\CallAnalysisImpl.groovy";
            context = FileUtils.readFileToString(new File(path));//將指令碼內容轉為字串
        } catch (IOException e) {
            log.error("file is not found[{}]", e);
        }
        return context;
    }

    /**
     * .
     * 執行groovy指令碼
     *
     * @param script
     */
    public static void execGroovy(String script) {
        try {
            CallAnalysis objClass = GroovyUtils.instanceTaskGroovyScript(script);//獲取例項物件
            objClass.load();//呼叫指令碼方法
        } catch (Exception t) {
            log.error("execGroovy file {} error", script);
        }
    }

    /**
     * .
     * main方法
     * @param args
     */
    public static void main(String[] args) {
        System.out.println("==================");
        CallAnalysisGroovyTask task = new CallAnalysisGroovyTask();
        String script = task.getGroovy();//獲取指令碼
        execGroovy(script);//例項化指令碼,執行方法
        System.out.println("==================");
    }
}

Groovy特性驗證

利用Groovy指令碼特性,不重啟服務,實時修改資料

第一步:將之前Groovy指令碼資料修改。存於資料庫表中,動態載入指令碼

@Slf4j
class CallAnalysisImpl implements CallAnalysis {

    private int anInt = 10;
    private int bnInt = 10;

    @Override
    void load() {
        log.info("當前類:[{}]", this.getClass().getName())
        log.info("我被Groovy指令碼載入...")
        log.info("計算結果:[{}]", (anInt + bnInt))
    }
}

第二步:資料庫表中:新增、查詢Groovy指令碼,動態載入執行

 /**
     * .
     * 讀取指令碼,進行入庫操作
     *
     * @return
     */
    @GetMapping("/saveScript")
    public String saveScript() {
        String scriptStr = callAnalysisGroovyTask.getGroovy();
        Script script = new Script();//實體類物件
        script.setScript(scriptStr);//指令碼內容
        script.setRuleId("1");//規則id
        script.setScriptName("演示一");//指令碼名稱
        service.save(script);
        return "新增成功";
    }


    /**
     * .
     * 從資料庫表中,動態獲取指令碼
     *
     * @param ruleId 規則id
     * @return 指令碼內容
     */
    @GetMapping("/groovy")
    public String groovy(final String ruleId) {
        Script scr = scriptService.findScriptByRuleId(ruleId);//根據規則id查詢
        String scriptStr = scr.getScript();
        callAnalysisGroovyTask.execGroovy(scriptStr);
        return scriptStr;
    }

新增結果

 查詢結果、控制檯執行結果

第三步:多次修改表資料值,檢視執行結果

總語:

目的達成,可見在不重啟服務時,多次修改資料,指令碼內容都會被動態載入。此處只是簡單舉例驗證,可自行擴充套件

相關文章