- Spring MVC Cookbook
- Alex Bretet
- 2874字
- 2021-07-16 13:03:18
Defining the project structure with Maven
In this recipe, we will focus on defining, with Maven, the project structure we need for our application.
Getting ready
We will initially create two Eclipse projects: one for the application and one for the components that ZipCloud as a company could share later on with other projects. Take a look at the following image which presents the project components that we are going to build:

The application project cloudstreetmarket-parent will have three modules. Two of them will be packaged as web archives (war): the main web application and the REST API. One of them will be packaged as a jar dependency (cloudstreetmarket-core).
The company-specific project zipcloud-parent will have only one submodule—zipcloud-core, which will be packaged as jar.
How to do it...
The following steps will help us create a Maven parent project:
- From Eclipse, navigate to File | New | Other.
- A New wizard opens up wherein you can select the type of project within a hierarchy. Then, open the Maven category, select Maven Project, and click on Next.
The New Maven Project wizard opens as shown in the following screenshot:
- Make sure to check the Create a simple project option. Click on Next.
- Fill up the next wizard as follows:
edu.zipcloud.cloudstreetmarket
as Group Idcloudstreetmarket-parent
as Artifact Id0.0.1-SNAPSHOT
as Versionpom
as PackagingCloudStreetMarket Parent
as Name- Then, click on the Finish button
The parent project must appear in the package explorer on the left-hand side of the dashboard.
We now have to tell m2eclipse which Java compiler version you plan to use in this project so that it automatically adds the right JRE system library to the submodules we are about to create. This is done through the
pom.xml
file. - Edit
pom.xml
file to specify the Java compiler version:- Double-click on the pom.xml file. The m2eclipse Overview tab shows up by default. You have to click on the last tab, pom.xml, to access the full XML definition.
- In this definition, add the following block at the end but still as part of the <project> node. (You can also copy/paste this piece of code from the cloudstreetmarket-parent's
pom.xml
of thechapter_1
source code):<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.8</source> <target>1.8</target> <verbose>true</verbose> <fork>true</fork> <executable>${JAVA_HOME}/bin/javac</executable> <compilerVersion>1.8</compilerVersion> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.4.2</version> <configuration> <jvm>${JAVA_HOME}/bin/java</jvm> <forkMode>once</forkMode> </configuration> </plugin> </plugins> </build>
Note
You have probably noticed the maven-surefire-plugin declaration as well. We will review it soon; it allows us to run unit tests during the build.
- Now, we will create submodules:
As submodules of the Parent project, we have seen that we needed one web module to handle and render the site's screens, one web module for the REST API, and one other module that will be used to package all the business logic (services, data access, and so on.) specific to the first product
cloudstreetmarket.com
:- From the main Webapp module:in Eclipse, navigate to File | New | Other. A New wizard opens up through which you can select the type of project within a hierarchy. Open the Maven category, select Maven Module, and click on Next.
- The New Maven Module wizard opens up after this; fill it up as follows:
Check Create a simple project.
Enter
cloudstreetmarket-webapp
as Module Name.Enter
cloudstreetmarket-parent
as Parent project. - Click on the Next button after which the next step shows up. Enter the following entries in that new window:
Enter
edu.zipcloud.cloudstreetmarket
as Group Id.Enter
0.0.1-SNAPSHOT
as Version.Select war as Packaging.
Enter
CloudStreetMarket Webapp
as Name.Then click on the Finish button.
- Now we will go ahead to create and REST API module:
We are going to repeat the previous operation with different parameters.
- From Eclipse, navigate to File | New | Other. The selection wizard pops up when you go there. After this, open the Maven category, select Maven Module, and click on Next:
- In the New Maven Module wizard, enter the following entries:
Check the Create a simple project option.
Enter
cloudstreetmarket-api
as Module Name.Enter
cloudstreetmarket-parent
as Parent project. - Click on the Next button to proceed to the next step. Enter the following entries in that window:
Enter
edu.zipcloud.cloudstreetmarket
as Group Id.Enter
0.0.1-SNAPSHOT
as Version.Select war as Packaging.
Enter
CloudStreetMarket API
as Name.Then click on the Finish button.
- Now, we will create the core module:
For this, navigate to File | New | Other. The selection wizard pops up when you do so. Open the Maven category, select Maven Module, and click on Next.
- In the New Maven Module wizard, enter the following entries:
Check the Create a simple project option.
Enter
cloudstreetmarket-core
as Module Name.Enter
cloudstreetmarket-parent
as Parent project. - Click on the Next button to go to the next step. Fill in the fields with the following:
Enter
edu.zipcloud.cloudstreetmarket
as Group Id.Enter
0.0.1-SNAPSHOT
as Version.This time, select jar as Packaging.
Enter
CloudStreetMarket Core
as Name.Then click on the Finish button.
If you have the Java perspective activated (in the top-right corner), you should see the overall created structure matching the screenshot here:
- In the New Maven Module wizard, enter the following entries:
- Now, we will create a company-specific project and its module(s):
Let's assume that many different categories of dependencies (core, messaging, reporting, and so on…) will be part of the company-business project later.
- We need a parent project, so from Eclipse, navigate to File | New | Other. The selection wizard pops up. Open the Maven category, select Maven Project, and click on Next.
- In the first step of the New Maven Project wizard, as for the Parent project we created earlier, only check the Create a simple Project and Use default workspace location options.
- Click on the Next button and fill in the next wizard as follows:
Enter
edu.zipcloud
as Group Id.Enter
zipcloud-parent
as Artifact Id.Enter
0.0.1-SNAPSHOT
as Version.Select pom as Packaging.
Enter
ZipCloud Factory Business Parent
as Name.
Again, in the created
pom.xml
file, add the following block inside the<project>
node to create the underlying modules properly and to enable automatic test execution. (You can also copy/paste this piece of code from the zipcloud-parent's pom.xml file of the chapter_1 source code):<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.8</source> <target>1.8</target> <verbose>true</verbose> <fork>true</fork> <executable>${JAVA_HOME}/bin/javac</executable> <compilerVersion>1.8</compilerVersion> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.4.2</version> <configuration> <jvm>${JAVA_HOME}/bin/java</jvm> <forkMode>once</forkMode> </configuration> </plugin> </plugins> </build>
Now, we are going to create one company-business core module, which will be a sub module of the parent project we just created.
For this, navigate to File | New | Other. The selection wizard pops up. Open the Maven category, select Maven Module, and click on Next.
- In the New Maven Module wizard, enter the following details:
Check the Create a simple project option.
Enter
zipcloud-core
as Module Name.Enter
zipcloud-parent
as Parent project. - Click on the Next button and go to the next step. Here, enter the following details:
Enter
edu.zipcloud
as Group Id.Enter
0.0.1-SNAPSHOT
as Version.Select jar as Packaging.
Select
ZipCloud Factory Core Business
as Name.
- Now, build the two projects:
If the structure is correct, the following Maven command could be successfully run:
mvn clean install
Tip
This command can be launched in the terminal if Maven is installed on the development machine.
In our study case, we will, for now, launch it using the m2eclipse modified Run As menu: Right click on the zipcloud-parent project and click on Run As | Maven Clean.
Note
In the Maven console, you should now see this beautiful line at the bottom:
[INFO] BUILD SUCCESS
Now, repeat the operation for the install build phase. You should now see the following output in the console:
[INFO] ZipCloud Parent .......................SUCCESS [ 0.313 s] [INFO] ZipCloud Core .........................SUCCESS [ 1.100 s] [INFO] ---------------------------------------------------------- [INFO] BUILD SUCCESS [INFO] ----------------------------------------------------------
Ok, now you should be able to build
cloudstreetmarket-parent
as well.For this, right-click on the cloudstreetmarket -parent project and click on Run As | Maven Clean. The Maven console should print the following after this step:
[INFO] BUILD SUCCESS
Again, right-click on the cloudstreetmarket -parent project and click on Run As | Maven Install. The Maven console should now print the following:
[INFO] CloudStreetMarket Parent ..............SUCCESS [ 0.313 s] [INFO] CloudStreetMarket Webapp ..............SUCCESS [ 6.129 s] [INFO] CloudStreetMarket Core ................SUCCESS [ 0.922 s] [INFO] CloudStreetMarket API .................SUCCESS [ 7.163 s] [INFO] ---------------------------------------------------------- [INFO] BUILD SUCCESS [INFO] ----------------------------------------------------------
Scrolling up a bit should display the following trace:
------------------------------------------------------- T E S T S ------------------------------------------------------- There are no tests to run. Results : Tests run: 0, Failures: 0, Errors: 0, Skipped: 0
Note
Maven here, with the help of the maven-surefire-plugin, which we manually added, parses all the classes encountered in the
src/test/java
directories. Again, this path can be customized.In the detected test classes, Maven will also run the methods annotated with the JUnit
@Test
annotation. A JUnit dependency is required in the project.
How it works...
In this section, we are going through quite a few concepts about Maven so that you can better understand its standards.
New Maven project, new Maven module
The project creation screens we just went through also come from the m2eclipse plugin. These screens are used to initialize a Java project with a preconfigured pom.xml
file and a standard directory structure.
The m2eclipse plugin also provides a set of shortcuts to run Maven build phases and some handy tabs (already seen) to manage project dependencies and visualize the pom.xml
configuration.
The standard project hierarchy
Navigating through the created projects, you should be able to notice a recurring hierarchy made of the following directories: src/main/java
, src/main/resource
, src/test/java
, and src/test/resource
. This structure is the default structure that Maven drives us through. This model has become a standard nowadays. But, we can still override it (in the pom.xml
files) and create our own hierarchy.
If you remember the maven-compiler-plugin definition added in the pom.xml
files of the parent projects, there were the following four lines of code that we used:
<verbose>true</verbose> <fork>true</fork> <executable>${JAVA_HOME}/bin/javac</executable> <compilerVersion>1.8</compilerVersion>
These lines allow Maven to use an external JDK for the compiler. It is better to have control over which compiler Maven uses, especially when managing different environments.
Also, there were the following two lines that might look like an over configuration:
<source>1.8</source> <target>1.8</target>
From a strict Maven point of view, these lines are optional when an external JDK is defined with a specified compilerVersion. Initially, with these two lines, we can control which Java version we want the default code to be compiled in. When maintaining older systems, the existing code might still compile in a previous version of Java.
Actually, m2eclipse specifically expects these two lines in order to add JRE System Library [JavaSE-1.8]
to the build path of the jar
and war
modules. Now, with these lines, Eclipse compiles these projects in the same way Maven does: in Java SE 8.
Tip
If this dependency still shows up as a different version of Java, you may need to right-click on the module and then navigate to Maven | Update Project.
The project's structure in the IDE
About the parent projects in the Eclipse project hierarchy; did you notice that the created submodules seem duplicated as standalone projects and as direct children of the parent? This is due to the fact that Eclipse doesn't handle hierarchies of projects yet in Luna. For this reason, the modules appear as separated projects. It might be slightly confusing because the source code appears to be located beside the parent projects. This is not the case in reality, it is only the way they are rendered, so we can have all the tools normally bound to the project level.
Note
At this time, JetBRAINS IntelliJ IDEA already supports visual hierarchies of the projects.
Finally, if you open a parent project's pom.xml
file, you should see the <modules>
node populated with the created submodules. This has been done automatically as well by m2eclipse. We recommend that you keep an eye on this feature because m2eclipse doesn't always update these <modules>
nodes depending on which way you alter the project hierarchy.
Maven's build life cycles
A build life cycle in Maven is a specific sequence (and a group) of predefined operations called phases. There are three existing life cycles in Maven: default, clean, and site.
Let's have a look at all the phases that include the default and clean life cycles (probably the life cycles the most commonly used by developers).
The clean life cycle
The Maven clean phase plays a central role. It resets a project build from Maven's perspective. It is usually about deleting the target directory that is created by Maven during the build process. Here are some details about the phases included in the clean life cycle. These details come from the Maven documentation:
The default life cycle
In the default life cycle, you can find the most interesting build phases that deal with source generation, compilation, resource handling, tests, integration tests, and artefact deployment. Here are some details about the phases included in the default life cycle:
Plugin goals
With the concept of plugins, Maven acquires a much wider dimension. Maven natively provides built-in plugins, but external plugins can be introduced just as other dependencies (identified by groupIds and artefactIds).
Each build phase can be attached to zero, one, or more plugin goals. A goal represents a specific and concrete task responsible for building or handling a project in a certain manner. Some phases have goals bound to them, by default, through native plugins.
Built-in life cycle bindings
Now that we have seen the purpose of each phase in the presented two life cycles, we must say that, for the default life cycle, depending upon which module packaging type we are choosing, only some of these phases are potentially activated for goal execution.
Let's see the phases that we skipped in the default life cycle for different packaging types:
Tip
In Chapter 9, Testing and Troubleshooting, we will practically bind external plugins goals to identified build phases.
In summary, calling: mvn clean install on a jar packaged-module will result in executing the following phases: clean, process-resources, compile, process-test-resources, test-compile, test, package, and install.
About Maven commands
When Maven is told to execute one or more phases targeting a specific project's pom.xml
file, it will execute the requested phase(s) for each of its modules.
Then, for every single requested phase, Maven will do the following:
- Identify which life cycle the phase belongs to
- Look for the packaging of the current module and identify the right life cycle binding
- Execute all the phases in the hierarchy of the identified life cycle bindings, which are located before the requested phase in the hierarchy
Note
By the term execute all the phases, we mean execute all the underlying detected and attached plugin goals (native plugins or not).
In summary, calling mvn clean install
on a jar
packaged module will execute the following phases: clean
, process-resources
, compile
, process-test-resources
, test-compile
, test
, package
, and install
.
There's more...
You may wonder why we have created these projects and modules in regard to our application.
How did we choose the jar module's name?
About the Maven structure, the best names for nondeployable modules often emphasize a functional purpose, a specific concept created by the business, or are driven by the product (cloudstreetmarket-chat, cloudstreetmarket-reporting, cloudstreetmarket-user-management, and so on.). This strategy makes the dependency management easier because we can infer whether a new module requires another module or not. Thinking about controllers, services, and DAO layers at a macro scale doesn't really make sense at this stage, and it could lead to design interference or circular dependencies. These technical subcomponents (service, DAO, and so on) will be present or not, as needed, in each functional module as Java packages but not as JAR-packaged dependencies.
How did we choose the names for deployable modules?
Choosing a name for a deployable module (war
) is a bit different different from choosing a name for a JAR-packaged module. The deployable archive must be thought of as scalable and potentially load balanced. It is fair to assume that the requests that will target the application to retrieve HTML contents can be distinguished from the ones that will return REST contents.
With this assumption, in our case it has been our wish to split the war
into two. Doing so may raise the question of how the web sessions are maintained between the two webapps. We will answer this point later on.
Why did we create core modules?
We created the core modules, firstly, because it is certain that, in the cloudstreetmarket
application and also in the company-shared project, we will have POJOs, exceptions, constants, enums, and some services that will be used horizontally by almost all the modules or applications. If a concept is specific to a created functional module, it must not be part of core modules.
Then, it is probably better to start big grained to refine later rather than thinking about modules that may be implemented differently or even not implemented at all. In our case, we are a start-up, and it is not silly to say that the 5 to 10 features we are going to implement can constitute the core business of this application.
See also...
- We also recommend that you install Code Style Formatters. Triggered from the Save Event, we have, with these formatters, the ability to restyle our code automatically with a uniform predefinition. Having such formatters in a team is much appreciated since it guarantees the same rendering while comparing two files with a versioning tool.
- Web程序設(shè)計(jì)及應(yīng)用
- DevOps with Kubernetes
- 深度學(xué)習(xí)經(jīng)典案例解析:基于MATLAB
- PostgreSQL Replication(Second Edition)
- Hands-On Swift 5 Microservices Development
- Android移動(dòng)開(kāi)發(fā)案例教程:基于Android Studio開(kāi)發(fā)環(huán)境
- Python入門很輕松(微課超值版)
- uni-app跨平臺(tái)開(kāi)發(fā)與應(yīng)用從入門到實(shí)踐
- R的極客理想:量化投資篇
- 大規(guī)模語(yǔ)言模型開(kāi)發(fā)基礎(chǔ)與實(shí)踐
- JavaScript Concurrency
- 體驗(yàn)之道:從需求到實(shí)踐的用戶體驗(yàn)實(shí)戰(zhàn)
- 少年小魚的魔法之旅:神奇的Python
- 米思齊實(shí)戰(zhàn)手冊(cè):Arduino圖形化編程指南
- R for Data Science Cookbook