C-Sharp | Java | Python | Swift | GO | WPF | Ruby | Scala | F# | JavaScript | SQL | PHP | Angular | HTML
Spring Boot Multi-Module ProjectMulti-Module ProjectA Spring Boot project that contains nested maven projects is called the multi-module project. In the multi-module project, the parent project works as a container for base maven configurations. In other words, a multi-module project is built from a parent pom that manages a group of submodules. Or A multi-module project is defined by a parent POM referencing one or more submodules. The parent maven project must contain the packaging type pom that makes the project as an aggregator. The pom.xml file of the parent project consists the list of all modules, common dependencies, and properties that are inherited by the child projects. The parent pom is located in the project's root directory. The child modules are actual Spring Boot projects that inherit the maven properties from the parent project. When we run the multi-module project, all the modules are deployed together in an embedded Tomcat Server. We can deploy an individual module, also. Parent POMThe parent POM defines the Group ID, Artifact ID, version, and packaging. In the previous Maven projects, we have seen that the parent POM defines the packaging jar. But in the multi-module project, the parent POM defines the packaging pom. The packaging pom refers to other Maven projects. Why we need multi-module projectSplitting the project into multiple modules is useful and easy to maintain. We can also easily edit or remove modules in the project without affecting the other modules. It is useful when we required to deploy modules individually. We only need to specify all the dependencies in the parent pom. All the other modules share the same pom, so we need not to specify the same dependency in each module separately. It makes the code easier to keep in order with a big project. Child module-ear, war, and jarThe child module may be any project and can have any packaging. We are free to create any type of dependency between modules and bundles together. For example, we are creating an EAR (Enterprise ARchive), WAR (Web ARchive), and JAR (Java ARchive) file. A JAR file is bundled into a war file that is bundled into an EAR file. The EAR file is the final package that is ready to deploy on the application server. The EAR file contains one or many WAR files. Each WAR file contains the service project that has common code to all WAR files and packaging type in the JAR. Maven child projects/ modules
Multi-Module Project Directory StructureLet's understand the multi-module project directory structure. In the following image, we have created a project named spring-boot-multi-module-project. It contains the parent pom at the bottom of the directory. After that, we have created two Maven Modules named module1 and module2, respectively. These two modules contain their own pom files. Let's open the parent POM and see what it configures when we create Maven modules in the project. pom.xml <?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>2.2.2.BUILD-SNAPSHOT</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.TheDeveloperBlog</groupId> <artifactId>spring-boot-example</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring-boot-multi-module-project</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <packaging>pom</packaging> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.1.RELEASE</version> <type>pom</type> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> </repository> <repository> <id>spring-snapshots</id> <name>Spring Snapshots</name> <url>https://repo.spring.io/snapshot</url> <snapshots> <enabled>true</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> </pluginRepository> <pluginRepository> <id>spring-snapshots</id> <name>Spring Snapshots</name> <url>https://repo.spring.io/snapshot</url> <snapshots> <enabled>true</enabled> </snapshots> </pluginRepository> </pluginRepositories> <modules> <module>module1</module> <module>module2</module> </modules> </project> The above pom file is same as we have seen in the previous examples. But in this pom file, two things to be noticed: packaging and modules. When we create multi-module project, we need to configure packaging pom in the parent pom file instead of jar. <packaging>pom</packaging> When we create Maven Modules in the project, Spring Boot automatically configures the modules in the parent pom inside the module tag, as shown below. <modules> <module>module1</module> <module>module2</module> </modules> Now, we are going to see what inside the pom file of module1. pom.xml <?xml version="1.0"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.TheDeveloperBlog</groupId> <artifactId>spring-boot-multi-module-project</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <groupId>com.TheDeveloperBlog</groupId> <artifactId>module1</artifactId> <version>0.0.1-SNAPSHOT</version> <name>module1</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> </project> Here, a point to be noticed that the above pom file does not contain the common dependencies like starter-web, web-mvc, etc. It inherits all the common dependencies and the properties from the parent pom. Spring Boot Multi-Module Project ExampleLet' create an example of a multi-module application.
Application Module The application module is the main module of the project. It contains the application class in which the main method is defined that is necessary to run the Spring Boot Application. It also contains application configuration properties, Controller, views, and resources. The Application Module includes Model Module, Service Implementation Module as dependency that contains Model Module, Repository Module, and Service API module. Model Module The Model Module contains Entities and Visual Objects to be used in the project. Repository Module The Repository module contains repositories to be used in the project. It depends on the Model Module. Service API Module The Service API module contains all project services. It also depends on Model Module. Service Implementation Module The Service Implementation module implements the service. It depends on Repository Module and Service API Module. POM Aggregator (Parent POM)The parent pom contains all the application modules. It also includes all the common dependencies and properties that are needed by more than one module. Dependencies are defined without version because the project has defined the Spring IO Platform as a parent. Let's understand the structure of the multi-module application that we have created. Spring-boot-multimodule ├── pom.xml │ └── REDME.adoc ├── application │ ├── pom.xml │ └── src │ └── main │ ├── java │ │ └── sample │ │ └── multimodule │ │ ├── SampleWebJspApplication.java │ │ └── web │ │ └── WelcomeController.java │ └── resources │ ├── application.properties │ └── templates │ └── welcome │ └── show.html ├── model │ ├── pom.xml │ └── src │ └── main │ └── java │ └── sample │ └── multimodule │ └── domain │ └── entity │ └── Account.java | ├── repository │ ├── pom.xml │ └── src │ └── main │ └── java │ └── sample │ └── multimodule │ └── repository │ └── AccountRepository.java ├── service-api │ ├── pom.xml │ └── src │ └── main │ └── java │ └── sample │ └── multimodule │ └── service │ └── api │ ├── AccountNotFoundException.java │ └── AccountService.java └── service-impl ├── pom.xml └── src └── main └── java └── sample └── multimodule └── service └── impl └── AccountServiceImpl.java Step 1: Create a Maven Project with the name spring-boot-multimodule. Step 2: Open the pom.xml (parent pom) file and change the packaging type jar to pom. pom.xml (parent pom) <?xml version="1.0" encoding="UTF-8" standalone="no"?> <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> <!-- Spring IO Platform is the parent of the generated application to be able to use Spring Boot and all its default configuration --> <parent> <groupId>io.spring.platform</groupId> <artifactId>platform-bom</artifactId> <version>2.0.1.RELEASE</version> </parent> <groupId>sample.multimodule</groupId> <artifactId>sample.multimodule</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>pom</packaging> <name>Parent - Pom Aggregator</name> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!-- Spring Boot dependencies --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> </project> One thing to be noticed in the above pom file is that there is no maven module configured because we have not created yet. Now we will create Maven Modules one by one that we have specified above. Step 3: Create a Maven Module with the name application. Step 4: Open the pom.xml file of application module and ensure that the packaging type is jar. pom.xml <?xml version="1.0" encoding="UTF-8" standalone="no"?> <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> <parent> <groupId>sample.multimodule</groupId> <artifactId>sample.multimodule</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>sample.multimodule.application</artifactId> <packaging>jar</packaging> <name>Project Module - Application</name> <dependencies> <!-- Project modules --> <dependency> <groupId>sample.multimodule</groupId> <artifactId>sample.multimodule.service.impl</artifactId> <version>${project.version}</version> </dependency> <!-- Spring Boot dependencies --> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> </dependencies> <build> <plugins> <!-- Spring Boot plugins --> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> Step 5: Create the main class. It is the class that is to be run. SampleWebJspApplication.java package sample.multimodule; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.SpringApplication; import org.springframework.boot.orm.jpa.EntityScan; @SpringBootApplication public class SampleWebJspApplication { public static void main(String[] args) throws Exception { SpringApplication.run(SampleWebJspApplication.class, args); } } Step 6: Create a Controller class with the name WelocameController under the package smaple.multimodule.web. WelcomeController.java package sample.multimodule.web; import java.util.Date; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import sample.multimodule.domain.entity.Account; import sample.multimodule.service.api.AccountService; @Controller public class WelcomeController { @Value("${application.message:Hello World}") private String message = "Hello World"; @Autowired protected AccountService accountService; @RequestMapping("/") public String welcome(Map<String, Object> model) { // Trying to obtain 23 account Account account = accountService.findOne("23"); if(account == null){ // If there's some problem creating account, return show view with error status model.put("message", "Error getting account!"); model.put("account", ""); return "welcome/show"; } // Return show view with 23 account info String accountInfo = "Your account number is ".concat(account.getNumber()); model.put("message", this.message); model.put("account", accountInfo); return "welcome/show"; } @RequestMapping("foo") public String foo(Map<String, Object> model) { throw new RuntimeException("Foo"); } } Step 7: Create a HTML file with the name show.html under the folder src/main/resource -> templates ->welcome. show.html <!DOCTYPE HTML> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>Spring Boot Multimodule</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <div> <b>Message: </b> <span th:text="${message}" /> </div> <div> <b>Your account: </b> <span th:text="${account}" /> </div> </body> </html> Step 8: Open theapplication.properties file, configure the application message and thymeleaf cache to false. application.properties # Application messages application.message = Hello User! dummy.type = type-inside-the-war # Spring Thymeleaf config spring.thymeleaf.cache = false After creating all the above files, the application module directory looks like the following: Let's create the second module that is model. Step 9: Create a Maven Module with the name model. Step 10: Open the pom.xml file of model module and ensure that the packaging type is jar. pom.xml <?xml version="1.0" encoding="UTF-8" standalone="no"?> <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> <parent> <groupId>sample.multimodule</groupId> <artifactId>sample.multimodule</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>sample.multimodule.model</artifactId> <packaging>jar</packaging> <name>Project Module - Model</name> <description>Module that contains all Entities and Visual Objects to be used in the project. It doesn't have any dependencies. </description> </project> Step 11: Create a class with the name Account under the package sample.multimodule.domain.entity. Account.java package sample.multimodule.domain.entity; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; @Entity public class Account { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String number; private String type; private String creditCardNumber; /** * Create an empty account. */ public Account() { } /** * Create a new account. * * @param number * the account number * @param id * the account id */ public Account(Long id, String number) { this.number = number; this.id = id; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getCreditCardNumber() { return creditCardNumber; } public void setCreditCardNumber(String creditCardNumber) { this.creditCardNumber = creditCardNumber; } } After creating all the above files, the model module directory looks like the following: Let's create the third module that is the repository. Step 12: Create a Maven Module with the name repository. Step 13: Open the pom.xml file of application module and ensure that the packaging type is jar. pom.xml <?xml version="1.0" encoding="UTF-8" standalone="no"?> <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> <parent> <groupId>sample.multimodule</groupId> <artifactId>sample.multimodule</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>sample.multimodule.repository</artifactId> <packaging>jar</packaging> <name>Project Module - Repository</name> <description>Module that contains all repositories to be used in the project. Depends of Model Module.</description> <dependencies> <!-- Project modules --> <dependency> <groupId>sample.multimodule</groupId> <artifactId>sample.multimodule.model</artifactId> <version>${project.version}</version> </dependency> <!-- Spring Boot dependencies --> <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <scope>runtime</scope> </dependency> </dependencies> </project> Step 14: Create a class with the name AccountRepositoryunder the package sample.multimodule.repository. AccountRepository.java package sample.multimodule.repository; import org.springframework.data.domain.*; import org.springframework.data.repository.*; import org.springframework.stereotype.Repository; import sample.multimodule.domain.entity.Account; @Repository public interface AccountRepository extends CrudRepository<Account, Long> { Account findByNumber(String number); } After creating all the above files, the repository module directory looks like the following: Let's create the fourth module that is service-api. Step 15: Create a Maven Module with the name service-api. Step 16: Open the pom.xml file of application service-api and ensure that the packaging type is jar. pom.xml <?xml version="1.0" encoding="UTF-8" standalone="no"?> <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> <parent> <groupId>sample.multimodule</groupId> <artifactId>sample.multimodule</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>sample.multimodule.service.api</artifactId> <packaging>jar</packaging> <name>Project Module - Service API</name> <description>Module that contains API of all project services. Depends of Model Module.</description> <dependencies> <!-- Project Modules --> <dependency> <groupId>sample.multimodule</groupId> <artifactId>sample.multimodule.model</artifactId> <version>${project.version}</version> </dependency> </dependencies> </project> Step 17: Create a package with the name sample.multimodule.service.api. Step 18: Create a class with the name AccountNotFoundException. It handles the exception if the account is not found. AccountNotFoundException.java package sample.multimodule.service.api; public class AccountNotFoundException extends RuntimeException { private static final long serialVersionUID = -3891534644498426670L; public AccountNotFoundException(String accountId) { super("No such account with id: " + accountId); } } Step 19: Create a class with the name AccountService. It provides the service related to account, such as find and create an account. AccountService.java package sample.multimodule.service.api; import java.util.List; import sample.multimodule.domain.entity.Account; public interface AccountService { /** * Finds the account with the provided account number. * * @param number The account number * @return The account * @throws AccountNotFoundException If no such account exists. */ Account findOne(String number) throws AccountNotFoundException; /** * Creates a new account. * @param number * @return created account */ Account createAccountByNumber(String number); } After creating all the above files, the service-api module directory looks like the following: Step 20: Create a Maven Module with the name service-impl. Step 21: Open the pom.xml file of application service-impl and ensure that the packaging type is jar. pom.xml <?xml version="1.0" encoding="UTF-8" standalone="no"?> <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> <parent> <groupId>sample.multimodule</groupId> <artifactId>sample.multimodule</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>sample.multimodule.service.impl</artifactId> <packaging>jar</packaging> <name>Project Module - Service Implementation</name> <description>Module that contains services implementation defined on Service API module. Depends of Repository Module and Service API Module.</description> <dependencies> <!-- Project Modules --> <dependency> <groupId>sample.multimodule</groupId> <artifactId>sample.multimodule.repository</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>sample.multimodule</groupId> <artifactId>sample.multimodule.service.api</artifactId> <version>${project.version}</version> </dependency> </dependencies> </project> Step 22: Create a class with the name AccountServiceImpl under the package sample.multimodule.service.impl. AccountServiceImpl.java package sample.multimodule.service.impl; import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import sample.multimodule.domain.entity.Account; import sample.multimodule.repository.AccountRepository; import sample.multimodule.service.api.AccountService; import sample.multimodule.service.api.AccountNotFoundException; @Service public class AccountServiceImpl implements AccountService { @Value("${dummy.type}") private String dummyType; @Autowired private AccountRepository accountRepository; /** * {@inheritDoc} * <p/> * Dummy method for testing purposes. * * @param number The account number. Set 0000 to get an {@link AccountNotFoundException} */ @Override public Account findOne(String number) throws AccountNotFoundException { if(number.equals("0000")) { throw new AccountNotFoundException("0000"); } Account account = accountRepository.findByNumber(number); if(account == null){ account = createAccountByNumber(number); } return account; } @Override public Account createAccountByNumber(String number) { Account account = new Account(); account.setNumber(number); return accountRepository.save(account); } public String getDummyType() { return dummyType; } public void setDummyType(String dummyType) { this.dummyType = dummyType; } } After creating all the above files, the service-impl module directory looks like the following: Now open the parent pom file, we see that all the Maven Modules that we have created is configured in the parent pom inside the Now ensure that all the five modules are created, as shown below: After creating all the modules, the main project directory looks like the following: Step 23: Now run the SampleWebJspApplication.java file as Java Application. Step 24: Open the browser and invoke the URL http://localhost:8080. It returns the Message and account number that is 23.
Next TopicSpring Boot Packaging
|