官术网_书友最值得收藏!

  • 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:

  1. From Eclipse, navigate to File | New | Other.
  2. 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:

  3. Make sure to check the Create a simple project option. Click on Next.
  4. Fill up the next wizard as follows:
    • edu.zipcloud.cloudstreetmarket as Group Id
    • cloudstreetmarket-parent as Artifact Id
    • 0.0.1-SNAPSHOT as Version
    • pom as Packaging
    • CloudStreetMarket 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.

  5. 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 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>
      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.

  6. 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:

    1. 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.
    2. 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.

    3. 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.

  7. Now we will go ahead to create and REST API module:

    We are going to repeat the previous operation with different parameters.

    1. 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:
    2. 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.

    3. 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.

  8. 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.

    1. 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.

    2. 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:

  9. 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.

    1. 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.
    2. 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.
    3. 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.

    1. 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.

    2. 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.

  10. 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.
主站蜘蛛池模板: 义马市| 安庆市| 哈密市| 师宗县| 阜新| 宁武县| 云梦县| 丹寨县| 临夏市| 翁牛特旗| 怀化市| 灌云县| 太和县| 定陶县| 澄迈县| 泗水县| 福建省| 方城县| 清河县| 龙井市| 黄梅县| 文登市| 调兵山市| 邵武市| 兴隆县| 邯郸市| 贵阳市| 法库县| 环江| 沙坪坝区| 凌源市| 和田县| 金门县| 邛崃市| 菏泽市| 绥阳县| 化德县| 梅州市| 黄大仙区| 惠水县| 扎兰屯市|