g3 summit 2016 - taking advantage of groovy annotations

86
G3 Summit Taking advantage of Groovy Annotations Iván López - @ilopmar

Upload: ivan-lopez-martin

Post on 16-Apr-2017

4.937 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: G3 Summit 2016 - Taking Advantage of Groovy Annotations

G3 Summit

Taking advantage of Groovy Annotations

Iván López - @ilopmar

Page 2: G3 Summit 2016 - Taking Advantage of Groovy Annotations

➢ Iván López - @ilopmar

➢ Groovy & Grails developer

Member of Grails team at OCI

➢ @MadridGUG coordinator

http://www.madridgug.com

➢ Greach organizer (@greachconf)

http://greachconf.com

➢ Speaker: SpringOne 2GX, GR8Conf, Codemotion, GeeCon, Spring IO, Greach, JavaCro, RigaDevDay,...

About me...

Page 3: G3 Summit 2016 - Taking Advantage of Groovy Annotations

“The best code is not code at all

Page 4: G3 Summit 2016 - Taking Advantage of Groovy Annotations

1.A little bit of theory

Page 5: G3 Summit 2016 - Taking Advantage of Groovy Annotations

AST and compilation

▷ Abstract Syntax Tree

▷ AST modified during compilation

▷ Hook into the compiler phases

Page 6: G3 Summit 2016 - Taking Advantage of Groovy Annotations

AST Transformations

▷ Global ▷ Local

Page 7: G3 Summit 2016 - Taking Advantage of Groovy Annotations

2.Out-of-the-box ASTs

Page 8: G3 Summit 2016 - Taking Advantage of Groovy Annotations

AST transformations categories

▷ Code generation

▷ Class design

▷ Logging improvements

▷ Declarative concurrency

▷ Cloning and externalizing

▷ Safe scripting

▷ Compiler directives

▷ Dependencies handling

Page 9: G3 Summit 2016 - Taking Advantage of Groovy Annotations

Code generation

Page 10: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@ToString

▷ Human readable toString

▷ Effective Java by Joshua Bloch (item 10)

Page 11: G3 Summit 2016 - Taking Advantage of Groovy Annotations

class User { String name Integer age}

def u = new User(name: 'Iván', age: 36)

println u // User@1d2a54b2

Page 12: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@groovy.transform.ToStringclass User { String name Integer age}

def u = new User(name: 'Iván', age: 36)assert u.toString() == 'User(Iván, 36)'

class User { String name Integer age}

def u = new User(name: 'Iván', age: 36)

println u // User@1d2a54b2

Page 13: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@groovy.transform.ToStringclass User { String name Integer age}

def u = new User(name: 'Iván', age: 36)assert u.toString() == 'User(Iván, 36)'

String toString() { def _result = new StringBuilder() _result.append('User(') _result.append(this.name) _result.append(', ') _result.append(this.age) _result.append(')') return _result.toString()}

class User { String name Integer age}

def u = new User(name: 'Iván', age: 36)

println u // User@1d2a54b2

Page 14: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@ToString

▷ includeNames, excludes, includes, includeSuper, includeSuperProperties, includeFields, ignoreNulls, includePackage, cache

Page 15: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@ToString

▷ includeNames, excludes, includes, includeSuper, includeSuperProperties, includeFields, ignoreNulls, includePackage, cache

@groovy.transform.ToString(includeNames = true, excludes = ['name'])class User { String name Integer age}

def u = new User(name: 'Iván', age: 36)assert u.toString() == 'User(age:36)'

Page 16: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@EqualsAndHashCode

▷ Generate equals and hashCode implementations

▷ Effective Java items 8 & 9

Page 17: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@EqualsAndHashCode

▷ Generate equals and hashCode implementations

▷ Effective Java items 8 & [email protected] User { String name Integer age}

def u1 = new User(name: 'Iván', age: 36)def u2 = new User(name: 'Iván', age: 36)

assert u1 == u2assert u1.hashCode() == u2.hashCode()

Page 18: G3 Summit 2016 - Taking Advantage of Groovy Annotations

int hashCode() { def _result = HashCodeHelper.initHash() _result = HashCodeHelper.updateHash(_result, this.name) _result = HashCodeHelper.updateHash(_result, this.age) return _result}

boolean canEqual(Object other) { return other instanceof User}

