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
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Using ZK With Spring Boot
  • Actuator Enhancements: Spring Framework 6.2 and Spring Boot 3.4
  • How Spring Boot Starters Integrate With Your Project
  • A Practical Guide to Creating a Spring Modulith Project

Trending

  • Beyond Manual Annotation: Engineering Self-Correcting Pseudo-Labeling Pipelines
  • Build a GitHub Slack Bot With AWS Bedrock and MCP, Part 2
  • How to Interpret the Number of Spring ApplicationContexts in Integration Tests
  • Operationalizing Enterprise AI at Scale: Architecture, Governance, and Adoption
  1. DZone
  2. Coding
  3. Frameworks
  4. Multiple Spring Boot Applications in the Same Project

Multiple Spring Boot Applications in the Same Project

I frequently use the Spring Boot framework in my demos. The latest one shows how to achieve CQRS using two different code paths including Spring Data JPA and jOOQ

By 
Nicolas Fränkel user avatar
Nicolas Fränkel
·
Dec. 06, 21 · Tutorial
Likes (1)
Comment
Save
Tweet
Share
8.1K Views

Join the DZone community and get the full member experience.

Join For Free

I frequently use the Spring Boot framework in my demos. The latest one is no different. It shows how to achieve CQRS using two different code paths:

  • the command part is implemented via Spring Data JPA
  • the query part via jOOQ

My use case is a banking application that offers a REST layer allowing clients to call any parts. Demoing the query part is easy enough with curl as the URL is not complex:

Shell
 
curl localhost:8080/balance/123          // 1


  1. Query the balance of the account 123

On the other hand, creating a new operation, e.g., a credit, requires passing data to curl. While it's feasible to do that, the payload's structure itself is complex as it's part JSON. Hence, it's a risk to use curl to demo the command part in front of a live audience. I tend to avoid unnecessary risks, so I thought about a few alternatives.

My first idea was to prepare the command in advance, to copy-paste during the demo. I wanted to have a couple of different operations, so I'd have to:

  • Either prepare a single command, paste it and change it before running it
  • Or prepare all the different commands

To me, both were too awkward.

Another option was to create another @SpringBootApplication annotated class in the same project:

Java
 
@SpringBootApplication
public class GeneratorApplication {

    @Bean
    public CommandLineRunner run() {
        var template = new RestTemplate();
        return args -> {
            var operation = generateRandomOperation();            // 1
            LongStream.range(0, Long.parseLong(args[0]))          // 2
                .forEach(
                    operation -> template.postForObject(          // 3
                        "http://localhost:8080/operation",
                        operation,
                        Object.class));
        };
    }

    public static void main(String[] args) {
        new SpringApplicationBuilder(GeneratorApplication.class)
                .run(args);
    }
}


  1. Generate a random Operation, somehow
  2. Get the number of calls from the argument
  3. Call the URL of the main web application

When I launched this application after the other web one, it failed. There are two reasons for that:

  1. Both applications share the same Maven POM. As the spring-boot-starter-web is on the classpath, the generator application tries to launch Tomcat. It fails because the first application did bind the default port.
  2. Spring Boot relies on component scanning by default. Hence, the web application scans the generator application and its declared beans and creates them. It's possible to redefine some of the beans this way. However, the web app also creates the CommandLineRunner bean above. It thus "posts to itself" while its server is not ready yet.

The most straightforward solution is to move each application's classes in their own dedicated Maven module. You need to create a POM in each module with only the necessary dependencies. Plus, I need to use a couple of classes in the runner from the web app. While I could duplicate them in the other module, it's extra work and complexity.

To prevent classpath scanning, we move each application class into its package. Note that it doesn't work when packages have a parent-child relationship: they must be siblings.

To create beans of specific classes, we need to rely on particular annotations depending on their nature:

  • For JPA entities, @EntityScan, pointing to the package to scan
  • For JPA repositories, @EnableJpaRepositories, pointing to the package to scan
  • For other classes, @Import points to the classes to generate bean from

The final step is to prevent the generator application from launching the webserver. You can configure it when launching the application.

Java
 
@SpringBootApplication
@EnableJpaRepositories("org.hazelcast.cqrs")                      // 1
@EntityScan("org.hazelcast.cqrs")                                 // 2
public class GeneratorApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(GeneratorApplication.class)
                .web(WebApplicationType.NONE)                     // 3
                .run(args);
    }

    // Command-line runner
}


  1. Scan for JPA repositories
  2. Scan for JPA entities
  3. Prevent the webserver from launching

It works as expected, and we can finally reap the benefits of the setup.

For real-world applications, you'll probably create a self-executing JAR. You'll need to develop a way to configure the main class during the build, one for each application. I think it's better not to do it and keep this as a demo hack.

To go further:

  • Create a Non-web Application
  • Use Spring Data Repositories
  • Separate @Entity Definitions from Spring Configuration
  • Importing Additional Configuration Classes
Spring Framework Web application Spring Boot

Published at DZone with permission of Nicolas Fränkel. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Using ZK With Spring Boot
  • Actuator Enhancements: Spring Framework 6.2 and Spring Boot 3.4
  • How Spring Boot Starters Integrate With Your Project
  • A Practical Guide to Creating a Spring Modulith Project

Partner Resources

×

Comments

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

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

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 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook