Groovy 是增强 Java 平台的唯一的脚本语言。它提供了类似于 Java 的语法,内置映射(Map)、列表(List)、方法、类、闭包(closure)以及生成器。脚本语言不会替代系统编程语言,两者是相互补充的。
大名鼎鼎的 Gradle,背后是 Groovy。Spring 的未来越来越多的使用 Groovy,甚至在用 Jira 跟踪项目时,背后也有 Groovy。实际上,就应用场景而言,Java 开发已经有越来越多的 Groovy 出现在后台了。而对于一般的应用开发,只要能用 Java 就都能用到 Groovy,唯一的难点只在于能不能招到足够的人员。
Groovy脚本的基础概念请移步
Groovy 简介
那么接下来介绍SpringBoot如何集成Groovy脚本,并应用到实际开发中。
<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>
package groovy def HelloWorld(){ println "hello world" }
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); } }
package groovy /** * 简易加法 * @param a 数字a * @param b 数字b * @return 和 */ def add(int a, int b) { return a + b } /** * map转化为String * @param paramMap 参数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() }
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 数字a\n" + " * @param b 数字b\n" + " * @return 和\n" + " */\n" + "def add(int a, int b) {\n" + " return a + b\n" + "}\n" + "\n" + "/**\n" + " * map转化为String\n" + " * @param paramMap 参数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" + "}"); //执行加法脚本 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); } }
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); } }
package com.example.springbootgroovy.service; import org.springframework.stereotype.Service; @Service public class GroovyTestService { public void test(){ System.out.println("我是SpringBoot框架的成员类,但该方法由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() }
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"; } }
通过第四步中我们可以看到,在Groovy中是可以获取到SpringBoot容器对象的。虽然很方便,但是很危险。如果没有做好权限控制,Groovy脚本将会成为攻击你系统最有力的武器!!!