preface

Since the project needs to use Groovy language, I have studied it roughly in the past two days. This article is a simple summary of learning, and the main content is referred to the official documentation (Groovy official documentation is still very good, highly recommended reading). I hope this article will be helpful for those of you who are preparing to use Groovy or who are interested in it. Please feel free to point out any mistakes you may have made. Your understanding of the language is rather superficial.

Introduction to the

Groovy is an Apache dynamic/agile programming language based on the JVM platform. In terms of language design, it absorbs the excellent features of Python, Ruby and Smalltalk languages. Its syntax is very concise and beautiful. Development efficiency is also very high (programming language development efficiency and performance are contradictory, more advanced programming language performance is worse, because it means more low-level encapsulation, but the development efficiency is higher, depending on the use of scenarios). In addition, Groovy works seamlessly with the Java language. If you forget the syntax when writing Groovy, you can just follow the Java syntax, or you can call Groovy scripts from Java. This effectively reduces the cost of learning Groovy for Java developers. Groovy is not a replacement for Java, but rather a complementary language, depending on the problem being solved and the context in which it is used.

Quick start

  1. Download the Groovy Development Kit (GDK) www.groovy-lang.org/download.ht…
  2. Creating a Groovy project

    To use IDEA, you need to install Groovy’s support plugin. After installation, the Groovy project option will appear in the new project. You can select Groovy project and associate it with Groovy Libray, or you can create it directly.groovyFiles are run directly from the command line.
  3. Hello World

    To print “Hello World” in Java, create a class and then create a main method.
    public class Hello {
     public static void main(String[] args) {
         System.out.println("hello world"); }}Copy the code

    In Groovy, all of these can be omitted, and the following four ways can output “Hello world”.

    System.out.println("hello world");
    System.out.println "hello world";
    println("hello world")
    println 'hello world'Copy the code

    Of course, you can also run it in the main method of a class, just like Java.

    class Hello {
     static void main(args) {
         println 'hello world'}}Copy the code

    If the Groovy Script file contains only executing code and no class definition, the Groovy compiler generates a subclass of Script with the same class name as the file name of the Script file, and the code in the Script is contained in a file namedrunA main method is also generated as an entry point to the entire script. So, as a JVM platform language, it’s essentially the same as Java.

Some differences with Java

The default import

Groovy imports the following packages and classes by default, without explicitly importing them using import statements.

java.io.*
java.lang.*
java.math.BigDecimal
java.math.BigInteger
java.net.*
java.util.*
groovy.lang.*
groovy.util.*Copy the code

Multiple methods

In Groovy, the method called is selected at run time. This mechanism, known as runtime dispatch or multi-methods, selects methods based on the type of the runtime argument. Java takes the opposite approach: compile time selects methods based on the declared type.

Here is an example where the same Java code running in Java and Groovy has different results.

int method(String arg) {
    return 1;
}
int method(Object arg) {
    return 2;
}
Object o = "Object";
int result = method(o);
// In Java
assertEquals(2, result);
// In Groovy
assertEquals(1, result);Copy the code

The reason for the difference is that Java uses static data types and O is declared as an Object, whereas Groovy selects when the methods are actually called at runtime. Because we are calling a String object, we call the String version of the method naturally.

Array initialization syntax

In Groovy, {… } blocks are reserved for closures, so you can’t initialize an array the way you do in Java

int[] array = { 1.2.3}Copy the code

It should look something like this

int[] array = [1.2.3]Copy the code

POJO

Groovy implicitly creates getters and setters by default, and provides constructors with parameters, which are equivalent.

// In Java
public class Person {
    private String name;
    Person(String name) {
        this.name = name
    }
    public String getName(a) {
        return name
    }
    public void setName(String name) {
        this.name = name
    }
}

// In Groovy
class Person {
    String name
}

def person = new Person(name: 'Joe')
assert 'Joe' == person.name
person.name = 'bill'
/ / person. Elegantly-named setName () 'bill'
assert 'bill' == person.getName()Copy the code

Package access

In Java, if you don’t explicitly specify access modifiers (public, protected, private) then package access is given by default, but in Groovy it is given by default, so the @packagescope annotation is required.

class Person {
    @PackageScope String name
}Copy the code

ARM blocks

The ARM (Automatic Resource Management) statement block (or TWR syntax) was introduced in Java 7 to reduce the complexity of IO manipulation code, but Groovy doesn’t support it. Instead, Groovy offers multiple closure-based approaches that achieve the same effect and are much more elegant.

