java动态执行逻辑_动态执行代码逻辑
動態執行邏輯的方法據我所知有一下兩種方式
QLExpress
Groovy
QLExpress
QLExpress是阿里開源的動態腳本執行的項目。 由阿里的電商業務規則、表達式(布爾組合)、特殊數學公式計算(高精度)、語法分析、腳本二次定制等強需求而設計的一門動態腳本引擎解析工具。 在阿里集團有很強的影響力,同時為了自身不斷優化、發揚開源貢獻精神,于2012年開源。
這種方案在配置上感覺不太方便,原因是沒有IDE支持、某些JAVA語法不支持。。。
Groovy
來著百度百科
Groovy 是 用于Java虛擬機的一種敏捷的動態語言,它是一種成熟的面向對象編程語言,既可以用于面向對象編程,又可以用作純粹的腳本語言。使用該種語言不必編寫過多的代碼,同時又具有閉包和動態語言中的其他特性。
Groovy是JVM的一個替代語言(替代是指可以用 Groovy 在Java平臺上進行 Java 編程),使用方式基本與使用 Java代碼的方式相同,該語言特別適合與Spring的動態語言支持一起使用,設計時充分考慮了Java集成,這使 Groovy 與 Java 代碼的互操作很容易。(注意:不是指Groovy替代java,而是指Groovy和java很好的結合編程。
原理
通過Groovy提供的GroovyClassLoader把源代碼動態加載編譯成Class,Class再實例化成對象
動手實現
依賴
org.codehaus.groovy
groovy
3.0.0-rc-1
cn.hutool
hutool-all
5.0.3
創建動態腳本工廠,inject方法用于擴展。
package cn.dhbin.dynamic;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import groovy.lang.GroovyClassLoader;
import java.util.concurrent.ConcurrentHashMap;
/**
* 動態腳本工廠
* 作用:
* 通過字符串源碼生成Class
* Class -> 實例
*
* @author donghaibin
* @date 2019/11/19
*/
public class DynamicFactory {
/**
* 單例
*/
private static DynamicFactory dynamicFactory = new DynamicFactory();
/**
* groovy類加載器
*/
private GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
/**
* 緩存Class
*/
private ConcurrentHashMap> classCache = new ConcurrentHashMap<>();
/**
* 獲取單例
*
* @return 實例
*/
public static DynamicFactory getInstance() {
return dynamicFactory;
}
/**
* 加載創建實例,prototype
*
* @param codeSource 源代碼
* @return 實例
* @throws Exception 異常
*/
public IScript loadNewInstance(String codeSource) throws Exception {
if (StrUtil.isNotBlank(codeSource)) {
Class> aClass = getCodeSourceClass(codeSource);
if (aClass != null) {
Object instance = aClass.newInstance();
if (instance != null) {
if (instance instanceof IScript) {
this.inject((IScript) instance);
return (IScript) instance;
} else {
throw new IllegalArgumentException(StrUtil.format("創建實例失敗,[{}]不是IScript的子類", instance.getClass()));
}
}
}
}
throw new IllegalArgumentException("創建實例失敗,instance is null");
}
/**
* code text -> class
* 通過類加載器生成class
*
* @param codeSource 源代碼
* @return class
*/
private Class> getCodeSourceClass(String codeSource) {
String md5 = SecureUtil.md5(codeSource);
Class> aClass = classCache.get(md5);
if (aClass == null) {
aClass = groovyClassLoader.parseClass(codeSource);
classCache.putIfAbsent(md5, aClass);
}
return aClass;
}
/**
* 對script對象處理
*
* @param script {@link IScript}
*/
public void inject(IScript script) {
// to do something
}
}
定義腳本模板
package cn.dhbin.dynamic;
/**
* 腳本接口,所有腳本實現該接口的{@link IScript#run(String)}方法
*
* @author donghaibin
* @date 2019/11/19
*/
public interface IScript {
/**
* 具體邏輯
*
* @param param 參數
* @return 執行結果
*/
String run(String param);
}
腳本執行器
package cn.dhbin.dynamic;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author donghaibin
* @date 2019/11/19
*/
public class ScriptExecutor {
/**
* 緩存實例
*/
private ConcurrentHashMap objCache = new ConcurrentHashMap<>();
/**
* 執行腳本
*
* @param id 實例Id
* @return 運行結果
*/
public String run(String id, String param) {
IScript script = objCache.get(id);
if (script == null) {
throw new IllegalArgumentException("未找到實例, id = [" + id + "]");
} else {
return script.run(param);
}
}
/**
* 注冊實例
*
* @param id 實例id
* @param script 實例
* @return 返回前一個實例,如果為null,則是新插入
*/
public IScript register(String id, IScript script) {
return objCache.put(id, script);
}
/**
* 移除實例
*
* @param id 實例id
* @return 移除的實例
*/
public IScript remove(String id) {
return objCache.remove(id);
}
}
到這里,就基本實現了腳本的加載-實例化-執行。下面測試
編寫腳本
package cn.dhbin.dynamic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author donghaibin
* @date 2019/11/19
*/
public class SimpleScript implements IScript{
private static final Logger log = LoggerFactory.getLogger(SimpleScript.class);
@Override
public String run(String param) {
log.info("輸入的參數是:[{}]", param);
log.info("你好世界");
return "hello world";
}
}
測試用例
package com.pig4cloud.pig.sms.dynamic;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
/**
* @author donghaibin
* @date 2019/11/19
*/
@Slf4j
class DynamicFactoryTest {
@Test
void runWithExecutor() throws Exception {
DynamicFactory dynamicFactory = DynamicFactory.getInstance();
ScriptExecutor executor = new ScriptExecutor();
String codeSource = "package cn.dhbin.dynamic;\n" +
"\n" +
"import org.slf4j.Logger;\n" +
"import org.slf4j.LoggerFactory;\n" +
"\n" +
"/**\n" +
" * @author donghaibin\n" +
" * @date 2019/11/19\n" +
" */\n" +
"public class SimpleScript implements IScript{\n" +
"\n" +
"\tprivate static final Logger log = LoggerFactory.getLogger(SimpleScript.class);\n" +
"\n" +
"\t@Override\n" +
"\tpublic String run(String param) {\n" +
"\t\tlog.info(\"輸入的參數是:[{}]\", param);\n" +
"\t\tlog.info(\"你好世界\");\n" +
"\t\treturn \"hello world\";\n" +
"\t}\n" +
"\n" +
"}\n";
IScript script = dynamicFactory.loadNewInstance(codeSource);
String id = "1";
executor.register(id, script);
for (int i = 0; i < 10; i++) {
String result = executor.run(id, "abc");
log.info("結果:[{}]", result);
}
}
@Test
void runWithoutExecutor() throws Exception{
DynamicFactory dynamicFactory = DynamicFactory.getInstance();
String codeSource = "package cn.dhbin.dynamic;\n" +
"\n" +
"import org.slf4j.Logger;\n" +
"import org.slf4j.LoggerFactory;\n" +
"\n" +
"/**\n" +
" * @author donghaibin\n" +
" * @date 2019/11/19\n" +
" */\n" +
"public class SimpleScript implements IScript{\n" +
"\n" +
"\tprivate static final Logger log = LoggerFactory.getLogger(SimpleScript.class);\n" +
"\n" +
"\t@Override\n" +
"\tpublic String run(String param) {\n" +
"\t\tlog.info(\"輸入的參數是:[{}]\", param);\n" +
"\t\tlog.info(\"你好世界\");\n" +
"\t\treturn \"hello world\";\n" +
"\t}\n" +
"\n" +
"}\n";
for (int i = 0; i < 10; i++) {
IScript script = dynamicFactory.loadNewInstance(codeSource);
String result = script.run("abc");
log.info("結果:[{}]", result);
}
}
}
執行結果
11:19:32.243 [main] INFO cn.dhbin.dynamic.SimpleScript - 輸入的參數是:[abc]
11:19:32.255 [main] INFO cn.dhbin.dynamic.SimpleScript - 你好世界
11:19:32.255 [main] INFO cn.dhbin.dynamic.DynamicFactoryTest - 結果:[hello world]
11:19:32.255 [main] INFO cn.dhbin.dynamic.SimpleScript - 輸入的參數是:[abc]
11:19:32.255 [main] INFO cn.dhbin.dynamic.SimpleScript - 你好世界
11:19:32.255 [main] INFO cn.dhbin.dynamic.DynamicFactoryTest - 結果:[hello world]
11:19:32.255 [main] INFO cn.dhbin.dynamic.SimpleScript - 輸入的參數是:[abc]
11:19:32.255 [main] INFO cn.dhbin.dynamic.SimpleScript - 你好世界
11:19:32.255 [main] INFO cn.dhbin.dynamic.DynamicFactoryTest - 結果:[hello world]
11:19:32.255 [main] INFO cn.dhbin.dynamic.SimpleScript - 輸入的參數是:[abc]
11:19:32.255 [main] INFO cn.dhbin.dynamic.SimpleScript - 你好世界
11:19:32.255 [main] INFO cn.dhbin.dynamic.DynamicFactoryTest - 結果:[hello world]
11:19:32.255 [main] INFO cn.dhbin.dynamic.SimpleScript - 輸入的參數是:[abc]
11:19:32.255 [main] INFO cn.dhbin.dynamic.SimpleScript - 你好世界
11:19:32.255 [main] INFO cn.dhbin.dynamic.DynamicFactoryTest - 結果:[hello world]
11:19:32.255 [main] INFO cn.dhbin.dynamic.SimpleScript - 輸入的參數是:[abc]
11:19:32.255 [main] INFO cn.dhbin.dynamic.SimpleScript - 你好世界
11:19:32.255 [main] INFO cn.dhbin.dynamic.DynamicFactoryTest - 結果:[hello world]
11:19:32.255 [main] INFO cn.dhbin.dynamic.SimpleScript - 輸入的參數是:[abc]
11:19:32.256 [main] INFO cn.dhbin.dynamic.SimpleScript - 你好世界
11:19:32.256 [main] INFO cn.dhbin.dynamic.DynamicFactoryTest - 結果:[hello world]
11:19:32.256 [main] INFO cn.dhbin.dynamic.SimpleScript - 輸入的參數是:[abc]
11:19:32.256 [main] INFO cn.dhbin.dynamic.SimpleScript - 你好世界
11:19:32.256 [main] INFO cn.dhbin.dynamic.DynamicFactoryTest - 結果:[hello world]
11:19:32.256 [main] INFO cn.dhbin.dynamic.SimpleScript - 輸入的參數是:[abc]
11:19:32.256 [main] INFO cn.dhbin.dynamic.SimpleScript - 你好世界
11:19:32.256 [main] INFO cn.dhbin.dynamic.DynamicFactoryTest - 結果:[hello world]
11:19:32.256 [main] INFO cn.dhbin.dynamic.SimpleScript - 輸入的參數是:[abc]
11:19:32.256 [main] INFO cn.dhbin.dynamic.SimpleScript - 你好世界
11:19:32.256 [main] INFO cn.dhbin.dynamic.DynamicFactoryTest - 結果:[hello world]
兩個用例執行的結果都一樣,區別就是一個使用了執行器。這樣做的目的是提高運行效率,執行器緩存了實例對象,不用每次執行都實例化。
總結
Groovy這種方案其實是從xxl-job這個定時任務項目中提取出來的。它還擴展了Spring的幾個注解,能從Spring的容器中加載Bean并使用。項目鏈接: https://gitee.com/xuxueli0323...
思考
通過groovy動態加載Class,再結合Spring的生命周期,是否可以實現動態添加Bean?是否可以實現動態添加Controller?
總結
以上是生活随笔為你收集整理的java动态执行逻辑_动态执行代码逻辑的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java用流体加减乘除_任意输入两个数,
- 下一篇: core identity mysql_