DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • How to Use Java to Build Single Sign-on
  • Distributed Tracing System (Spring Cloud Sleuth + OpenZipkin)
  • Java, Spring Boot, and MongoDB: Performance Analysis and Improvements
  • How To Build Web Service Using Spring Boot 2.x

Trending

  • Monolith: The Good, The Bad and The Ugly
  • Endpoint Security Controls: Designing a Secure Endpoint Architecture, Part 1
  • Next Evolution in Integration: Architecting With Intent Using Model Context Protocol
  • Building Resilient Networks: Limiting the Risk and Scope of Cyber Attacks
  1. DZone
  2. Coding
  3. Frameworks
  4. How to Use Java Modules to Build a Spring Boot Application

How to Use Java Modules to Build a Spring Boot Application

Learn about how to use the newly introduced Java Platform Module System in Java 9 to create a Spring Boot application.

By 
Bruno Leite user avatar
Bruno Leite
·
Sep. 23, 20 · Tutorial
Likes (3)
Comment
Save
Tweet
Share
21.2K Views

Join the DZone community and get the full member experience.

Join For Free

The most well known and reliable development language is Java—which recently shifted to a 6-month release schedule. This allows the user to have access to more frequent updates to the language. Among other things, the modular system was recently introduced in Java 9.

JPMS, or the Java Platform Module System, adds two foundational capabilities when building Java apps.

The first is reliable configuration, which replaces the class-path mechanism, allowing program components to declare explicit dependencies upon each other.

The second is strong encapsulation, which lets components declare which public types are accessible to others, and which aren’t. 

Modules house grouped packages, serving as building blocks within larger programs. The declaration of a module specifies which other modules are required to compile and run code; packages, classes, and interfaces. 

Visibility modifiers such as public, private, protected, and default aren't enough for external visibility. For example, a “Ulti” class can be used throughout a library for different packages within a JAR file, however, it is not meant for use outside of the library. With JPMS, you won’t have to worry about this type of situation.

Table of Contents 

  • Introduction
  • Install a Java 9+ JDK
  • Project Structure
    • How to Structure a Modular Project with Maven
  • Build an Application Without Java Modules
    • Create the Persistence Module
    • Create the Web Application Module
  • Secure Your Web Application
    • Register an Application on Okta
    • Configure the App with Okta Information
  • Using Java Modules
    • Modularize the persistence Library
    • Modularize the application Project
  • Running the Application
  • Learning More About Java Modular System


Introduction

When Java 9 was created, the JDK went under a major refactoring to modularize its content. It created various modules to organize the contents. Some examples include: java.base, java.sql, and java.xml(along with many others). To date, there are a total of 60 modules in Java 14 JDK.

java.base has fundamental classes like Object, String, Integer, Double, etc. While java.sql has classes related to accessing the JDBC API like ResultSet, Connection and others. Additionally, java.xml has classes related to XML manipulation like XMLStreamReader, XMLStreamWriter and similar classes in that vein.

The modularization enabled the possibility of reducing the Java runtime to include just the java.base if your application only depends on this module. By using the jlink tool that is bundled with the JDK, you can create a micro runtime with only the JDK modules you need. This post won’t cover how to use jlink—as it is not the focus—but you can see an example on this Baeldung article.

For the rest of this article, you should have at least some basic understanding of Spring Boot, Maven, and REST web services principles as well as Docker installed on your machine.

Install a Java 9+ JDK

First, you’ll need a Java 9+ JDK in order to use modules. If you have been using Java 8, you’ll likely have to download a separate JDK with a version of 9 or later to be used for this tutorial. This project is set up to use JDK 11 in this tutorial. You can download the JDKs from AdoptOpenJDK. Just make sure your JAVA_HOME environment variable is pointing to that JDK.

Project Structure

In this article, we’ll be covering how to develop a simple application with two modules: the application module (that contains the web-facing classes) and the persistence module (that contains the data access layer). We’ll also be using a couple of dependencies (spring-boot-starter-data-mongodb and okta-spring-boot-starter) to illustrate how they are configured when building a modular application.

The project source code can be found at GitHub

How to Structure a Modular Project with Maven

We’re going to create this project folder structure manually to better understand it. Each module will live inside a separate directory and have its own pom.xml file. There will also be a pom.xml on the project root that will serve as the parent pom for the modules. Create the following folder structure:

Java
 




x
12


 
1
.
2
├── application
3
│   ├── pom.xml
4
│   └── src
5
│       └── main
6
│           ├── java
7
│           └── resources
8
├── persistence
9
│   └── src
10
│       └── main
11
│           └── java
12
└── pom.xml



