SpringBoot整合Groovy實現(xiàn)動態(tài)編程

Groovy簡介

Groovy 是增強 Java 平臺的唯一的腳本語言。它提供了類似于 Java 的語法,內(nèi)置映射(Map)、列表(List)、方法、類、閉包(closure)以及生成器。腳本語言不會替代系統(tǒng)編程語言,兩者是相互補充的。
大名鼎鼎的 Gradle,背后是 Groovy。Spring 的未來越來越多的使用 Groovy,甚至在用 Jira 跟蹤項目時,背后也有 Groovy。實際上,就應(yīng)用場景而言,Java 開發(fā)已經(jīng)有越來越多的 Groovy 出現(xiàn)在后臺了。而對于一般的應(yīng)用開發(fā),只要能用 Java 就都能用到 Groovy,唯一的難點只在于能不能招到足夠的人員。

應(yīng)用場景

  • 連接已有的組件
  • 處理經(jīng)常變化的多種類型的實體
  • 具有圖形化用戶界面
  • 擁有快速變化的功能

集成與使用

那么接下來介紹SpringBoot如何集成Groovy腳本,并應(yīng)用到實際開發(fā)中。

第一步、與SpringBoot集成

 <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-all</artifactId>
            <version>2.4.7</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

第二步、寫出Groovy版本的“Hello World”
1、HelloWorld.groovy腳本代碼

package groovy

def HelloWorld(){
    println "hello world"
}

2、創(chuàng)建測試類GroovyTest.java

package com.example.springbootgroovy.service;

import groovy.lang.GroovyShell;
import groovy.lang.Script;

/**
 * 這個是Groovy的第一個小程序,腳本為:
 * 
 package groovy
 
 def helloworld(){
  println "hello world"
 }
 *
 */
public class GroovyTest {

    public static void main(String[] args) throws Exception {
        //創(chuàng)建GroovyShell
        GroovyShell groovyShell = new GroovyShell();
        //裝載解析腳本代碼
        Script script = groovyShell.parse("package groovy\n" +
                "\n" +
                "def HelloWorld(){\n" +
                "    println \"hello world\"\n" +
                "}");
        //執(zhí)行
        script.invokeMethod("HelloWorld", null);
    }
}

第三步、傳入變量與獲取返回值
1、變量與返回值Groovy腳本代碼

package groovy

/**
 * 簡易加法
 * @param a 數(shù)字a
 * @param b 數(shù)字b
 * @return 和
 */
def add(int a, int b) {
    return a + b
}

/**
 * map轉(zhuǎn)化為String
 * @param paramMap 參數(shù)map
 * @return 字符串
 */
def mapToString(Map<String, String> paramMap) {
    StringBuilder stringBuilder = new StringBuilder();
    paramMap.forEach({ key, value ->
        stringBuilder.append("key:" + key + ";value:" + value)
    })
    return stringBuilder.toString()
}

2、創(chuàng)建測試類GroovyTest2.java

package com.example.springbootgroovy.service;

import groovy.lang.GroovyShell;
import groovy.lang.Script;

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

/**
 * 向Groovy腳本中傳入變量,以及獲取返回值
 */
public class GroovyTest2 {
    public static void main(String[] args) {
        //創(chuàng)建GroovyShell
        GroovyShell groovyShell = new GroovyShell();
        //裝載解析腳本代碼
        Script script = groovyShell.parse("package groovy\n" +
                "\n" +
                "/**\n" +
                " * 簡易加法\n" +
                " * @param a 數(shù)字a\n" +
                " * @param b 數(shù)字b\n" +
                " * @return 和\n" +
                " */\n" +
                "def add(int a, int b) {\n" +
                "    return a + b\n" +
                "}\n" +
                "\n" +
                "/**\n" +
                " * map轉(zhuǎn)化為String\n" +
                " * @param paramMap 參數(shù)map\n" +
                " * @return 字符串\n" +
                " */\n" +
                "def mapToString(Map<String, String> paramMap) {\n" +
                "    StringBuilder stringBuilder = new StringBuilder();\n" +
                "    paramMap.forEach({ key, value ->\n" +
                "        stringBuilder.append(\"key:\" + key + \";value:\" + value)\n" +
                "    })\n" +
                "    return stringBuilder.toString()\n" +
                "}");
        //執(zhí)行加法腳本
        Object[] params1 = new Object[]{1, 2};
        int sum = (int) script.invokeMethod("add", params1);
        System.out.println("a加b的和為:" + sum);
        //執(zhí)行解析腳本
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("科目1", "語文");
        paramMap.put("科目2", "數(shù)學(xué)");
        Object[] params2 = new Object[]{paramMap};
        String result = (String) script.invokeMethod("mapToString", params2);
        System.out.println("mapToString:" + result);
    }
}

