Spring Webflux Multi-Module


In this technical post, we will review how to build a multi-module project using Gradle and Maven with Spring Boot. NOTE: If you need to know what tools you need to have installed in your computer in order to create a Spring Boot basic project, please refer my previous post: Spring Boot. Let’s start creating a new Spring Boot project with Webflux as a library:

spring init --dependencies=webflux,lombok --build=gradle --language=java library

Here is the complete build.gradle generated:

plugins {
  id 'org.springframework.boot' version '2.5.5' apply false
  id 'io.spring.dependency-management' version '1.0.11.RELEASE'
  id 'java'
}

group = 'com.jos.dem.springboot.module.library'
sourceCompatibility = 16

configurations {
  compileOnly {
    extendsFrom annotationProcessor
  }
}

repositories {
  mavenCentral()
}

dependencies {
  implementation 'org.springframework.boot:spring-boot-starter-webflux'
  compileOnly 'org.projectlombok:lombok'
  annotationProcessor 'org.projectlombok:lombok'
  testImplementation 'org.springframework.boot:spring-boot-starter-test'
  testImplementation 'io.projectreactor:reactor-test'
}

dependencyManagement {
  imports{
    mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES
  }
}

test {
  useJUnitPlatform()
}

Since we are creating a library here, we want Spring Boot’s dependency management to be used in this project without applying some Spring Boot’s features, therefore we will use: apply false. SpringBootPlugin provides a BOM_COORDINATES to handle project’s versions and dependencies. Let’s create a service so we can use it in our application module.

package com.jos.dem.springboot.module.library.service;

import com.jos.dem.springboot.module.library.config.LibraryProperties;
import lombok.RequiredArgsConstructor;
import reactor.core.publisher.Mono;

import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Value;

@Service
@RequiredArgsConstructor
public class MessageService {

  private final LibraryProperties libraryProperties;

  public Mono<String> getMessage(){
    return Mono.just(libraryProperties.getMessage());
  }

}

Next, let’s create an application project, from the $PROJECT_HOME:

spring init --dependencies=webflux,lombok --build=gradle --language=java application

This is the build.gradle file generated:

plugins {
  id 'org.springframework.boot' version '2.5.5'
  id 'io.spring.dependency-management' version '1.0.11.RELEASE'
  id 'java'
}

group = 'com.jos.dem.springboot.module'
version = '1.0.0-SNAPSHOT'
sourceCompatibility = 16

configurations {
  compileOnly {
    extendsFrom annotationProcessor
  }
}

repositories {
  mavenCentral()
}

dependencies {
  implementation 'org.springframework.boot:spring-boot-starter-webflux'
  implementation project(':library')
  compileOnly 'org.projectlombok:lombok'
  annotationProcessor 'org.projectlombok:lombok'
  testImplementation 'org.springframework.boot:spring-boot-starter-test'
  testImplementation 'io.projectreactor:reactor-test'
}

test {
  useJUnitPlatform()
}

That’s it, we are importing our library as project dependency using Gradle implementation. Now it is time to create a controller and Spring Boot Application files.

package com.jos.dem.springboot.module.controller;

import lombok.RequiredArgsConstructor;
import reactor.core.publisher.Mono;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import com.jos.dem.springboot.module.library.service.MessageService;

@RestController
@RequiredArgsConstructor
public class ModuleController {

  private final MessageService messageService;

  @GetMapping("/")
  public Mono<String> index(){
    return messageService.getMessage();
  }

}

Spring Boot application:

package com.jos.dem.springboot.module;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ModuleApplication {

  public static void main(String[] args) {
    SpringApplication.run(ModuleApplication.class, args);
  }

}

We need to provide a message for the service in the library by using $PROJECT_HOME/application/src/main/resources/application.yml.

app:
  message: 'Hello World!'

Finally add a settings.gradle to the root directory to drive our build at the top level.

rootProject.name = 'spring-boot-module'

include 'library'
include 'application'

To run the project from $PROJECT_HOME:

gradle build :application:bootRun

Using Maven

You can do the same using Maven, the only difference is that you need to specify --build=maven parameter in the spring init command line. This is the library pom file generated:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.jos.dem.springboot.module</groupId>
  <artifactId>library</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.5</version>
    <relativePath /> <!-- lookup parent from repository -->
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>16</java.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

</project>

Here is the application pom 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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.jos.dem.springboot.module</groupId>
  <artifactId>application</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.5</version>
    <relativePath /> <!-- lookup parent from repository -->
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>16</java.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    <dependency>
      <groupId>com.jos.dem.springboot.module</groupId>
      <artifactId>library</artifactId>
      <version>1.0.0-SNAPSHOT</version>
    </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>

And this is our pom.xml in the $PROJECT_HOME directory.

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.jos.dem.springboot.module</groupId>
  <artifactId>spring-boot-module</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <packaging>pom</packaging>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.5</version>
    <relativePath /> <!-- lookup parent from repository -->
  </parent>

  <modules>
    <module>library</module>
    <module>application</module>
  </modules>

</project>

Build the library first with mvn install and then run the application. From $PROJECT_HOME:

mvn install spring-boot:run -pl application

To see the results go to this URL in your browser:

 http://localhost:8080/

To browse the project go here, to download the project:

git clone git@github.com:josdem/spring-boot-modules.git

Return to the main article