boolean equals(Object other) { if (other == null) { return false } if (this.is(other)) { return true } if (!(other instanceof User)) { return false } User otherTyped = ((other) as User) if (!(otherTyped.canEqual(this))) { return false } if (!(this.getName().is(otherTyped.getName()))) { if (this.getName().is(this) && !(otherTyped.getName().is(otherTyped)) || !(this.getName().is(this)) && otherTyped.getName().is(otherTyped)) { return false } else { if (!(this.getName().is(this) && otherTyped.getName().is(otherTyped))) { if (!(this.getName() == otherTyped.getName())) { return false } } } } if (!(this.getAge().is(otherTyped.getAge()))) { if (this.getAge().is(this) && !(otherTyped.getAge().is(otherTyped)) || !(this.getAge().is(this)) && otherTyped.getAge().is(otherTyped)) { return false } else { if (!(this.getAge().is(this) && otherTyped.getAge().is(otherTyped))) { if (!(this.getAge() == otherTyped.getAge())) { return false } } } } return true}

Page 19: G3 Summit 2016 - Taking Advantage of Groovy Annotations
Page 20: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@EqualsAndHashCode

▷ excludes, includes, callSuper, includeFields, cache, useCanEqual

Page 21: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@EqualsAndHashCode

▷ excludes, includes, callSuper, includeFields, cache, useCanEqual

@groovy.transform.EqualsAndHashCode(includes = 'name')class User { String name Integer age}

def u1 = new User(name: 'Iván', age: 36)def u2 = new User(name: 'Iván', age: 42)

assert u1 == u2assert u1.hashCode() == u2.hashCode()

Page 22: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@TupleConstructor

▷ Generate constructors

Page 23: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@TupleConstructor

▷ Generate constructors

@groovy.transform.TupleConstructorclass User { String name Integer age}

Page 24: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@TupleConstructor

▷ Generate constructors

@groovy.transform.TupleConstructorclass User { String name Integer age}

// Default map constructordef u1 = new User(name: 'Iván', age: 36)

Page 25: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@TupleConstructor

▷ Generate constructors

@groovy.transform.TupleConstructorclass User { String name Integer age}

// Default map constructordef u1 = new User(name: 'Iván', age: 36)

// Generated tuple constructordef u2 = new User('Iván', 36)def u3 = new User('Iván')

Page 26: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@TupleConstructor

▷ Generate constructors

@groovy.transform.TupleConstructorclass User { String name Integer age}

// Default map constructordef u1 = new User(name: 'Iván', age: 35)

// Generated tuple constructordef u2 = new User('Iván', 36)def u3 = new User('Iván')

User(String name = null, Integer age = null) { this.name = name this.age = age }

Page 27: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@TupleConstructor

▷ excludes, includes, includeFields, includeProperties, includeSuperFields, includeSuperProperties, callSuper, force

Page 28: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@Canonical

▷ @ToString + @EqualsAndHashCode + @TupleConstructor

Page 29: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@Canonical

▷ @ToString + @EqualsAndHashCode + @TupleConstructor

@groovy.transform.Canonicalclass User { String name Integer age}

Page 30: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@Canonical

▷ @ToString + @EqualsAndHashCode + @TupleConstructor

def u1 = new User(name: 'Iván', age: 36)assert u1.toString() == 'User(Iván, 36)' // @ToString

@groovy.transform.Canonicalclass User { String name Integer age}

Page 31: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@Canonical

▷ @ToString + @EqualsAndHashCode + @TupleConstructor

def u2 = new User('Iván', 36) // @TupleConstructorassert u2.toString() == 'User(Iván, 36)'

def u1 = new User(name: 'Iván', age: 36)assert u1.toString() == 'User(Iván, 36)' // @ToString

@groovy.transform.Canonicalclass User { String name Integer age}

Page 32: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@Canonical

▷ @ToString + @EqualsAndHashCode + @TupleConstructor

assert u1 == u2 // @EqualsAndHashCodeassert u1.hashCode() == u2.hashCode() // @EqualsAndHashCode

def u2 = new User('Iván', 36) // @TupleConstructorassert u2.toString() == 'User(Iván, 36)'

def u1 = new User(name: 'Iván', age: 36)assert u1.toString() == 'User(Iván, 36)' // @ToString

