diff --git a/spring-boot-modules/spring-boot-groovy/pom.xml b/spring-boot-modules/spring-boot-groovy/pom.xml
index 820bb0fd7a..ab1fef6865 100644
--- a/spring-boot-modules/spring-boot-groovy/pom.xml
+++ b/spring-boot-modules/spring-boot-groovy/pom.xml
@@ -27,6 +27,7 @@
org.codehaus.groovy
groovy
+ ${groovy.version}
org.springframework.boot
@@ -70,6 +71,7 @@
com.baeldung.springwithgroovy.SpringBootGroovyApplication
+ 3.0.13
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-groovy/src/main/groovy/com/baeldung/springgroovyconfig/GroovyBeanBuilder.groovy b/spring-boot-modules/spring-boot-groovy/src/main/groovy/com/baeldung/springgroovyconfig/GroovyBeanBuilder.groovy
new file mode 100644
index 0000000000..ca4e07d302
--- /dev/null
+++ b/spring-boot-modules/spring-boot-groovy/src/main/groovy/com/baeldung/springgroovyconfig/GroovyBeanBuilder.groovy
@@ -0,0 +1,23 @@
+package com.baeldung.springgroovyconfig
+
+
+beans {
+
+ // Declares a simple bean with a constructor argument
+ companyBean(Company, name: 'ABC Inc');
+
+ // The same bean can be declared using a simpler syntax: beanName(type, constructor-args)
+ company String, 'XYZ Inc'
+
+ // Declares an employee object with setters referencing the previous bean
+ employee(Employee) {
+ firstName = 'Lakshmi'
+ lastName = 'Priya'
+
+ // References to other beans can be done in both the ways
+ company = company // or vendor = ref('company')
+ }
+
+ // Allows import of other configuration files, both XML and Groovy
+ importBeans('classpath:xml-bean-config.xml')
+}
diff --git a/spring-boot-modules/spring-boot-groovy/src/main/groovy/com/baeldung/springgroovyconfig/NotificationService.groovy b/spring-boot-modules/spring-boot-groovy/src/main/groovy/com/baeldung/springgroovyconfig/NotificationService.groovy
new file mode 100644
index 0000000000..5c4ad09276
--- /dev/null
+++ b/spring-boot-modules/spring-boot-groovy/src/main/groovy/com/baeldung/springgroovyconfig/NotificationService.groovy
@@ -0,0 +1,7 @@
+package com.baeldung.springgroovyconfig;
+
+interface NotificationService {
+
+ String getMessage();
+
+}
diff --git a/spring-boot-modules/spring-boot-groovy/src/main/groovy/com/baeldung/springgroovyconfig/NotificationServiceImpl.groovy b/spring-boot-modules/spring-boot-groovy/src/main/groovy/com/baeldung/springgroovyconfig/NotificationServiceImpl.groovy
new file mode 100644
index 0000000000..ae3f0a6b58
--- /dev/null
+++ b/spring-boot-modules/spring-boot-groovy/src/main/groovy/com/baeldung/springgroovyconfig/NotificationServiceImpl.groovy
@@ -0,0 +1,6 @@
+package com.baeldung.springgroovyconfig;
+
+class NotificationServiceImpl implements NotificationService {
+
+ String message;
+}
diff --git a/spring-boot-modules/spring-boot-groovy/src/main/groovy/com/baeldung/springgroovyconfig/SpringGroovyConfiguration.groovy b/spring-boot-modules/spring-boot-groovy/src/main/groovy/com/baeldung/springgroovyconfig/SpringGroovyConfiguration.groovy
new file mode 100644
index 0000000000..54d40e1692
--- /dev/null
+++ b/spring-boot-modules/spring-boot-groovy/src/main/groovy/com/baeldung/springgroovyconfig/SpringGroovyConfiguration.groovy
@@ -0,0 +1,25 @@
+package com.baeldung.springgroovyconfig
+
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.Configuration
+
+@Configuration
+class SpringGroovyConfiguration {
+
+ public static void main(String[] args) { }
+
+ @Bean
+ List fruits() {
+ [
+ 'Apple',
+ 'Orange',
+ 'Banana',
+ 'Grapes'
+ ]
+ }
+
+ @Bean
+ Map rankings() {
+ [1: 'Gold', 2: 'Silver', 3: 'Bronze']
+ }
+}
diff --git a/spring-boot-modules/spring-boot-groovy/src/main/java/com/baeldung/springgroovyconfig/Company.java b/spring-boot-modules/spring-boot-groovy/src/main/java/com/baeldung/springgroovyconfig/Company.java
new file mode 100644
index 0000000000..477c2c13cb
--- /dev/null
+++ b/spring-boot-modules/spring-boot-groovy/src/main/java/com/baeldung/springgroovyconfig/Company.java
@@ -0,0 +1,41 @@
+package com.baeldung.springgroovyconfig;
+
+public class Company {
+
+ private String name;
+ private String contact;
+ private String type;
+
+ public Company() {
+ }
+
+ public Company(String name) {
+ super();
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getContact() {
+ return contact;
+ }
+
+ public void setContact(String contact) {
+ this.contact = contact;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-groovy/src/main/java/com/baeldung/springgroovyconfig/Employee.java b/spring-boot-modules/spring-boot-groovy/src/main/java/com/baeldung/springgroovyconfig/Employee.java
new file mode 100644
index 0000000000..3a90f98268
--- /dev/null
+++ b/spring-boot-modules/spring-boot-groovy/src/main/java/com/baeldung/springgroovyconfig/Employee.java
@@ -0,0 +1,32 @@
+package com.baeldung.springgroovyconfig;
+
+public class Employee {
+
+ private String firstName;
+ private String lastName;
+ private Company company;
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public Company getCompany() {
+ return company;
+ }
+
+ public void setCompany(Company company) {
+ this.company = company;
+ }
+}
diff --git a/spring-boot-modules/spring-boot-groovy/src/main/resources/StringJoiner.groovy b/spring-boot-modules/spring-boot-groovy/src/main/resources/StringJoiner.groovy
new file mode 100644
index 0000000000..51afa9bc06
--- /dev/null
+++ b/spring-boot-modules/spring-boot-groovy/src/main/resources/StringJoiner.groovy
@@ -0,0 +1,7 @@
+class StringJoiner {
+
+ String join(String arg1, String arg2) {
+ arg1 + arg2;
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-groovy/src/main/resources/StringJoinerScript.groovy b/spring-boot-modules/spring-boot-groovy/src/main/resources/StringJoinerScript.groovy
new file mode 100644
index 0000000000..224f5fb9a8
--- /dev/null
+++ b/spring-boot-modules/spring-boot-groovy/src/main/resources/StringJoinerScript.groovy
@@ -0,0 +1 @@
+arg1 + arg2;
diff --git a/spring-boot-modules/spring-boot-groovy/src/main/resources/groovy-xml-config.xml b/spring-boot-modules/spring-boot-groovy/src/main/resources/groovy-xml-config.xml
new file mode 100644
index 0000000000..475bd582e7
--- /dev/null
+++ b/spring-boot-modules/spring-boot-groovy/src/main/resources/groovy-xml-config.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+ package com.baeldung.springgroovyconfig;
+ import com.baeldung.springgroovyconfig.NotificationService;
+
+ class Notifier implements NotificationService {
+ String message
+ }
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-groovy/src/test/java/com/baeldung/springgroovyconfig/SpringGroovyConfigUnitTest.java b/spring-boot-modules/spring-boot-groovy/src/test/java/com/baeldung/springgroovyconfig/SpringGroovyConfigUnitTest.java
new file mode 100644
index 0000000000..3059773d59
--- /dev/null
+++ b/spring-boot-modules/spring-boot-groovy/src/test/java/com/baeldung/springgroovyconfig/SpringGroovyConfigUnitTest.java
@@ -0,0 +1,144 @@
+package com.baeldung.springgroovyconfig;
+
+import static org.assertj.core.api.Assertions.fail;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.BeansException;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.context.support.GenericGroovyApplicationContext;
+import org.springframework.util.ResourceUtils;
+
+import groovy.lang.Binding;
+import groovy.lang.GroovyObject;
+import groovy.util.GroovyScriptEngine;
+
+class SpringGroovyConfigUnitTest {
+
+ private static final String FILE_NAME = "GroovyBeanBuilder.groovy";
+ private static final String FILE_PATH = "src/main/groovy/com/baeldung/springgroovyconfig/";
+
+ @Test
+ void givenGroovyConfigFile_whenCalledWithBeanName_thenReturnCompanyBean() {
+ try (GenericGroovyApplicationContext ctx = new GenericGroovyApplicationContext()) {
+ ctx.load("file:" + getPath(FILE_PATH) + FILE_NAME);
+ ctx.refresh();
+
+ Company company = (Company) ctx.getBean("companyBean");
+
+ assertEquals("ABC Inc", company.getName());
+ } catch (BeansException | IllegalStateException e) {
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ void givenGroovyConfigFile_whenCalledWithRefBean_thenReturnEmployeeBean() {
+ try (GenericGroovyApplicationContext ctx = new GenericGroovyApplicationContext()) {
+ ctx.load("file:" + getPath(FILE_PATH) + FILE_NAME);
+ ctx.refresh();
+
+ Employee employee = ctx.getBean(Employee.class);
+
+ assertEquals("Lakshmi", employee.getFirstName());
+ assertEquals("Priya", employee.getLastName());
+ assertEquals("XYZ Inc", employee.getCompany()
+ .getName());
+ } catch (BeansException | IllegalStateException e) {
+ fail(e.getMessage());
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ void givenGroovyFileWithSpringAnnotations_whenCalledWithBeanName_thenReturnValidBean() {
+
+ try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();) {
+ ctx.register(SpringGroovyConfiguration.class);
+ ctx.refresh();
+
+ List fruits = (List) ctx.getBean("fruits");
+
+ assertNotNull(fruits);
+ assertTrue(fruits.size() == 4);
+ assertEquals("Apple", fruits.get(0));
+ } catch (BeansException | IllegalStateException e) {
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ void givenGroovyBeanConfiguredInXml_whenCalledWithBeanName_thenReturnValidBean() {
+ try (ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("groovy-xml-config.xml")) {
+ ctx.refresh();
+
+ NotificationService notifier = (NotificationService) ctx.getBean("notification");
+
+ assertEquals("Hello", notifier.getMessage());
+ } catch (BeansException | IllegalStateException e) {
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ void givenGroovyBeanConfiguredAsInlineScript_whenCalledWithBeanName_thenReturnValidBean() {
+ try (ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("groovy-xml-config.xml")) {
+ ctx.refresh();
+
+ NotificationService notifier = (NotificationService) ctx.getBean("notifier");
+
+ assertEquals("Have a nice day!", notifier.getMessage());
+ } catch (BeansException | IllegalStateException e) {
+ fail(e.getMessage());
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ void givenGroovyScript_whenCalledWithScriptEngine_thenReturnsResult() {
+ try {
+ GroovyScriptEngine engine = new GroovyScriptEngine(ResourceUtils.getFile("file:src/main/resources/")
+ .getAbsolutePath(), this.getClass().getClassLoader());
+ Class joinerClass = engine.loadScriptByName("StringJoiner.groovy");
+ GroovyObject joiner = joinerClass.getDeclaredConstructor()
+ .newInstance();
+ Object result = joiner.invokeMethod("join", new Object[] { "Mr.", "Bob" });
+
+ assertEquals("Mr.Bob", result.toString());
+ } catch (Exception e) {
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ void givenGroovyScript_whenCalledWithBindingObject_thenReturnsResult() {
+ try {
+ GroovyScriptEngine engine = new GroovyScriptEngine(ResourceUtils.getFile("file:src/main/resources/")
+ .getAbsolutePath(), this.getClass().getClassLoader());
+ Binding binding = new Binding();
+ binding.setVariable("arg1", "Mr.");
+ binding.setVariable("arg2", "Bob");
+ Object result = engine.run("StringJoinerScript.groovy", binding);
+
+ assertEquals("Mr.Bob", result.toString());
+ } catch (Exception e) {
+ fail(e.getMessage());
+ }
+ }
+
+ private String getPath(String filePath) {
+ String pathPart = new File(".").getAbsolutePath();
+ pathPart = pathPart.replace(".", "");
+ pathPart = pathPart.replace("\\", "/");
+ pathPart = pathPart + filePath;
+
+ return pathPart;
+ }
+
+}