第四步、啟動SpringBoot,在Groovy腳本中通過SpringContextUtil獲取SpringBoot容器中的Bean
1、創(chuàng)建SpringContextUtil.java

package com.example.springbootgroovy.util;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * Spring上下文獲取
 */
@Component
public class SpringContextUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContextUtil.applicationContext = applicationContext;
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * 通過name獲取 Bean.
     *
     * @param name
     * @return
     */
    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
    }

    /**
     * 通過class獲取Bean.
     *
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class<T> clazz) {
        return getApplicationContext().getBean(clazz);
    }

    /**
     * 通過name,以及Clazz返回指定的Bean
     *
     * @param name
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(String name, Class<T> clazz) {
        return getApplicationContext().getBean(name, clazz);
    }
}

2、創(chuàng)建GroovyTestService.java,并加上@Service注解加入到SpringBoot容器中

package com.example.springbootgroovy.service;

import org.springframework.stereotype.Service;

@Service
public class GroovyTestService {

    public void test(){
        System.out.println("我是SpringBoot框架的成員類,但該方法由Groovy腳本調(diào)用");
    }

}

3、Groovy腳本如下

package groovy

import com.example.springbootgroovy.service.GroovyTestService
import com.example.springbootgroovy.util.SpringContextUtil

/**
 * 靜態(tài)變量
 */
class Globals {
    static String PARAM1 = "靜態(tài)變量"
    static int[] arrayList = [1, 2]
}

def getBean() {
    GroovyTestService groovyTestService = SpringContextUtil.getBean(GroovyTestService.class);
    groovyTestService.test()
}

4、啟動類代碼如下

package com.example.springbootgroovy;

import groovy.lang.GroovyShell;
import groovy.lang.Script;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/groovy")
@SpringBootApplication
public class SpringBootGroovyApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootGroovyApplication.class, args);
    }

    @RequestMapping("/test")
    public String test() {
        //創(chuàng)建GroovyShell
        GroovyShell groovyShell = new GroovyShell();
        //裝載解析腳本代碼
        Script script = groovyShell.parse("package groovy\n" +
                "\n" +
                "import com.example.springbootgroovy.service.GroovyTestService\n" +
                "import com.example.springbootgroovy.util.SpringContextUtil\n" +
                "\n" +
                "/**\n" +
                " * 靜態(tài)變量\n" +
                " */\n" +
                "class Globals {\n" +
                "    static String PARAM1 = \"靜態(tài)變量\"\n" +
                "    static int[] arrayList = [1, 2]\n" +
                "}\n" +
                "\n" +
                "def getBean() {\n" +
                "    GroovyTestService groovyTestService = SpringContextUtil.getBean(GroovyTestService.class);\n" +
                "    groovyTestService.test()\n" +
                "}");
        //執(zhí)行
        script.invokeMethod("getBean", null);
        return "ok";
    }
}

5、啟動后調(diào)用接口:http://localhost:8080/groovy/test,查看運行結(jié)果。

通過第四步中我們可以看到,在Groovy中是可以獲取到SpringBoot容器對象的。雖然很方便,但是很危險。如果沒有做好權(quán)限控制,Groovy腳本將會成為攻擊你系統(tǒng)最有力的武器!??!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容