@groovy.transform.Canonicalclass User { String name Integer age}

Page 33: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@InheritConstructors

▷ Reduce boilerplate code when parent classes have multiple constructors

▷ Useful when overriding exception classes

Page 34: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@groovy.transform.InheritConstructorsclass MyException extends Exception {}

Page 35: G3 Summit 2016 - Taking Advantage of Groovy Annotations

protected MyException(String param0, Throwable param1, boolean param2, boolean param3) { super(param0, param1, param2, param3)}

public MyException(Throwable param0) { super(param0) }

public MyException(String param0, Throwable param1) { super(param0, param1) }

public MyException(String param0) { super(param0) }

public MyException() { super() }

@groovy.transform.InheritConstructorsclass MyException extends Exception {}

Page 36: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@Lazy

▷ Lazy initialization of fields

▷ Useful when creating expensive resources

▷ Effective Java item 71

Page 37: G3 Summit 2016 - Taking Advantage of Groovy Annotations

class SomeBean { @Lazy LinkedList myField}

Page 38: G3 Summit 2016 - Taking Advantage of Groovy Annotations

class SomeBean { @Lazy LinkedList myField}

public LinkedList getMyField() { if ($myField != null) { $myField } else { $myField = new LinkedList() }}

Page 39: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@Sortable

▷ Comparable interface

▷ compareTo method natural order

▷ N methods returning comparators

▷ Effective Java item 12

Page 40: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@groovy.transform.Sortableclass User { String name Integer age Integer born}

Page 41: G3 Summit 2016 - Taking Advantage of Groovy Annotations

public int compareTo(User other) { if (this.is(other)) return 0 Integer value = 0 value = this.name <=> other.name if (value != 0) return value value = this.age <=> other.age if (value != 0) return value value = this.born <=> other.born if (value != 0) return value return 0}

private static class User$NameComparator extends AbstractComparator<User> { public int compare(User arg0, User arg1) { if (arg0 == arg1) return 0 if (arg0 != null && arg1 == null) return -1 if (arg0 == null && arg1 != null) return 1 return arg0.name <=> arg1.name }}

private static class User$AgeComparator extends AbstractComparator<User> { ...}

@groovy.transform.Sortableclass User { String name Integer age Integer born}

Page 42: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@groovy.transform.Sortableclass User { String name Integer age Integer born}

Page 43: G3 Summit 2016 - Taking Advantage of Groovy Annotations

def users = [ new User(name: 'Mary', age: 15, born: 2000), new User(name: 'Peter', age: 44, born: 1970), new User(name: 'John', age: 35, born: 1979),]

@groovy.transform.Sortableclass User { String name Integer age Integer born}

Page 44: G3 Summit 2016 - Taking Advantage of Groovy Annotations

assert users.sort(false, User.comparatorByName())*.name == ['John', 'Mary', 'Peter']assert users.sort(false, User.comparatorByAge())*.born == [2000, 1979, 1970]

def users = [ new User(name: 'Mary', age: 15, born: 2000), new User(name: 'Peter', age: 44, born: 1970), new User(name: 'John', age: 35, born: 1979),]

@groovy.transform.Sortableclass User { String name Integer age Integer born}

Page 45: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@Sortable

▷ includes, excludes

@groovy.transform.Sortable(excludes = 'age')class User { String name Integer age Integer born}

def users = [ new User(name: 'Mary', age: 15, born: 2000), new User(name: 'Peter', age: 44, born: 1970), new User(name: 'John', age: 35, born: 1979),]

assert users.sort(false, User.comparatorByName())*.name == ['John', 'Mary', 'Peter']assert users.sort(false, User.comparatorByAge())*.born == [2000, 1979, 1970]

Page 46: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@Builder

▷ Create fluent API calls

▷ Multiple building strategies

▷ Multiple configuration options: builder name, prefix, excludes, includes,...

▷ Effective Java item 2

Page 47: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@groovy.transform.builder.Builderclass User { String name Integer age Integer born}

Page 48: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@groovy.transform.builder.Builderclass User { String name Integer age Integer born}

def u = User.builder() .name('Iván') .age(36) .born(1979) .build()

assert u.name == 'Iván'assert u.age == 36assert u.born == 1979

Page 49: G3 Summit 2016 - Taking Advantage of Groovy Annotations