//In Groovy
Path file = Paths.get("/User/lihengming/test.txt");
Charset charset = Charset.forName("UTF-8");
try (BufferedReader reader = Files.newBufferedReader(file, charset)) {
    String line;
    while((line = reader.readLine()) ! =null) { System.out.println(line); }}catch (IOException e) {
    e.printStackTrace();
}

//In Groovy
new File('/User/lihengming/test.txt').eachLine('UTF-8') {
   println it
}
// Or this way, more like the Java way
new File('/User/lihengming/test.txt').withReader('UTF-8') { reader ->
   reader.eachLine {
       println it
   }
}
// This is the simplest way to read and print the contents of the text
print new File('/User/lihengming/test.txt').textCopy the code

The inner class

Groovy also supports internal classes and the implementation is the same as Java, but it should not be considered in terms of the Java language specification. Keep shaking the head about things that are different. The inner class in Groovy looks a bit like the implementation of the Groovy.lang.Closure class.

// Static inner class
class A {
    static class B {}}new A.B()

// Anonymous inner class
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit

CountDownLatch called = new CountDownLatch(1)

Timer timer = new Timer()
timer.schedule(new TimerTask() {
    void run(a) {
        called.countDown()
    }
}, 0)

assert called.await(10, TimeUnit.SECONDS)Copy the code

Lambda expressions

Java 8 supports Lambda expressions and method references

Runnable run = () -> System.out.println("Run");
list.forEach(System.out::println);Copy the code

Lambdas in Java 8 can almost be thought of as anonymous inner classes. Groovy doesn’t use this syntax, and instead uses closures.

Runnable run = { println 'run' }
list.each { println it } // or list.each(this.&println)Copy the code

GString

Because String literals enclosed in double quotes are interpreted as GString values (short for “Groovy strings”), when a String literal in a class contains a dollar character ($), the Groovy and Java compilers compile it, Groovy will most likely fail to compile, or produce different results from Java compilation.

Normally, Groovy will automatically convert GString and String if an API declares the type of a parameter. Be careful with Java apis that take Object and check their actual types.

String and character literals

In Groovy, single-quoted literals are strings, while double-quoted literals can be strings or GStrings, depending on whether there is interpolation in them or not.

assert 'c'.getClass()==String
assert "c".getClass()==String
assert "c${1}".getClass() in GStringCopy the code

Basic grammar

  • Groovy statements do not use semicolons (;), which is fully compatible with Java syntax.
  • Groovy= =Equivalent to JavaequalsMethods.

annotation

