#
Create a multi-module application (Spring Boot & Maven)
This tutorial explains how we can create a multi-module Spring Boot Java application with Maven. My example will contain a simple service and a library which is used by the service.
The steps for creating a such a project are:
- Create a Root Project
- Create the Library Module
- Create the Service Module
#
Prerequisites
Java and Maven are already installed.
#
Create a Root Project
1. Create the folder which will keep the project
This folder in my case is "D:\1-maven-lab\MyApplication". This is considered the root folder.
2. Add the pom.xml file
POM = Project Object Model
The pom.xml file
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.multi-module-app</groupId>
<artifactId>my-application</artifactId>
<version>0.1.0</version>
<packaging>pom</packaging>
<modules>
<module>library</module>
<module>service</module>
</modules>
</project>
Info
- library and service are not yet defined and are the library and the service which are the modules of this project.
<packaging>pom</packaging>
: "pom" is used for marking this directory as "root".- this POM file from the root is named Super POM.
3. Create the directory structure
In "D:\1-maven-lab\MyApplication" we create 2 directory : "library" and "service".
#
Create the Library Module
1. Create the folder for the library
In "D:\1-maven-lab\MyApplication\library" we create the following directories: src -> main -> java -> com -> example -> mylib.
2. Add/Create the pom.xml file
In "D:\1-maven-lab\MyApplication\library" I add the following pom.xml file:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>library</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>library</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Info
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
this block is not needed in pom.xml because the library will not be executable.
3. Create a service to read a value from application.properties
package com.example.mylib;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("custom")
public class ServiceProperties {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Info
The library is created to read from the application.properties of the service/application which use this library. Generally, the library has no values in application.properties, but sometimes could have.
4. Create the main service in the library
package com.example.mylib;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Service;
@Service
@EnableConfigurationProperties(ServiceProperties.class)
public class MyService {
private final ServiceProperties serviceProperties;
public MyService(ServiceProperties serviceProperties) {
this.serviceProperties = serviceProperties;
}
public String message() {
return this.serviceProperties.getMessage();
}
}
Info
@EnableConfigurationProperties(ServiceProperties.class)
force the creation of ServiceProperties Bean.
5. Create a simple unit test for MyService
For this we need to create the package (see the class definition) and we will add the following code:
package com.example.mylib;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest("custom.message=Hello")
public class MyServiceTest {
@Autowired
private MyService myService;
@Test
public void contextLoads() {
assertThat(myService.message()).isNotNull();
}
@SpringBootApplication
static class TestConfiguration {
}
}
5. Test the library
Run the command mvn clean install
in "D:\1-maven-lab\MyApplication\library".
When I run it I receive the following result:
D:\1-maven-lab\MyApplication\library>mvn clean install
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------< com.example:library >-------------------------
[INFO] Building library 0.0.1-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- clean:3.3.2:clean (default-clean) @ library ---
[INFO] Deleting D:\1-maven-lab\MyApplication\library\target
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ library ---
[INFO] skip non existing resourceDirectory D:\1-maven-lab\MyApplication\library\src\main\resources
[INFO] skip non existing resourceDirectory D:\1-maven-lab\MyApplication\library\src\main\resources
[INFO]
[INFO] --- compiler:3.11.0:compile (default-compile) @ library ---
[INFO] Changes detected - recompiling the module! :source
[INFO] Compiling 2 source files with javac [debug release 21] to target\classes
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ library ---
[INFO] skip non existing resourceDirectory D:\1-maven-lab\MyApplication\library\src\test\resources
[INFO]
[INFO] --- compiler:3.11.0:testCompile (default-testCompile) @ library ---
[INFO] Changes detected - recompiling the module! :dependency
[INFO] Compiling 1 source file with javac [debug release 21] to target\test-classes
[INFO]
[INFO] --- surefire:3.1.2:test (default-test) @ library ---
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.example.mylib.MyServiceTest
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.2.0)
2024-01-11T22:40:44.141+02:00 INFO 19136 --- [ main] com.example.mylib.MyServiceTest : Starting MyServiceTest using Java 21 with PID 19136 (started by Catalin in D:\1-maven-lab\MyApplication\library)
2024-01-11T22:40:44.144+02:00 INFO 19136 --- [ main] com.example.mylib.MyServiceTest : No active profile set, falling back to 1 default profile: "default"
2024-01-11T22:40:44.578+02:00 INFO 19136 --- [ main] com.example.mylib.MyServiceTest : Started MyServiceTest in 0.665 seconds (process running for 1.419)
WARNING: A Java agent has been loaded dynamically (C:\Users\Catalin\.m2\repository\net\bytebuddy\byte-buddy-agent\1.14.10\byte-buddy-agent-1.14.10.jar)
WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning
WARNING: If a serviceability tool is not in use, please run with -Djdk.instrument.traceUsage for more information
WARNING: Dynamic loading of agents will be disallowed by default in a future release
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.832 s -- in com.example.mylib.MyServiceTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] --- jar:3.3.0:jar (default-jar) @ library ---
[INFO] Building jar: D:\1-maven-lab\MyApplication\library\target\library-0.0.1-SNAPSHOT.jar
[INFO]
[INFO] --- install:3.1.1:install (default-install) @ library ---
[INFO] Installing D:\1-maven-lab\MyApplication\library\pom.xml to C:\Users\Catalin\.m2\repository\com\example\library\0.0.1-SNAPSHOT\library-0.0.1-SNAPSHOT.pom
[INFO] Installing D:\1-maven-lab\MyApplication\library\target\library-0.0.1-SNAPSHOT.jar to C:\Users\Catalin\.m2\repository\com\example\library\0.0.1-SNAPSHOT\library-0.0.1-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.292 s
[INFO] Finished at: 2024-01-11T22:40:45+02:00
[INFO] ------------------------------------------------------------------------
All looks good so far.
#
Create the Service Module
Now let's create the main service.
1. Create the folder for the library
In "D:\1-maven-lab\MyApplication\service" we create the following directories: src -> main -> java -> com -> example -> service.
2. Add/Create the pom.xml file
In "D:\1-maven-lab\MyApplication\service" I add the following pom.xml file:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>service</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>library</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Info
As you can see, the dependency on the library is added.
3. Add the main class
Here is the main class for the main service:
package com.example.service;
import com.example.mylib.MyService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication(scanBasePackages = "com.example")
@RestController
public class DemoApplication {
private final MyService myService;
public DemoApplication(MyService myService) {
this.myService = myService;
}
@GetMapping("/")
public String home() {
return myService.message();
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
4. Add in application.properties some content
In application.properties file I added the following content:
custom.message=Hello Everybody !
#
Test the project
When I run "http://localhost:8080" in the browser I need to see "Hello Everybody !" on the browser.
Info
Using this multi-modules project, with Maven, we can build the modules ("library" and "service" in my case) with one command
mvn install
or mvn clean install
in the root directory ("D:\1-maven-lab\MyApplication" in my example).