public static class User$UserBuilder extends Object {

private String name private Integer age private Integer born

public User$UserBuilder() { }

public User$UserBuilder name(String name) { this.name = name return this }

public User$UserBuilder age(Integer age) { this.age = age return this }

public User$UserBuilder born(Integer born) { this.born = born return this }

public User build() { User _theUser = new User() _theUser.name = name _theUser.age = age _theUser.born = born return _theUser }}

@groovy.transform.builder.Builderclass User { String name Integer age Integer born}

def u = User.builder() .name('Iván') .age(36) .born(1979) .build()

assert u.name == 'Iván'assert u.age == 36assert u.born == 1979

Page 50: G3 Summit 2016 - Taking Advantage of Groovy Annotations

Class design

Page 51: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@Delegate

▷ Implements delegation design pattern

▷ Delegate calls on object to method on delegated properties

▷ All public methods are delegated

Page 52: G3 Summit 2016 - Taking Advantage of Groovy Annotations

import java.time.LocalDate

class Conference { @groovy.lang.Delegate LocalDate when String name}

Page 53: G3 Summit 2016 - Taking Advantage of Groovy Annotations

import java.time.LocalDate

class Conference { @groovy.lang.Delegate LocalDate when String name}

def greach = new Conference(name: 'Greach', when: LocalDate.of(2017, 03, 31))def gr8conf = new Conference(name: 'GR8Conf' when: LocalDate.of(2017, 05, 31))

Page 54: G3 Summit 2016 - Taking Advantage of Groovy Annotations

def greach = new Conference(name: 'Greach', when: LocalDate.of(2017, 03, 31))def gr8conf = new Conference(name: 'GR8Conf' when: LocalDate.of(2017, 05, 31))

assert greach.isBefore(gr8conf)

import java.time.LocalDate

class Conference { @groovy.lang.Delegate LocalDate when String name}

Page 55: G3 Summit 2016 - Taking Advantage of Groovy Annotations

class Conference { ... public boolean isAfter(ChronoLocalDate param0) { when.isAfter(param0) }

public boolean isBefore(ChronoLocalDate param0) { when.isBefore(param0) } ...}

def greach = new Conference(name: 'Greach', when: LocalDate.of(2017, 03, 31))def gr8conf = new Conference(name: 'GR8Conf' when: LocalDate.of(2017, 05, 31))

assert greach.isBefore(gr8conf)

import java.time.LocalDate

class Conference { @groovy.lang.Delegate LocalDate when String name}

Page 56: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@Immutable

▷ Create immutable classes

▷ Effective Java item 15

▷ Rules for immutability

Page 57: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@groovy.transform.Immutableclass User { String name Integer age}

def u = new User(name: 'Iván', age: 36)

Page 58: G3 Summit 2016 - Taking Advantage of Groovy Annotations

// This does not compile// You are not allowed to overwrite// the final class 'User'.class Admin extends User {}

@groovy.transform.Immutableclass User { String name Integer age}

def u = new User(name: 'Iván', age: 36)

Page 59: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@groovy.transform.Immutableclass User { String name Integer age}

def u = new User(name: 'Iván', age: 36)

try { u.name = 'John'} catch (ReadOnlyPropertyException e) { println e}

// This does not compile// You are not allowed to overwrite // the final class 'User'.class Admin extends User {}

Page 60: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@Memoized

▷ Cache the result of a method

Page 61: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@Memoized

▷ Cache the result of a method

@groovy.transform.MemoizedLong fibonacci(Integer n) { if (n < 2) return 1 else return fibonacci(n-1) + fibonacci(n-2)}

fibonacci(300)

Page 62: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@Memoized

▷ Cache the result of a method

@groovy.transform.MemoizedLong fibonacci(Integer n) { if (n < 2) return 1 else return fibonacci(n-1) + fibonacci(n-2)}

fibonacci(300)

