1.什么是groovy?
Groovy 是构建在 JVM 上的一个轻量级却强大的动态语言,它结合了 Python、Ruby 和 Smalltalk 的许多强大的特性。 Groovy 就是用 Java 写的,Groovy 语法与 Java 语法类似,Groovy 代码能够与 Java 代码很好地结合,也能用于扩展现有代码。相对于 Java,它在编写代码的灵活性上有非常明显的提升,Groovy 可以使用其他 Java 语言编写的库。 Groovy 是增强 Java 平台的唯一的脚本语言。它提供了类似于 Java 的语法,内置映射(Map)、列表(List)、方法、类、闭包(closure)以及生成器。脚本语言不会替代系统编程语言,两者是相互补充的。
系统编程语言的目的:
- 开发复杂的算法或者数据结构
- 实现计算密集型应用
- 操作大型数据集
- 实现定义良好的、变更缓慢的需求
脚本语言应用的目的:
- 连接已有的组件
- 处理经常变化的多种类型的实体
- 具有图形化用户界面
- 拥有快速变化的功能
集成groovy的好处:
- groovy跟java都是基于jvm的语言,可以在java项目中集成groovy并充分利用groovy的动态功能;
- groovy兼容几乎所有的java语法,开发者完全可以将groovy当做java来开发,甚至可以不使用groovy的特有语法,仅仅通过引入groovy并使用它的动态能力;
- groovy可以直接调用项目中现有的java类(通过import导入),通过构造函数构造对象并直接调用其方法并返回结果;
- groovy支持通过GroovyShell预设对象,在groovy动态脚本中直接调用预设对象的方法。因此我们可以通过将spring的bean预设到GroovyShell运行环境中,在groovy动态脚本中直接调用spring容器中bean来调用其方法,这点对于spring项目非常方便!
2.代码工程
实验目的
如何通过动态groovy脚本直接调用spring context中注册的bean的方法
pom.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springboot-demo</artifactId>
<groupId>com.et</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>groovy</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.codehaus.groovy/groovy-all -->
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.7</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
</dependencies>
</project>
conotroller
将binding对象注入后,在初始化方法init()中用binding对象构造GroovyShell对象,在提供的execute接口实现中用GroovyShell对象生成Script脚本对象,并调用Script的run()方法运行动态脚本并返回结果。
kotlin
package com.et.groovy.controller;
import com.et.groovy.component.TestScript;
import groovy.lang.Binding;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyShell;
import groovy.lang.Script;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
@RestController
@RequestMapping("/groovy/script")
public class GroovyScriptController {
@Autowired
private Binding groovyBinding;
private GroovyShell groovyShell;
@PostConstruct
public void init(){
GroovyClassLoader groovyClassLoader = new GroovyClassLoader(this.getClass().getClassLoader());
CompilerConfiguration compilerConfiguration = new CompilerConfiguration();
compilerConfiguration.setSourceEncoding("utf-8");
compilerConfiguration.setScriptBaseClass(TestScript.class.getName());
groovyShell = new GroovyShell(groovyClassLoader, groovyBinding, compilerConfiguration);
}
@RequestMapping(value = "/execute", method = RequestMethod.POST)
public String execute(@RequestBody String scriptContent) {
Script script = groovyShell.parse(scriptContent);
return String.valueOf(script.run());
}
}
service
ini
package com.et.groovy.service;
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import groovy.lang.Script;
import org.springframework.stereotype.Service;
@Service
public class TestService {
public String testQuery(long id){
return "Test query success, id is " + id;
}
public static void main(String[] args) {
Binding groovyBinding = new Binding();
groovyBinding.setVariable("testService", new TestService());
GroovyShell groovyShell = new GroovyShell(groovyBinding);
String scriptContent = "import pers.doublebin.example.groovy.script.service.TestService\n" +
"def query = new TestService().testQuery(1L);\n" +
"query";
/*String scriptContent = "def query = testService.testQuery(2L);\n" +
"query";*/
Script script = groovyShell.parse(scriptContent);
System.out.println(script.run());
}
}
config
首先配置类可以实现org.springframework.context.ApplicationContextAware接口用来获取应用上下文,然后再配置类中通过应用上下文获取所有的bean并注册到groovy的Binding中
typescript
package com.et.groovy.config;
import groovy.lang.Binding;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
@Configuration
public class GroovyBindingConfig implements ApplicationContextAware {
private ApplicationContext applicationContext;
//@Bean("groovyBinding")
public Binding groovyBinding() {
Binding groovyBinding = new Binding();
Map<String, Object> beanMap = applicationContext.getBeansOfType(Object.class);
for (String beanName : beanMap.keySet()) {
groovyBinding.setVariable(beanName, beanMap.get(beanName));
}
return groovyBinding;
}
@Bean("groovyBinding1")
public Binding groovyBinding1() {
Map<String, Object> beanMap = applicationContext.getBeansOfType(Object.class);
return new Binding(beanMap);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
components
groovy支持通过GroovyShell预设对象,在groovy动态脚本中直接调用预设对象的方法
scala
package com.et.groovy.component;
import groovy.lang.Script;
public class TestScript extends Script {
@Override
public Object run() {
return null;
}
public Integer add (Integer first, Integer second) {
return first + second;
}
}
以上只是一些关键代码,所有代码请参见下面代码仓库
代码仓库
3.测试
启动Spring Boot应用