First, let’s define the root pom.xml. It contains the common <parent> indication to spring-boot-started-parent and two entries in the <module> section. These entries are the names of the directories for the modules we are developing. Please note that they are specific to Maven and denote sub-projects, having nothing to do with the Java modules that we’ll be working on later.

Java
 




xxxxxxxxxx
1
24


 
1
<?xml version="1.0" encoding="UTF-8"?>
2
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4
    <modelVersion>4.0.0</modelVersion>
5
    <parent>
6
        <groupId>org.springframework.boot</groupId>
7
        <artifactId>spring-boot-starter-parent</artifactId>
8
        <version>2.3.1.RELEASE</version>
9
        <relativePath/> <!-- lookup parent from repository -->
10
    </parent>
11
    <groupId>com.okta.developer</groupId>
12
    <artifactId>spring-boot-with-modules</artifactId>
13
    <version>0.0.1-SNAPSHOT</version>
14
    <packaging>pom</packaging>
15

          
16
    <properties>
17
        <java.version>11</java.version>
18
    </properties>
19

          
20
    <modules>
21
        <module>application</module>
22
        <module>persistence</module>
23
    </modules>
24
</project>



The persistence module will have a pom.xml like the one below and point to the parent pom.xml that we defined earlier. This will have a dependency on spring-data-mongo since we’ll be saving our data to a Mongo DB.


Java
 




xxxxxxxxxx
1
19


 
1
<?xml version="1.0" encoding="UTF-8"?>
2
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4
    <modelVersion>4.0.0</modelVersion>
5
    <parent>
6
        <groupId>com.okta.developer</groupId>
7
        <artifactId>spring-boot-with-modules</artifactId>
8
        <version>0.0.1-SNAPSHOT</version>
9
    </parent>
10
    <artifactId>spring-boot-with-modules-persistence</artifactId>
11

          
12
    <dependencies>
13
        <dependency>
14
            <groupId>org.springframework.boot</groupId>
15
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
16
        </dependency>
17
    </dependencies>
18
</project>



Finally, the application module will have a pom.xml (below), pointing to the parent pom.xml (above). It will also have a dependency on spring-boot-starter-web— as we’ll be creating REST endpoints on it—and a dependency on our persistence module:

Java
x
33
 
1
<?xml version="1.0" encoding="UTF-8"?>
2
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4
    <modelVersion>4.0.0</modelVersion>
5
    <parent>
6
        <groupId>com.okta.developer</groupId>
7
        <artifactId>spring-boot-with-modules</artifactId>
8
        <version>0.0.1-SNAPSHOT</version>
9
    </parent>
10
    <artifactId>spring-boot-with-modules-app</artifactId>
11
12
    <dependencies>
13
        <dependency>
14
            <groupId>org.springframework.boot</groupId>
15
            <artifactId>spring-boot-starter-web</artifactId>
16
        </dependency>
17
        <dependency>
18
            <groupId>com.okta.developer</groupId>
19
            <artifactId>spring-boot-with-modules-persistence</artifactId>
20
            <version>${project.version}</version>
21
        </dependency>
22
    </dependencies>
23
24
    <build>
25
        <plugins>
26
            <plugin>
27
            <groupId>org.springframework.boot</groupId>
28
                <artifactId>spring-boot-maven-plugin</artifactId>
29
            </plugin>
30
        </plugins>
31
    </build>
32
33
</project>


To compile the project, run mvn compile from the project root.

NOTE: Don’t confuse Maven modules with Java Modules.

  • Maven modules are used to separate a project into multiple sub-projects. The main project will have a pom.xml referencing sub-projects in the <modules> section. Each sub-project will have its own pom.xml. When building the main project, it will automatically build the sub-projects too.

  • Java modules is another name for JPMS (Java Platform Module System), it was added in JDK 9 under the name Project Jigsaw. It allows applications (packaged as JAR or WAR) to define a module-info.java. This special module-info file contains a set of directives which define its dependencies and which classes are exported for use by other libraries.

Build an Application Without Java Modules

The presence of module-info.java in an application’s source root defines that it is using Java modules. You will first build the application without JPMS and enable it in a later step.

Create the Persistence Module

Create a class Bird in the persistence module in persistence/src/main/java/com/okta/developer/animals/bird/Bird.java. This will represent the entity that we’ll be saving to DB.

Java
 




xxxxxxxxxx
1
36


 
1
package com.okta.developer.animals.bird;
2

          
3
import org.springframework.data.annotation.Id;
4

          
5
public class Bird {
6

          
7
    @Id
8
    private String id;
9

          
10
    private String specie;
11
    private String size;
12

          
13
    public String getId() {
14
        return id;
15
    }
16

          
17
    public void setId(String id) {
18
        this.id = id;
19
    }
20

          
21
    public String getSpecie() {
22
        return specie;
23
    }
24

          
25
    public void setSpecie(String specie) {
26
        this.specie = specie;
27
    }
28

          
29
    public String getSize() {
30
        return size;
31
    }
32

          
33
    public void setSize(String size) {
34
        this.size = size;
35
    }
36
}