@groovy.transform.MemoizedUser getUserInfo(Long userId) { // Expensive repetitive // network operation}

Page 63: G3 Summit 2016 - Taking Advantage of Groovy Annotations

Logging improvements

Page 64: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@Log, @Log4j, @Log4j2, @Slf4j

▷ Static final field for the logger

Page 65: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@Log, @Log4j, @Log4j2, @Slf4j

@groovy.util.logging.Log4jclass MyClass { void method() { log.debug "My debug message" }}

▷ Static final field for the logger

Page 66: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@Log, @Log4j, @Log4j2, @Slf4j

@groovy.util.logging.Log4jclass MyClass { void method() { log.debug "My debug message" }}

▷ Static final field for the logger

class MyClass { private static final Logger log = Logger.getLogger(Saludador.name) void method() { if (log.isLoggable(Level.DEBUG)) { log.debug "My debug message" } }}

Page 67: G3 Summit 2016 - Taking Advantage of Groovy Annotations

Declarative concurrency

Page 68: G3 Summit 2016 - Taking Advantage of Groovy Annotations

Declarative concurrency

▷ @Synchronized

▷ @WithReadLock

▷ @WithWriteLock

Page 69: G3 Summit 2016 - Taking Advantage of Groovy Annotations

Cloning and externalizing

Page 70: G3 Summit 2016 - Taking Advantage of Groovy Annotations

Cloning and externalizing

▷ @AutoClone

▷ @AutoExternalize

Page 71: G3 Summit 2016 - Taking Advantage of Groovy Annotations

Safe scripting

Page 72: G3 Summit 2016 - Taking Advantage of Groovy Annotations

Safe scripting

▷ @ThreadInterrupt

▷ @TimedInterrupt

▷ @ConditionalInterrupt

Page 73: G3 Summit 2016 - Taking Advantage of Groovy Annotations

Compiler directives

Page 74: G3 Summit 2016 - Taking Advantage of Groovy Annotations

Compiler directives

▷ @TypeChecked

▷ @CompileStatic

▷ @CompileDynamic

Page 75: G3 Summit 2016 - Taking Advantage of Groovy Annotations

Dependencies handling

Page 76: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@Grab

▷ Grape dependency manager

Page 77: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@Grab

▷ Grape dependency manager

@Grab(group='org.springframework', module='spring-orm', version='3.2.5.RELEASE')import org.springframework.jdbc.core.JdbcTemplate

// or

@Grab('org.springframework:spring-orm:3.2.5.RELEASE')import org.springframework.jdbc.core.JdbcTemplate

Page 78: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@GrabResolver

▷ Grape dependency manager

@Grab(group='org.springframework', module='spring-orm', version='3.2.5.RELEASE')import org.springframework.jdbc.core.JdbcTemplate

// or

@Grab('org.springframework:spring-orm:3.2.5.RELEASE')import org.springframework.jdbc.core.JdbcTemplate

@GrabResolver(name='restlet', root='http://maven.restlet.org/')@Grab(group='org.restlet', module='org.restlet', version='1.1.6')

Page 79: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@GrabExclude

▷ Grape dependency manager

@Grab(group='org.springframework', module='spring-orm', version='3.2.5.RELEASE')import org.springframework.jdbc.core.JdbcTemplate

// or

@Grab('org.springframework:spring-orm:3.2.5.RELEASE')import org.springframework.jdbc.core.JdbcTemplate

@GrabResolver(name='restlet', root='http://maven.restlet.org/')@Grab(group='org.restlet', module='org.restlet', version='1.1.6')

@Grab('net.sourceforge.htmlunit:htmlunit:2.8')@GrabExclude('xml-apis:xml-apis')

Page 80: G3 Summit 2016 - Taking Advantage of Groovy Annotations

Bonus

Page 81: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@Pokemonclass Foo { void doStuff() { try { // Some code that can throws // different exceptions } catch (e) { // Gotta catch’em all } }}

Page 82: G3 Summit 2016 - Taking Advantage of Groovy Annotations

@Pokemonclass Foo { void doStuff() { try { // Some code that can throws // different exceptions } catch (e) { // Gotta catch’em all } }}

class Foo { @com.github.danveloper.ast.Pokemon void doStuff() { // code }}

Page 83: G3 Summit 2016 - Taking Advantage of Groovy Annotations

3.Demo

Page 84: G3 Summit 2016 - Taking Advantage of Groovy Annotations

4.Summary

Page 85: G3 Summit 2016 - Taking Advantage of Groovy Annotations

“The best code is not code at all

Page 86: G3 Summit 2016 - Taking Advantage of Groovy Annotations

Thanks!Any questions?

@ilopmar

[email protected]

https://github.com/ilopmar

Iván López

http://bit.ly/g3summit-groovy