Comments, like Java, support single lines (//), multiple lines (/** /), and document comments (/** */)

Shebang line is also supported. (UNIX systems support a special single-line comment called Shebang line to indicate the environment in which the script is running, so that it can be run directly from the terminal.) The # must be the first character of the file.

#! /usr/bin/env groovy
println "Hello from the shebang line"Copy the code

variable

The default access modifier for variables defined in Groovy is public. Variables are defined in accordance with the Java variable naming convention. Variable names start with letters, underscores, or the dollar sign $, and can contain letters, numbers, underscores, and the dollar sign $, except for keywords. In addition to these rules, Groovy can omit the trailing semicolon when defining variables on a single line, but if multiple variables are on a single line, the variables must be separated by a semicolon.

Groovy defines variables in a similar way to Java, except that Groovy provides the def keyword, which lets you omit the definition of a variable type and deduce the type from the value of the variable.

def a = 123
def b = 'b'
def c = true 
boolean d = false
int e = 123Copy the code

If the literal value of a variable is a number, the type is automatically adjusted based on the size of the number

def a = 1
assert a instanceof Integer

// Integer.MAX_VALUE
def b = 2147483647
assert b instanceof Integer

// Integer.MAX_VALUE + 1
def c = 2147483648
assert c instanceof Long

// Long.MAX_VALUE
def d = 9223372036854775807
assert d instanceof Long

// Long.MAX_VALUE + 1
def e = 9223372036854775808
assert e instanceof BigIntegerCopy the code

Groovy’s default type for floating-point literals is BigDecimal for accuracy

def decimal = 123.456
println decimal.getClass() // class java.math.BigDecimalCopy the code

Groovy provides an easier way to declare numeric types: the type suffix

-integer uses I or i-long uses L or l-bigINTEGER uses G or g-bigDecimal uses G or g-double uses D or d-float uses F or FCopy the code

Using the example

def a = 123I
assert a instanceof Integer
def b= 123L
assert b instanceof LongCopy the code

string

There are two types of strings in Groovy, plain strings (java.lang.string) and interpolated strings (groovy.lang.gstring). Ordinary strings use single quotes

println 'hello'Copy the code

Interpolate strings using double quotes

def name = 'Joe'
println "hello $name"Copy the code

In addition, it also supports triple single quotation marks to preserve the text’s newline and indent format

Def strippedFirstNewline = "line one line two line three" println strippedFirstNewline // Def strippedFirstNewline2 = "\ line one line two line three" "println strippedFirstNewline2Copy the code

character

There is no explicit representation of character literals in Groovy, and there are three ways to specify what to display

char c1 = 'A' // Declaration type
assert c1 instanceof Character

def c2 = 'B' as char // Use the as keyword
assert c2 instanceof Character

def c3 = (char) 'C' // Cast
assert c3 instanceof CharacterCopy the code

Method (function)

The default access modifier for Groovy methods is public, and the return type of a method may not be declared, but the def keyword is added. Methods that return a value can be omitted. By default, the result of the last line of code is returned, or the specified return value if the return keyword is used.

String method1(a) {
    return 'hello'
}
assert method1(a) = ='hello';

def method2(a) {
    return 'hello'
}
assert method2(a) = ='hello';

def method3(a) {
    'hello'
}
assert method3(a) = ='hello';Copy the code

The parameter types of Groovy methods can be omitted and default to Object.

def add(int a, int b) {
    return a + b
}
// Is equivalent to the above
def add(a, b) {
    a + b
}Copy the code

Other features of Groovy’s methods are the same as Java’s, such as support for overloading, variable-length arguments (…). And so on.

closure

Groovy provides support for closures, which have a syntax similar to Lambda expressions and are simply an executable block of code or pointer to a function. Closures in Groovy are instances of the groovy.lang.Closure class, which allows closures to be assigned to variables or passed as arguments. Groovy’s syntax for defining closures is simple, as shown below.

// Closure parameters are optional
def closure = { [closureParameters -> ] statements }Copy the code

Closures can access external variables, whereas methods (functions) cannot.

def str = 'hello'
def closure={
    println str
}
closure()//helloCopy the code

Closures can be called in one of two ways: closure. Call (parameter) or closure (parameter), where parentheses can be omitted.

def closure = {
    param -> println param
}

closure('hello')
closure.call('hello')
closure 'hello'Copy the code

Arguments to closures are optional, and the -> operator can be omitted if there are no arguments.

def closure = {println 'hello'}
closure()Copy the code

Multiple parameters are separated by commas, and parameter types, like methods, can be explicitly declared or omitted.

def closure = { String x, int y ->                                
    println "hey ${x} the value is ${y}"
}Copy the code

If you have only one parameter, you can omit the parameter definition, and Groovy provides an implicit parameter it instead.

def closure = { it -> println it } 
// This is equivalent to the above
def closure = { println it }   
closure('hello')Copy the code

Closures can be passed in as arguments, and parentheses can be omitted when the closure is the only or last argument to a method.

def eachLine(lines, closure) {
    for (String line : lines) {
        closure(line)
    }
}

eachLine('a'.'z',{ println it }) 
// The parentheses can be omitted, equivalent to the above
eachLine('a'.'z') { println it }Copy the code

Lists

Groovy defines lists very succinctly, using brackets ([]) and separating elements with commas (,). The List in Groovy is actually java.util.List, and the implementation class uses java.util.ArrayList by default.

def numbers = [1.2.3]         

assert numbers instanceof List  
assert numbers.class == java.util.ArrayList  
assert numbers.size() == 3Copy the code

Arrays

Groovy’s syntax for defining arrays is very similar to that of List, except that arrays must be defined as array types. You can either define the type directly or use the DEF definition and then specify the type via the AS keyword.

String[] arrStr = ['Ananas'.'Banana'.'Kiwi'] // Declare the type as array String[]

assert arrStr instanceof String[]    
assert! (arrStrinstanceof List)

def numArr = [1.2.3] as int[]     // as keyword specifies type as array type int[]

assert numArr instanceof int[]       
assert numArr.size() == 3Copy the code

Maps

Groovy defines a Map in a neat way, with the key and val enclosed in parentheses, and the key and value separated by colons ([key:value]). The Map in Groovy is actually java.util.map, and the implementation class uses java.util.linkedhashMap by default.

// The key is not quoted, but Groovy converts it to a string by default
def colors = [red: '#FF0000', green: '#00FF00', blue: '#0000FF']

assert colors['red'] = ='#FF0000' // Access with brackets
assert colors.green == '#00FF00' // Use dot expression access

colors['pink'] = '#FF00FF' // Use brackets to add elements, equivalent to the Put (key,value) method of a Java Map
colors.yellow = '#FFFF00'// Use dot expressions to add elements
assert colors.pink == '#FF00FF'
assert colors['yellow'] = ='#FFFF00'
assert colors instanceof java.util.LinkedHashMap // The default LinkedHashMap type is used

// Groovy Map's default key syntax does not support variables. The key is the string 'keyVal' instead of the value 'name' of the keyVal variable.
def keyVal = 'name'
def persons = [keyVal: 'Guillaume'] 
assert! persons.containsKey('name')
assert persons.containsKey('keyVal')

// To use a variable as a key, use parentheses
def keyVal = 'name'
def persons = [(keyVal): 'Guillaume'] 
assert persons.containsKey('name')
assert! persons.containsKey('keyVal')Copy the code

Range

In Groovy you can use.. Operator to define an interval object, simplifying the code for range operations.

def range = 0.5
assert (0.5).collect() == [0.1.2.3.4.5]
assert (0..<5).collect() == [0.1.2.3.4] // It is equivalent to the left closed and right open interval
assert (0.5) instanceof List // Range is an implementation of the List interface
assert (0.5).size() == 6
assert ('a'.'d').collect() == ['a'.'b'.'c'.'d']// Can also be a character type

// Common usage scenarios
for (x in 1.10) {
    println x
}

('a'.'z').each {
    println it
}

def age = 25;
switch (age) {
    case 0.17:
        println 'minor'
        break
    case 18.30:
        println 'young'
        break
    case 31.50:
        println 'middle'
        break
    default:
        println 'older'
}Copy the code

Common Usage Scenarios

Grails

Grails is a highly productive, one-stop Web development framework based on Groovy and built on Spring/Spring Boot and Hibernate. Especially suitable for small teams for agile development, very high efficiency. Due to the low penetration rate due to performance and learning cost, most companies still prefer To choose Spring Boot as the development framework.

Gradle

Gradle is a project automation build tool based on Apache Ant and Apache Maven concepts. It uses a Groovy-based domain-specific language (DSL) for build configuration, ditching all the tedious XML-based configuration and focusing on Java applications, with support for downloading dependencies from Maven repositories. More and more projects (mostly Android projects) are using Gradle as a build tool for their projects. In the future, Gradle will gradually replace Maven, just as Maven has replaced Ant.

Build projects using Maven

<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">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.vs</groupId>
    <artifactId>com.vs.maven.gradle</artifactId>
    <version>1.0</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.6. RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa
            </artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.25</version>
        </dependency>
    </dependencies>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>Copy the code

Build projects using Gradle

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("Org. Springframework. The boot: spring - the boot - gradle - plugin: 1.2.6. RELEASE")
    }
}
dependencies {
    compile("org.springframework.boot:spring-boot-starter-web") {
        exclude module: "spring-boot-starter-tomcat"
    }
    compile("org.springframework.boot:spring-boot-starter-security")
    compile("org.springframework.boot:spring-boot-starter-data-jpa")
    testCompile("Mysql: mysql connector - Java: 5.1.25")}Copy the code