Now we’re going to create a repository to save this entity to DB. Spring Data MongoDB does this for us automatically, creating the CRUD operations so all we have to create is an interface extending MongoRepository. Create this class in persistence/src/main/java/com/okta/developer/animals/bird/BirdRepository.java:

Java
 




xxxxxxxxxx
1


 
1
package com.okta.developer.animals.bird;
2

          
3
import org.springframework.data.mongodb.repository.MongoRepository;
4

          
5
public interface BirdRepository extends MongoRepository<Bird, String> {
6
}



Finally, for the persistence module. We’ll be creating a service class to expose the persistence operations in persistence/src/main/java/com/okta/developer/animals/bird/BirdPersistence.java:

Java
 




xxxxxxxxxx
1
35


 
1
package com.okta.developer.animals.bird;
2

          
3
import org.springframework.beans.factory.annotation.Autowired;
4
import org.springframework.stereotype.Component;
5

          
6
import javax.annotation.PostConstruct;
7
import java.util.List;
8

          
9
@Component
10
public class BirdPersistence {
11

          
12
    private BirdRepository birdRepository;
13

          
14
    @Autowired
15
    public BirdPersistence(BirdRepository birdRepository) {
16
        this.birdRepository = birdRepository;
17
    }
18

          
19
    @PostConstruct
20
    void postConstruct(){
21
        Bird sampleBird = new Bird();
22
        sampleBird.setSpecie("Hummingbird");
23
        sampleBird.setSize("small");
24
        save(sampleBird);
25
    }
26

          
27
    public void save(Bird bird) {
28
        birdRepository.save(bird);
29
    }
30

          
31
    public List<Bird> get() {
32
        return birdRepository.findAll();
33
    }
34
}
35

          



Create the Web Application Module

In the application module, create the main application class application/src/main/java/com/okta/developer/SpringBootModulesApplication.java annotated with @SpringBootApplication:

Java
 




xxxxxxxxxx
1
14


 
1
package com.okta.developer;
2

          
3
import org.springframework.boot.SpringApplication;
4
import org.springframework.boot.autoconfigure.SpringBootApplication;
5

          
6
@SpringBootApplication
7
public class SpringBootModulesApplication {
8

          
9
    public static void main(String[] args) {
10
        SpringApplication.run(SpringBootModulesApplication.class, args);
11
    }
12
}
13

          



Add a controller to expose REST operations on the Bird classes This class will be stored on application/src/main/java/com/okta/developer/BirdController.java


Java
 




xxxxxxxxxx
1
31


 
1
package com.okta.developer;
2

          
3
import com.okta.developer.animals.bird.Bird;
4
import com.okta.developer.animals.bird.BirdPersistence;
5
import org.springframework.web.bind.annotation.GetMapping;
6
import org.springframework.web.bind.annotation.PostMapping;
7
import org.springframework.web.bind.annotation.RequestBody;
8
import org.springframework.web.bind.annotation.RestController;
9

          
10
import java.util.List;
11

          
12
@RestController
13
public class BirdController {
14

          
15
    private BirdPersistence birdPersistence;
16

          
17
    public BirdController(BirdPersistence birdPersistence) {
18
        this.birdPersistence = birdPersistence;
19
    }
20

          
21
    @GetMapping("bird")
22
    public List<Bird> getBird() {
23
        return birdPersistence.get();
24
    }
25

          
26
    @PostMapping("bird")
27
    public void saveBird(@RequestBody Bird bird) {
28
        birdPersistence.save(bird);
29
    }
30
}
31

          



At this point, the application is functional and can be run. Start a MongoDB instance using the following docker command:

docker run -p 27017:27017 mongo:3.6-xenial

Then, go to the project root and run:

mvn install && mvn spring-boot:run -pl application

If everything goes correctly, you’ll be able to navigate to http://localhost:8080/bird and see a JSON output like this:

[{"id":"5f03ff7277a08a55ae73c8b9","specie":"Hummingbird","size":"small"}]

Secure Your Web Application

Before we move on to using Java modules, it’s time to tune our app and make it secure by adding a single dependency and a couple configuration properties.

Add the following dependency to your application/pom.xml file:


Java
 




xxxxxxxxxx
1


 
1
<dependency>
2
    <groupId>com.okta.spring</groupId>
3
    <artifactId>okta-spring-boot-starter</artifactId>
4
    <version>1.3.0</version>
5
</dependency>



Register an Application on Okta

To begin, sign up for a forever-free Okta developer account.

