eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

1. Overview

Groovy is a dynamic and powerful JVM language which has numerous features like closures and traits.

In this tutorial, we’ll explore the concept of Metaprogramming in Groovy.

2. What Is Metaprogramming?

Metaprogramming is a programming technique of writing a program to modify itself or another program using metadata.

In Groovy, it’s possible to perform metaprogramming at both runtime and compile-time. Going forward, we’ll explore a few notable features of both techniques.

3. Runtime Metaprogramming

Runtime metaprogramming enables us to alter the existing properties and methods of a class. Also, we can attach new properties and methods; all at runtime.

Groovy provides a few methods and properties that help to alter the behavior of a class at runtime.

3.1. propertyMissing

When we try to access an undefined property of a Groovy class, it throws a MissingPropertyException. To avoid the exception, Groovy provides the propertyMissing method.

First, let’s write an Employee class with some properties:

class Employee {
    String firstName
    String lastName  
    int age
}

Second, we’ll create an Employee object and try to display an undefined property address. Consequently, it will throw the MissingPropertyException:

Employee emp = new Employee(firstName: "Norman", lastName: "Lewis")
println emp.address
groovy.lang.MissingPropertyException: No such property: 
address for class: com.baeldung.metaprogramming.Employee

Groovy provides the propertyMissing method to catch the missing property request. Therefore, we can avoid a MissingPropertyException at runtime.

To catch a missing property’s getter method call, we’ll define it with a single argument for the property name:

def propertyMissing(String propertyName) {
    "property '$propertyName' is not available"
}
def "testPropertyMissing"() {
    expect:
    emp.address == "property 'address' is not available"
}

Also, the same method can have the second argument as the value of the property, to catch a missing property’s setter method call:

def propertyMissing(String propertyName, propertyValue) { 
    println "cannot set $propertyValue - property '$propertyName' is not available" 
}

3.2. methodMissing

The methodMissing method is similar to propertyMissing. However, methodMissing intercepts a call for any missing method, thereby avoiding the MissingMethodException.

Let’s try to call the getFullName method on an Employee object. As getFullName is missing, execution will throw the MissingMethodException at runtime:

try {
    emp.getFullName()
} catch (MissingMethodException e) {
    println "method is not defined"
}

So, instead of wrapping a method call in a try-catch, we can define methodMissing:

def methodMissing(String methodName, def methodArgs) {
    "method '$methodName' is not defined"
}
assert emp.getFullName() == "method 'getFullName' is not defined"

3.3. ExpandoMetaClass

Groovy provides a metaClass property in all its classes. The metaClass property refers to an instance of the ExpandoMetaClass.

The ExpandoMetaClass class provides numerous ways to transform an existing class at runtime. For example, we can add properties, methods, or constructors.

First, let’s add the missing address property to the Employee class using metaClass property:

Employee.metaClass.address = ""
Employee emp = new Employee(firstName: "Norman", lastName: "Lewis", address: "US")
assert emp.address == "US"

Moving further, let’s add the missing getFullName method to the Employee class object at runtime:

emp.metaClass.getFullName = {
    "$lastName, $firstName"
}
assert emp.getFullName() == "Lewis, Norman"

Similarly, we can add a constructor to the Employee class at runtime:

def "testMetaClassConstructor"() {
    when:
    Employee.metaClass.constructor = { String firstName ->
        new Employee(firstName: firstName)
    }

    and:
    Employee norman = new Employee("Norman")

    then:
    norman.firstName == "Norman"
    norman.lastName == null
}

Likewise, we can add static methods using metaClass.static.

The metaClass property is not only handy to modify user-defined classes, but also existing Java classes at runtime.

For example, let’s add a capitalize method to the String class:

String.metaClass.capitalize = { String str ->
    str.substring(0, 1).toUpperCase() + str.substring(1)
}
assert "norman".capitalize() == "Norman"

3.4. Extensions

An extension can add a method to a class at runtime and make it accessible globally.

The methods defined in an extension should always be static, with the self class object as the first argument.

For example, let’s write a BasicExtension class to add a getYearOfBirth method to the Employee class:

class BasicExtensions {
    static int getYearOfBirth(Employee self) {
        return Year.now().value - self.age
    }
}

To enable the BasicExtensions, we’ll need to add the configuration file in the META-INF/services directory of our project.

So, let’s add the org.codehaus.groovy.runtime.ExtensionModule file with the following configuration:

moduleName=core-groovy-2 
moduleVersion=1.0-SNAPSHOT 
extensionClasses=com.baeldung.metaprogramming.extension.BasicExtensions

Let’s verify the getYearOfBirth method added in the Employee class:

def age = 28
def expectedYearOfBirth = Year.now() - age
Employee emp = new Employee(age: age)
assert emp.getYearOfBirth() == expectedYearOfBirth.value

Similarly, to add static methods in a class, we’ll need to define a separate extension class.

For instance, let’s add a static method getDefaultObj to our Employee class by defining StaticEmployeeExtension class:

class StaticEmployeeExtension {
    static Employee getDefaultObj(Employee self) {
        return new Employee(firstName: "firstName", lastName: "lastName", age: 20)
    }
}

Then, we enable the StaticEmployeeExtension by adding the following configuration to the ExtensionModule file:

staticExtensionClasses=com.baeldung.metaprogramming.extension.StaticEmployeeExtension

Now, all we need is to test our static getDefaultObj method on the Employee class:

assert Employee.getDefaultObj().firstName == "firstName"
assert Employee.getDefaultObj().lastName == "lastName"
assert Employee.getDefaultObj().age == 20

Similarly, using extensions, we can add a method to pre-compiled Java classes like Integer and Long:

public static void printCounter(Integer self) {
    while (self > 0) {
        println self
        self--
    }
    return self
}
assert 5.printCounter() == 0
public static Long square(Long self) {
    return self*self
}
assert 40l.square() == 1600l

4. Compile-time Metaprogramming

Using specific annotations, we can effortlessly alter the class structure at compile-time. In other words, we can use annotations to modify the abstract syntax tree of the class at the compilation.

Let’s discuss some of the annotations which are quite handy in Groovy to reduce boilerplate code. Many of them are available in the groovy.transform package.

If we carefully analyze, we’ll realize a few annotations provides features similar to Java’s Project Lombok.

4.1. @ToString

The @ToString annotation adds a default implementation of the toString method to a class at compile-time. All we need is to add the annotation to the class.

For instance, let’s add the @ToString annotation to our Employee class:

@ToString
class Employee {
    long id
    String firstName
    String lastName
    int age
}

Now, we’ll create an object of the Employee class and verify the string returned by the toString method:

Employee employee = new Employee()
employee.id = 1
employee.firstName = "norman"
employee.lastName = "lewis"
employee.age = 28

assert employee.toString() == "com.baeldung.metaprogramming.Employee(1, norman, lewis, 28)"

We can also declare parameters such as excludes, includes, includePackage and ignoreNulls with @ToString to modify the output string.

For example, let’s exclude id and package from the string of the Employee object:

@ToString(includePackage=false, excludes=['id'])
assert employee.toString() == "Employee(norman, lewis, 28)"

4.2. @TupleConstructor

Use @TupleConstructor in Groovy to add a parameterized constructor in the class. This annotation creates a constructor with a parameter for each property.

For example, let’s add @TupleConstructor to the Employee class:

@TupleConstructor 
class Employee { 
    long id 
    String firstName 
    String lastName 
    int age 
}

Now, we can create Employee object passing parameters in the order of properties defined in the class.

Employee norman = new Employee(1, "norman", "lewis", 28)
assert norman.toString() == "Employee(norman, lewis, 28)"

If we don’t provide values to the properties while creating objects, Groovy will consider default values:

Employee snape = new Employee(2, "snape")
assert snape.toString() == "Employee(snape, null, 0)"

Similar to @ToString, we can declare parameters such as excludes, includes and includeSuperProperties with @TupleConstructor to alter the behavior of its associated constructor as needed.

4.3. @EqualsAndHashCode

We can use @EqualsAndHashCode to generate the default implementation of equals and hashCode methods at compile time.

Let’s verify the behavior of @EqualsAndHashCode by adding it to the Employee class:

def "testEqualsAndHashCodeAnnotation"() {
    when:
    Employee norman = new Employee(1, "norman", "lewis", 28)
    Employee normanCopy = new Employee(1, "norman", "lewis", 28)

    then:
    norman == normanCopy
    norman.hashCode() == normanCopy.hashCode()
}

4.4. @Canonical

@Canonical is a combination of @ToString, @TupleConstructor, and @EqualsAndHashCode annotations.

Just by adding it, we can easily include all three to a Groovy class. Also, we can declare @Canonical with any of the specific parameters of all three annotations.

4.5. @AutoClone

A quick and reliable way to implement Cloneable interface is by adding the @AutoClone annotation.

Let’s verify the clone method after adding @AutoClone to the Employee class:

try {
    Employee norman = new Employee(1, "norman", "lewis", 28)
    def normanCopy = norman.clone()
    assert norman == normanCopy
} catch (CloneNotSupportedException e) {
    e.printStackTrace()
}

4.6. Logging Support With @Log, @Commons, @Log4j, @Log4j2, and @Slf4j

To add logging support to any Groovy class, all we need is to add annotations available in groovy.util.logging package.

Let’s enable the logging provided by JDK by adding the @Log annotation to the Employee class. Afterward, we’ll add the logEmp method:

def logEmp() {
    log.info "Employee: $lastName, $firstName is of $age years age"
}

Calling the logEmp method on an Employee object will show the logs on the console:

Employee employee = new Employee(1, "Norman", "Lewis", 28)
employee.logEmp()
INFO: Employee: Lewis, Norman is of 28 years age

Similarly, the @Commons annotation is available to add Apache Commons logging support. @Log4j is available for Apache Log4j 1.x logging support and @Log4j2 for Apache Log4j 2.x. Finally, use @Slf4j to add Simple Logging Facade for Java support.

5. Conclusion

In this tutorial, we’ve explored the concept of metaprogramming in Groovy.

Along the way, we’ve seen a few notable metaprogramming features both for runtime and compile-time.

At the same time, we’ve explored additional handy annotations available in Groovy for cleaner and dynamic code.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

eBook Jackson – NPI EA – 3 (cat = Jackson)