Groovy — единственный язык сценариев, расширяющий возможности платформы Java. Он предоставляет Java-подобный синтаксис со встроенными картами, списками, методами, классами, замыканиями и генераторами. Языки сценариев не заменяют языки системного программирования, они дополняют друг друга.
За Groovy стоит знаменитый Gradle. В будущем Spring будет все чаще использовать Groovy, и даже при отслеживании проектов с помощью Jira за этим стоит Groovy.
на самом деле,Сразу Сценарии применения С точки зрения,Java Произошло все больше событий Groovy Появился на заднем плане. Для общей разработки приложений, если их можно использовать. Java Его можно использовать Круто, единственная трудность заключается в том, сможет ли он набрать достаточное количество людей.
" Примечание. Сегодня мы рассказываем об использовании сценариев Groovy для реализации динамического программирования в проектах SpringBoot, что делает бизнес-логику динамичной и значительно повышает эффективность и гибкость разработки.
Затем давайте представим, как SpringBoot интегрирует сценарии Groovy и применяет их в реальной разработке.
1. Файл pom.xml выглядит следующим образом:
<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>
1. Код сценария HelloWorld.groovy
package groovy
def HelloWorld(){
println "hello world"
}
2. Создайте тестовый класс 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 {
//Создаем GroovyShell
GroovyShell groovyShell = new GroovyShell();
//Загружаем код разбора Скрипта
Script script = groovyShell.parse("package groovy\n" +
"\n" +
"def HelloWorld(){\n" +
" println \"hello world\"\n" +
"}");
//осуществлять
script.invokeMethod("HelloWorld", null);
}
}
3. Результаты операции
картина
1. Переменные и возвращаемые значения Код скрипта Groovy
package groovy
/**
* Простое дополнение
* @param a Номер а
* @param b Номер б
* @return и
*/
def add(int a, int b) {
return a + b
}
/**
* карта преобразована в строку
* @param paramMap Карта параметров
* @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. Создайте тестовый класс 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) {
//Создаем GroovyShell
GroovyShell groovyShell = new GroovyShell();
//Загружаем код разбора Скрипта
Script script = groovyShell.parse("package groovy\n" +
"\n" +
"/**\n" +
" * Простое дополнение\n" +
" * @param a Номер а\n" +
" * @param b Номер б\n" +
" * @return и\n" +
" */\n" +
"def add(int a, int b) {\n" +
" return a + b\n" +
"}\n" +
"\n" +
"/**\n" +
" * карта преобразована в строку\n" +
" * @param paramMap Карта параметров\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" +
"}");
//осуществлятьдобавление Скрипт
Object[] params1 = new Object[]{1, 2};
int sum = (int) script.invokeMethod("add", params1);
System.out.println("a плюс b равно:" + sum);
//осуществлятьанализировать Скрипт
Map<String, String> paramMap = new HashMap<>();
paramMap.put("Субъект 1", "язык");
paramMap.put("Субъект 2", «математика»);
Object[] params2 = new Object[]{paramMap};
String result = (String) script.invokeMethod("mapToString", params2);
System.out.println("mapToString:" + result);
}
}
3. Результаты операции
картина
Получите bean-компоненты в контейнере SpringBoot через SpringContextUtil в сценарии Groovy.
1. Создайте 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;
}
/**
* Получить по имени Bean.
*
* @param name
* @return
*/
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
/**
* Проведите Бин через класс.
*
* @param clazz
* @param <T>
* @return
*/
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
/**
* Вернуть указанный Bean через имя и Clazz
*
* @param name
* @param clazz
* @param <T>
* @return
*/
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
}
2. Создайте 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Скрипт");
}
}
3. Сценарий Groovy выглядит следующим образом.
package groovy
import com.example.springbootgroovy.service.GroovyTestService
import com.example.springbootgroovy.util.SpringContextUtil
/**
* статическийпеременная */
class Globals {
static String PARAM1 = "статическийпеременная" 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() {
//Создаем 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" +
" * статическийпеременная\n" +
" */\n" +
"class Globals {\n" +
" static String PARAM1 = \"статическийпеременная\"\n" +
" static int[] arrayList = [1, 2]\n" +
"}\n" +
"\n" +
"def getBean() {\n" +
" GroovyTestService groovyTestService = SpringContextUtil.getBean(GroovyTestService.class);\n" +
" groovyTestService.test()\n" +
"}");
//осуществлять
script.invokeMethod("getBean", null);
return "ok";
}
}
5、Интерфейс вызова после запуска:http://localhost:8080/groovy/test
,Результаты бега следующие
картина
Уведомление! ! !
" На четвертом шаге мы видим, что объект-контейнер SpringBoot можно получить в Groovy. Хоть это и удобно, но и опасно. Если контроль разрешений не выполнен должным образом, сценарии Groovy станут самым мощным оружием против вашей системы! ! !
Кроме того, если сценарий Groovy используется неправильно, это приведет к OOM и, в конечном итоге, к сбою сервера.
" Мое первоначальное использование
public static List<JSONObject> invokeMethod(String templateScript, JSONObject configParam) {
Binding groovyBinding = new Binding();
GroovyShell groovyShell = new GroovyShell(groovyBinding);
Script script = groovyShell.parse(templateScript);
Object[] params = new Object[]{configParam};
List<JSONObject> resultList = (List<JSONObject>) script.invokeMethod("methodName", params);
return resultList;
}
Такое использование определенно неправильно. Оно эквивалентно созданию экземпляров GroovyShell, Script и т. д. при каждом вызове этого метода. По мере увеличения количества вызовов неизбежно возникает OOM.
" Для первой модификации добавьте строку в конец метода: groovyShell.getClassLoader().clearCache();
То есть вызов методаclearCache в конце метода может очистить GroovyShell, Script и другие экземпляры, но этого все равно недостаточно. Причиной OOM является не просто слишком большое количество экземпляров GroovyShell, Script и т. д. Изучив информацию, мы обнаружили, что если код Java в скрипте также создает объекты или новые экземпляры, даже если GroovyShell уничтожен, объекты в скрипте не будет уничтожен.
Например, следующий скрипт создаст объект ArrayList. Этот объект не исчезнет при исчезновении экземпляров GroovyShell, Script и т. д., поэтому проблемы все равно будут.
def test(){
List<String> list = new ArrayList<>();
}
" Во втором преобразовании был добавлен SCRIPT_MAP, а существующие экземпляры Groovy были помещены в кэш для обслуживания.
/**
* Кэшируйте сценарии, чтобы избежать создания слишком большого количества
*/
private static final Map<String, Script> SCRIPT_MAP = Maps.newHashMap();
private static final GroovyClassLoader CLASS_LOADER = new GroovyClassLoader();
public static Script loadScript(String key, String rule) {
if (SCRIPT_MAP.containsKey(key)) {
return SCRIPT_MAP.get(key);
}
Script script = loadScript(rule, new Binding());
SCRIPT_MAP.put(key, script);
return script;
}
public static Script loadScript(String rule, Binding binding) {
if (StringUtils.isEmpty(rule)) {
return null;
}
try {
Class ruleClazz = CLASS_LOADER.parseClass(rule);
if (ruleClazz != null) {
log.info("load rule:" + rule + " success!");
return InvokerHelper.createScript(ruleClazz, binding);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
CLASS_LOADER.clearCache();
}
return null;
}
Преимущество этого метода в том, что он решает проблему ООМ.,Но есть и проблема,Если содержимое Скрипта изменено,Необходимо очиститьSCRIPT_MAP
,Перезагрузите экземпляр скрипта.
<END>