Once you’re signed into Okta, register your client application.

  • In the top menu, click on Applications
  • Select Add Application
  • Click Web and then Next
  • Enter Spring Boot with Java Modules as the Name (this value doesn’t matter, so feel free to change it)
  • Change the Login redirect URI to http://localhost:8080/login/oauth2/code/okta
  • Click Done

Configure the App with Okta Information

Create a file application/src/main/resources/application.properties with the following content:

Java
 




xxxxxxxxxx
1


 
1
okta.oauth2.issuer=https://{yourOktaDomain}/oauth2/default
2
okta.oauth2.clientId={clientId}
3
okta.oauth2.clientSecret={clientSecret} 



You can find {clientId} and {clientSecret} on the General tab of the Okta application you just created:

Client ID and secret

You can find {yourOktaDomain} on your Okta dashboard:

Okta dashboard

If you restart the app and navigate to http://localhost:8080/bird in an incognito/private browser window, you’ll see a login page.

Using Java Modules

Now it is time to modularize the app. This is achieved by placing a file module-info.java in each project’s source root (src/main/java). We’ll be doing this for both our modules: application and persistence. There are two ways to modularize a Java app—top-down and bottom-up. In this tutorial we’ll be showing the bottom-up approach; modularizing the libraries before the app. This approach is preferable as we’ll have persistence already modularized when writing the application and module-info.java. If application was modularized first then persistence would be treated as an automatic module and you would have to use the JAR file name for the module name.

Modularize the persistence Library

Create a module declaration file persistence/src/main/java/module-info.java with the following content:

Java
 




xxxxxxxxxx
1
10


 
1
module com.okta.developer.modules.persistence {
2

          
3
    requires java.annotation;
4
    requires spring.beans;
5
    requires spring.context;
6
    requires spring.data.commons;
7
    requires spring.data.mongodb;
8

          
9
    exports com.okta.developer.animals.bird;
10
}



Each requires keyword signalizes that this module will be depending on some other module. Spring (version 5) is not modularized yet, its JAR files don’t have the module-info.java. When you have a dependency on the modulepath (formerly the classpath for non-modular applications) like this they will be available as automatic modules.

An automatic module derives its name using a two-step process:

  • If the JAR defines the Automatic-Module-Name header in its `MANIFEST.MF, then that property defines the module’s name.
  • Alternately, the JAR file name is used to determine the name. The second approach is intrinsically unstable, so no modules with a dependency on such an automatic module should be published in public Maven repositories.

In Spring 5, the Automatic-Module-Name metadata was added to all Spring libraries, if/when Spring defines module-info, the metadata in your application does NOT need to change.

The exports keyword exports all classes in that package. When another module uses a requires clause referencing that package, it will have access to the package classes.

In this example, the module is exporting all classes under the com.okta.developer.animals.bird package.

Modularize the application Project

Create a module declaration file application/src/main/java/module-info.java with the following content:

Java
 




xxxxxxxxxx
1


 
1
module com.okta.developer.modules.app {
2

          
3
    requires com.okta.developer.modules.persistence;
4

          
5
    requires spring.web;
6
    requires spring.boot;
7
    requires spring.boot.autoconfigure;
8
}



This one is similar to the first but, along with the Spring dependencies, we also have the com.okta.developer.modules.persistence dependency.

By adding the requires com.okta.developer.modules.persistence this module will have access to the package that was exported: com.okta.developer.animals.bird.

Running the Application

Go to the project root and run

mvn install && mvn spring-boot:run -pl application

If everything is operating correctly you’ll be able to login and navigate to http://localhost:8080/bird where you’ll see JSON output.

Learning More About Java Modular System

The Java Modular System is an excellent addition to the Java ecosystem. It helps organize and isolate classes that were otherwise needlessly exposed. By looking at the application module-info.java, you can see a blueprint of the application dependencies.

This topic is broad and, if you want to learn more, this talk by Alex Buckley is an excellent start.

If you have an existing Spring Boot application that you’d like to have use the modular system, this other talk by Jaap Coomans will help you out.

You can learn more about securing Spring applications in these posts:

  • A Quick Guide to OAuth 2.0 with Spring Security
  • OpenID Connect Logout Options with Spring Boot
  • Build a CRUD App with Angular 9 and Spring Boot 2.2

If you have any questions about this post, please add a comment below. For more awesome content, follow @oktadev on Twitter, like us on Facebook, or subscribe to our YouTube channel.


Java (programming language) application Spring Framework Spring Boot Build (game engine)

Published at DZone with permission of Bruno Leite. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • How to Use Java to Build Single Sign-on
  • Distributed Tracing System (Spring Cloud Sleuth + OpenZipkin)
  • Java, Spring Boot, and MongoDB: Performance Analysis and Improvements
  • How To Build Web Service Using Spring Boot 2.x

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!