Every Spring Boot project has several modules and each module match some application layer (service layer, repository layer, web layer, model, etc..). In this article, let us see a maven-driven project as an example for showing Project Modules.
Example
pom.xml (overall project level)
XML
<? xml version = "1.0" encoding = "UTF-8" standalone = "no" ?> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 < 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 >gfg.multimodule</ groupId > < artifactId >gfg.multimodule</ artifactId > < version >0.0.1-SNAPSHOT</ version > < packaging >pom</ packaging > < name >Parent - Pom Aggregator</ name > < description >This pom is a maven aggregator that contains all application modules. Also, include all common dependencies needed by more than one module. Dependencies are defined without version because this project has defined Spring IO Platform as parent.</ description > < properties > < java.version >1.6</ java.version > </ properties > < modules > < module >model</ module > < module >repository</ module > < module >service-api</ module > < module >service-impl</ module > < module >application</ module > </ modules > < 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 > |
This sample project is containing the below modules
application (Here the starting point of the spring boot application is kept and it will also contain rendering pages)
Let us see the key important files
GfgWebJspApplication.java
Java
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class GfgWebJspApplication { public static void main(String[] args) throws Exception { SpringApplication.run(GfgWebJspApplication. class , args); } } |
WelcomeController.java
Java
import gfg.multimodule.domain.entity.SalaryAccount; import gfg.multimodule.service.api.EmployeeAccountService; 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; @Controller public class WelcomeController { @Value ( "${application.message:Hello World}" ) private String message = "Hello World" ; @Autowired protected EmployeeAccountService accountService; @RequestMapping ( "/" ) public String welcome(Map<String, Object> model) { // Trying to obtain for account 100 SalaryAccount account = accountService.findOne( "100" ); 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 100 account info // As a sample checking for account number 100 and // if available it will get redirected to show.html String accountInfo = "Your account number is " .concat( account.getEmployeeAccountNumber()); 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" ); } } |
show.html (Once the account is available, we can see the output from show.html)
HTML
<!DOCTYPE HTML> < head > < title >Spring Boot Multimodule</ title > < meta http-equiv = "Content-Type" content = "text/html; charset=UTF-8" /> < style > #orangerow { background:orange; font:17 px "Times New Roman", serif; color:#000000; } #yellowrow { background:yellow; font:17 px "Times New Roman", serif; color:#000000; } </ style > </ head > < body > < table border = "1" > < tr id = "yellowrow" > < td > Welcome Message </ td > < td > Account Details </ td > </ tr > < tr id = "orangerow" > < td > < span th:text = "${message}" /> </ td > < td > < span th:text = "${account}" /> </ td > </ tr > </ table > </ body > </ html > |
For this application folder, we need to have a specific pom.xml that will show the dependencies for this module
application -> pom.xml
XML
<? xml version = "1.0" encoding = "UTF-8" standalone = "no" ?> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 < modelVersion >4.0.0</ modelVersion > < parent > < groupId >gfg.multimodule</ groupId > < artifactId >gfg.multimodule</ artifactId > < version >0.0.1-SNAPSHOT</ version > </ parent > < artifactId >gfg.multimodule.application</ artifactId > < packaging >jar</ packaging > < name >Project Module - Application</ name > < description >This is the main module of the project. It contains Application.java class, that contains main method, necessary to run Spring Boot applications. It contains all necessary application configuration properties. It contains all web controllers, views and web resources. It include Service Implementation module as dependency that contains Model Module, Repository Module and Service Api Module. </ description > < dependencies > <!-- Project modules --> < dependency > < groupId >gfg.multimodule</ groupId > < artifactId >gfg.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 > |
Next module that we are going to see as a model (Here we will be having our POJO/entity classes and their related details).
SalaryAccount.java
Java
import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class SalaryAccount { @Id @GeneratedValue (strategy = GenerationType.AUTO) // data members. Their getter and setter private Long employeeAccountId; private String employeeAccountNumber; private String type; private String creditCardNumber; public SalaryAccount() {} public SalaryAccount(Long employeeAccountId, String employeeAccountNumber) { this .employeeAccountNumber = employeeAccountNumber; this .employeeAccountId = employeeAccountId; } public Long getEmployeeAccountId() { return employeeAccountId; } public void setEmployeeAccountId(Long employeeAccountId) { this .employeeAccountId = employeeAccountId; } public String getEmployeeAccountNumber() { return employeeAccountNumber; } public void setEmployeeAccountNumber(String employeeAccountNumber) { this .employeeAccountNumber = employeeAccountNumber; } 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; } } |
model -> pom.xml
XML
<? xml version = "1.0" encoding = "UTF-8" standalone = "no" ?> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 < modelVersion >4.0.0</ modelVersion > < parent > < groupId >gfg.multimodule</ groupId > < artifactId >gfg.multimodule</ artifactId > < version >0.0.1-SNAPSHOT</ version > </ parent > < artifactId >gfg.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 > |
Next, let us see the repository module. This is the module where we can have our CRUD-related operations interface can be written. Usually, it will have searching/inserting/updating/deleting
SalaryAccountRepository.java
Java
import gfg.multimodule.domain.entity.SalaryAccount; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; @Repository // It should extends CrudRepository as we will have useful // methods related to CRUD operations public interface SalaryAccountRepository extends CrudRepository<SalaryAccount, Long> { SalaryAccount findByEmployeeAccountNumber( String employeeAccountNumber); } |
pom.xml
XML
<? xml version = "1.0" encoding = "UTF-8" standalone = "no" ?> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 < modelVersion >4.0.0</ modelVersion > < parent > < groupId >gfg.multimodule</ groupId > < artifactId >gfg.multimodule</ artifactId > < version >0.0.1-SNAPSHOT</ version > </ parent > < artifactId >gfg.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 >gfg.multimodule</ groupId > < artifactId >gfg.multimodule.model</ artifactId > < version >${project.version}</ version > </ dependency > <!-- Spring Boot dependencies --> <!-- Should specify the database that we are using --> < dependency > < groupId >org.hsqldb</ groupId > < artifactId >hsqldb</ artifactId > < scope >runtime</ scope > </ dependency > </ dependencies > </ project > |
The next one is service-api. Business logic-related methods are introduced here
Important key files
EmployeeAccountService.java
Java
package gfg.multimodule.service.api; import gfg.multimodule.domain.entity.SalaryAccount; import java.util.List; public interface EmployeeAccountService { SalaryAccount findOne(String number) throws EmployeeAccountNotFoundException; SalaryAccount createAccountByEmployeeAccountNumber(String number); } |
EmployeeAccountNotFoundException.java
Java
public class EmployeeAccountNotFoundException extends RuntimeException { private static final long serialVersionUID = -3891534644498421234L; public EmployeeAccountNotFoundException( String accountId) { super ( "No such account with id: " + accountId); } } |
The final one is the implementation one. The service-impl This will implement the service methods (business logic requirements) and real implementation is getting done here
EmployeeAccountServiceImpl.java
Java
import gfg.multimodule.domain.entity.SalaryAccount; import gfg.multimodule.repository.SalaryAccountRepository; import gfg.multimodule.service.api.EmployeeAccountService; import gfg.multimodule.service.api.EmployeeAccountNotFoundException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @Service public class EmployeeAccountServiceImpl implements EmployeeAccountService { @Value ( "${dummy.type}" ) private String dummyType; @Autowired private SalaryAccountRepository accountRepository; @Override public SalaryAccount findOne(String employeeAccountNumber) throws EmployeeAccountNotFoundException { if (employeeAccountNumber.equals( "0000" )) { throw new EmployeeAccountNotFoundException( "0000" ); } SalaryAccount salaryAccount = accountRepository.findByEmployeeAccountNumber( employeeAccountNumber); if (salaryAccount == null ) { salaryAccount = createAccountByEmployeeAccountNumber( employeeAccountNumber); } return salaryAccount; } @Override public SalaryAccount createAccountByEmployeeAccountNumber( String employeeAccountNumber) { SalaryAccount account = new SalaryAccount(); account.setEmployeeAccountNumber( employeeAccountNumber); return accountRepository.save(account); } public String getDummyType() { return dummyType; } public void setDummyType(String dummyType) { this .dummyType = dummyType; } } |
Now let us see how to run the whole project. “mvn clean install” has to be given where the project got hosted (parent folder where pom,xml) present
Now we need to step ahead to the application folder and execute it as mvn spring-boot:run
On successful startup (Tomcat dependency is given and it is running on port 8080)
http://localhost:8080/
We can able to see as
By combining different project modules, as a maven project, we need to include all the modules and all the required dependencies need to be given, then the whole project is run successfully and we will get the output as above.