Spring supports

Dynamic scripting languages have been supported since Spring 2.0 (address in the Spring documentation section), including Groovy, which provides the
tag to define Groovy beans. Groovy beans can load script files using the script-source property, script files can come from local or network sources, and dynamic beans can be implemented using the refresh-check-delay property to listen for code changes within scripts to reload beans.

// from the file '/java/Calculator.java'
public interface Calculator {
    int add(int x, int y);
}

// from the file '/resources/CalculatorGroovyImpl.groovy'
class CalculatorGroovyImpl implements Calculator {
    int add(int x, int y) {
        x + y
    }
}Copy the code
<-- from the file 'beans.xml'to omitxmlns -->
<?xml version="1.0" encoding="UTF-8"? >
<beans>
    <lang:groovy id="calculator" script-source="classpath:CalculatorGroovyImpl.groovy" refresh-check-delay="1000"/>
</beans>Copy the code
public class Tester {
    public static void main(String[] args) throws InterruptedException {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Calculator calculator = (Calculator)context.getBean("calculator");
        / / try to modify CalculatorGroovyImpl. Groovy, x + y, modified to x * y.
        while (true){
            System.out.println(calculator.add(1.1));
            TimeUnit.SECONDS.sleep(1); }}}Copy the code

As of Spring 4.0, Spring supports the use of Groovy DSLS to define Bean configurations, as described in the Spring documentation section.

beans {
    //beanName(type)  
    dataSource(BasicDataSource) {
        // Inject properties
        driverClassName = "org.hsqldb.jdbcDriver"
        url = "jdbc:hsqldb:mem:grailsDB"
        username = "sa"
        password = ""
        settings = [mynew:"setting"]
    }
    sessionFactory(SessionFactory) {
       // Inject properties to reference other beans
        dataSource = dataSource
    }
    myService(MyService) {
       // Define nested beans using closures
        nestedBean = { AnotherBean bean ->
            dataSource = dataSource
        }
    }
}Copy the code
ApplicationContext context = new GenericGroovyApplicationContext("beans.groovy");
MyService myService = context.getBean(MyService.class);Copy the code

With the Java integration

Groovy is easy to integrate into a Java environment, leveraging its dynamic nature as a rules engine, process engine, and dynamic scripting environment, making it ideal for scenarios that don’t require frequent releases but often change. There are several ways to integrate (call) Groovy code in Java.

Eval

The groovy.util.Eval class is the simplest for dynamically executing Groovy code at runtime, providing several static factory methods that essentially encapsulate GroovyShell.

// Execute Groovy code
Eval.me("println 'hello world'");
// Bind custom parameters
Object result = Eval.me("age".22."If (age < 18){' minor '}else{' minor '}");
assertEquals(result, "Adult");
// Bind a simple calculation of a parameter named x
assertEquals(Eval.x(4."2*x"), 8);
// A simple calculation with two binding parameters named x and y
assertEquals(Eval.xy(4.5."x*y"), 20);
// A simple calculation with three binding parameters (x, y, and z)
assertEquals(Eval.xyz(4.5.6."x*y+z"), 26);Copy the code
GroovyShell

Groovy.lang.GroovyShell provides more functionality than just executing Groovy code, such as binding more variables, loading code from the file system, the network, and so on.

GroovyShell shell = new GroovyShell();
// More variables can be bound
shell.setVariable("age".22);
// Evaluate directly
shell.evaluate("If (age < 18){' minor '}else{' minor '}");
// Parse to script, defer execution or cache
Script script = shell.parse("If (age < 18){' minor '}else{' minor '}");
assertEquals(script.run(), "Adult");
// Scripts can be loaded/executed from more locations
//shell.evaluate(new File("script.groovy"));
//shell.evaluate(new URI("http://wwww.a.com/script.groovy"));Copy the code
GroovyClassLoader

Groovy. Lang. GroovyClassLoader is a custom Class loaders, can be loaded at run time groovy code, generated Class object.

 GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
 String scriptText = "class Hello { void hello() { println 'hello' } }";
 // Parse Groovy scripts into Class objects
 Class clazz = groovyClassLoader.parseClass(scriptText);
 //Class clazz = groovyClassLoader.parseClass(new File("script.groovy"));
 assertEquals(clazz.getName(),"Hello");
 clazz.getMethod("hello").invoke(clazz.newInstance());Copy the code
GroovyScriptEngine

Groovy. Util. GroovyScriptEngine can handle any dynamic compilation and loading of groovy code, can load the script from the unified position, and the ability to monitor the change of the script, when the script when they change to reload.

//script/groovy/hello.groovy
println "hello $name"Copy the code
GroovyScriptEngine scriptEngine = new GroovyScriptEngine("script/groovy");
Binding binding = new Binding();
binding.setVariable("name"."groovy");
while (true){
    scriptEngine.run("hello.groovy", binding);
    TimeUnit.SECONDS.sleep(1);
}Copy the code
// Output hello groovy Hello groovy.... // Change the hello.groovy code to println"hi $name"GroovyScriptEngine will reload hi Groovy hi GroovyCopy the code
JSR 223 javax.script API

Jsr-223 is the standard API for calling the scripting language in Java. Introduced in Java 6, the main purpose is to provide a unified framework for invoking multiple scripting languages in Java. Jsr-223 supports most popular scripting languages such as JavaScript, Scala, JRuby, Jython, and Groovy.

ScriptEngine engine = new ScriptEngineManager().getEngineByName("groovy");
Bindings bindings = new SimpleBindings();
bindings.put("age".22);
Object value = engine.eval("If (age < 18){' minor '}else{' minor '}",bindings);
assertEquals(value,"Adult");

//script/groovy/hello.groovy
//println "hello world"
engine.eval(new FileReader("script/groovy/hello.groovy"));
//hello worldCopy the code

Since Groovy already provides richer integration mechanisms of its own, it might be more appropriate to use Groovy as a scripting language in Java applications.

There are a few ways to integrate with Java, but I recommend reading them before using them: Common pitfalls of Groovy and Java integration

reference

  • Apache Groovy Docmentation
  • Spring Framework Reference Documentation