Maven - So erstellen Sie ein Multi-Modul-Projekt

Maven - So erstellen Sie ein Multi-Modul-Projekt

image

In diesem Tutorial zeigen wir Ihnen, wie Sie mit Maven ein Multi-Modul-Projekt mit vier Modulen verwalten können:

  1. Passwortmodul - Nur Schnittstelle.

  2. Password md5-Modul - Implementierung des Passwortmoduls, MD5-Passwort-Hashing.

  3. Passwort sha-Modul - Implementierung des Passwortmoduls, SHA-Passwort-Hashing.

  4. Webmodul - Eine einfache MVC-Webanwendung zum Hashing einer Eingabe mit MD5- oder SHA-Algorithmus.

Die Modulabhängigkeit.

$ password

$ password <-- password-md5

$ password <-- password-sha

$ web <-- (password-md5 | password-sha) <-- password

Einige Befehle zum Erstellen eines Projekts mit mehreren Modulen, zum Beispiel:

$ mvn -pl password compile  # compile password module only

$ mvn -pl password-sha compile  # compile password-sha module, also dependency - password

$ mvn -pl web compile       # compile web module only

$ mvn -am -pl web compile   # compile web module, also dependency - password-sha or password-md5, password

$ mvn -pl web jetty:run     # run web module with Jetty

$ mvn compile           # compile everything

Verwendete Technologien:

  1. Maven 3.5.3

  2. JDK 8

  3. Spring 5.1.0.RELEASE

  4. thymeleaf 3.0.10.RELEASE

1. Verzeichnisaufbau

In einem Projektlayout mit mehreren Modulen enthält ein übergeordnetes Projekt ein übergeordnetespom.xml und jedes Untermodul (Unterprojekte) enthält auch eigenepom.xml

image

2. Maven POM

Lassen Sie sich die Maven-Multimodul-POM-Dateien ansehen.

2.1 A Parent POM file, packaging is pom.

pom.xml


    4.0.0

    
    com.example.multi
    java-multi-modules
    pom
    1.0

    
    
        web
        password
        password-sha
        password-md5
    

2.2 In password module.

password/pom.xml



    
    
        java-multi-modules
        com.example.multi
        1.0
    
    4.0.0

    
    com.example.password
    password
    1.0
    jar

2.3 In password-md5 module.

password-md5/pom.xml



    
        java-multi-modules
        com.example.multi
        1.0
    
    4.0.0

    
    com.example.password
    password-md5
    1.0
    jar

    
        
            com.example.password
            password
            1.0
        
    

2.4 In password-sha module.

password-sha/pom.xml



    
        java-multi-modules
        com.example.multi
        1.0
    
    4.0.0

    
    com.example.password
    password-sha
    1.0
    jar

    
        
            com.example.password
            password
            1.0
        
    

2.4 In web module.

web/pom.xml



    
    
        com.example.multi
        java-multi-modules
        1.0
    
    4.0.0

    
    com.example
    web
    1.0
    war

    

        

        

        
        
            com.example.password
            password-sha
            1.0
        

    

3. Übergeordnetes Projekt

image

3.1 Parent pom, those properties and dependencies (JDK 8, JUnit 5, Spring 5) are shared by all sub modules.

pom.xml


    4.0.0

    com.example.multi
    java-multi-modules
    pom
    1.0

    
        
        UTF-8
        1.8
        1.8
        5.3.1
        5.1.0.RELEASE
    

    
        web
        password
        password-sha
        password-md5
    

    

        
        
            org.springframework
            spring-context
            ${spring.version}
        

        
        
            org.junit.jupiter
            junit-jupiter-engine
            ${junit.version}
            test
        

        
            org.junit.jupiter
            junit-jupiter-params
            ${junit.version}
            test
        

    

    
        
            
                org.apache.maven.plugins
                maven-surefire-plugin
                2.22.0
            
            
                org.apache.maven.plugins
                maven-dependency-plugin
                3.1.1
            
        
    

4. Passwortmodul

4.1 A module containing only one interface.

image

4.2 POM file.

password/pom.xml


    
        java-multi-modules
        com.example.multi
        1.0
    
    4.0.0

    com.example.password
    password
    1.0
    jar

4.3 An interface.

PasswordService.java

package com.example.password;

public interface PasswordService {

    String hash(String input);

    String algorithm();

}

5. Password-md5-Modul

5.1 Password module implementation.

image

5.2 POM file.

password-md5/pom.xml


    
        java-multi-modules
        com.example.multi
        1.0
    
    4.0.0

    
    com.example.password
    password-md5
    1.0
    jar

    
        
            com.example.password
            password
            1.0
        
    

5.3 MD5 hashing.

PasswordServiceImpl.java

package com.example.password;

import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

@Service
public class PasswordServiceImpl implements PasswordService {

    @Override
    public String hash(String input) {
        return md5(input);
    }

    @Override
    public String algorithm() {
        return "md5";
    }

    private String md5(String input) {

        StringBuilder result = new StringBuilder();
        MessageDigest md;

        try {
            md = MessageDigest.getInstance("MD5");
            byte[] hashInBytes = md.digest(input.getBytes(StandardCharsets.UTF_8));

            for (byte b : hashInBytes) {
                result.append(String.format("%02x", b));
            }
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalArgumentException(e);
        }

        return result.toString();
    }
}

5.4 Unit test.

TestPasswordService.java

package com.example.password;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class TestPasswordService {

    PasswordService passwordService;

    @BeforeEach
    void init() {
        passwordService = new PasswordServiceImpl();
    }

    @DisplayName("md5 -> hex")
    @ParameterizedTest
    @CsvSource({
            "123456, e10adc3949ba59abbe56e057f20f883e",
            "hello world, 5eb63bbbe01eeed093cb22bb8f5acdc3"
    })
    void testMd5hex(String input, String expected) {
        assertEquals(expected, passwordService.hash(input));
    }

}

6. Passwort-sha-Modul

6.1 Password module implementation.

image

6.2 POM file.

password-sha/pom.xml


    
        java-multi-modules
        com.example.multi
        1.0
    
    4.0.0

    com.example.password
    password-sha
    1.0
    jar

    
        1.11
    

    

        
            commons-codec
            commons-codec
            ${commos.codec.version}
        

        
            com.example.password
            password
            1.0
        

    

6.3 SHA hashing with the Apache common codec library

PasswordServiceImpl.java

package com.example.password;

import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.stereotype.Service;

@Service
public class PasswordServiceImpl implements PasswordService {

    @Override
    public String hash(String input) {
        return DigestUtils.sha256Hex(input);
    }

    @Override
    public String algorithm() {
        return "sha256";
    }

}

6.4 Unit test.

TestPasswordService.java

package com.example.password;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class TestPasswordService {

    PasswordService passwordService;

    @BeforeEach
    void init() {
        passwordService = new PasswordServiceImpl();
    }

    @DisplayName("sha256 -> hex")
    @ParameterizedTest
    @CsvSource({
            "123456, 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92",
            "hello world, b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
    })
    void testSha256hex(String input, String expected) {
        assertEquals(expected, passwordService.hash(input));
    }

}

7. Web-Modul

7.1 A Spring MCV + thymeleaf web application, hash an input with a md5 or sha algorithm.

image

7.2 POM file.

web/pom.xml



    
    
        com.example.multi
        java-multi-modules
        1.0
    
    4.0.0

    
    com.example
    web
    1.0
    war

    
        3.0.10.RELEASE
    

    

        

        
        
            com.example.password
            password-sha
            1.0
        

        
            org.springframework
            spring-webmvc
            ${spring.version}
        

        
            org.springframework
            spring-test
            ${spring.version}
        

        
        
            ch.qos.logback
            logback-classic
            1.2.3
        

        
        
            org.hamcrest
            hamcrest-library
            1.3
            test
        

        
        
            javax.servlet
            javax.servlet-api
            3.1.0
            provided
        

        
        
            org.thymeleaf
            thymeleaf
            ${thymeleaf.version}
        

        
            org.thymeleaf
            thymeleaf-spring5
            ${thymeleaf.version}
        

    

    
        java-web-project
        

            
                org.eclipse.jetty
                jetty-maven-plugin
                9.4.12.v20180830
            

            
                org.apache.maven.plugins
                maven-war-plugin
                3.2.2
            

        
    

7.3 Web module dependency

Terminal

$ mvn -pl web dependency:tree

[INFO] com.example:web:war:1.0
[INFO] +- com.example.password:password-sha:jar:1.0:compile
[INFO] |  +- commons-codec:commons-codec:jar:1.11:compile
[INFO] |  \- com.example.password:password:jar:1.0:compile
[INFO] +- org.springframework:spring-webmvc:jar:5.1.0.RELEASE:compile
[INFO] |  +- org.springframework:spring-aop:jar:5.1.0.RELEASE:compile
[INFO] |  +- org.springframework:spring-beans:jar:5.1.0.RELEASE:compile
[INFO] |  +- org.springframework:spring-core:jar:5.1.0.RELEASE:compile
[INFO] |  |  \- org.springframework:spring-jcl:jar:5.1.0.RELEASE:compile
[INFO] |  +- org.springframework:spring-expression:jar:5.1.0.RELEASE:compile
[INFO] |  \- org.springframework:spring-web:jar:5.1.0.RELEASE:compile
[INFO] +- org.springframework:spring-test:jar:5.1.0.RELEASE:compile
[INFO] +- ch.qos.logback:logback-classic:jar:1.2.3:compile
[INFO] |  +- ch.qos.logback:logback-core:jar:1.2.3:compile
[INFO] |  \- org.slf4j:slf4j-api:jar:1.7.25:compile
[INFO] +- org.hamcrest:hamcrest-library:jar:1.3:test
[INFO] |  \- org.hamcrest:hamcrest-core:jar:1.3:test
[INFO] +- javax.servlet:javax.servlet-api:jar:3.1.0:provided
[INFO] +- org.thymeleaf:thymeleaf:jar:3.0.10.RELEASE:compile
[INFO] |  +- ognl:ognl:jar:3.1.12:compile
[INFO] |  |  \- org.javassist:javassist:jar:3.20.0-GA:compile
[INFO] |  +- org.attoparser:attoparser:jar:2.0.5.RELEASE:compile
[INFO] |  \- org.unbescape:unbescape:jar:1.1.6.RELEASE:compile
[INFO] +- org.thymeleaf:thymeleaf-spring5:jar:3.0.10.RELEASE:compile
[INFO] +- org.springframework:spring-context:jar:5.1.0.RELEASE:compile
[INFO] +- org.junit.jupiter:junit-jupiter-engine:jar:5.3.1:test
[INFO] |  +- org.apiguardian:apiguardian-api:jar:1.0.0:test
[INFO] |  +- org.junit.platform:junit-platform-engine:jar:1.3.1:test
[INFO] |  |  +- org.junit.platform:junit-platform-commons:jar:1.3.1:test
[INFO] |  |  \- org.opentest4j:opentest4j:jar:1.1.1:test
[INFO] |  \- org.junit.jupiter:junit-jupiter-api:jar:5.3.1:test
[INFO] \- org.junit.jupiter:junit-jupiter-params:jar:5.3.1:test
[INFO] ------------------------------------------------------------------------

7.4 Spring stuff, integrate thymeleaf and configuration.

SpringConfig.java

package com.example.web.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring5.view.ThymeleafViewResolver;
import org.thymeleaf.templatemode.TemplateMode;

@EnableWebMvc
@Configuration
@ComponentScan({"com.example"})
public class SpringConfig implements WebMvcConfigurer {

    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**")
                .addResourceLocations("/resources/");
    }

    @Bean
    public SpringResourceTemplateResolver templateResolver() {
        SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
        templateResolver.setApplicationContext(this.applicationContext);
        templateResolver.setPrefix("/WEB-INF/views/");
        templateResolver.setSuffix(".html");
        templateResolver.setTemplateMode(TemplateMode.HTML);
        templateResolver.setCacheable(true);
        return templateResolver;
    }

    @Bean
    public SpringTemplateEngine templateEngine() {
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(templateResolver());
        templateEngine.setEnableSpringELCompiler(true);
        return templateEngine;
    }

    @Bean
    public ThymeleafViewResolver viewResolver() {
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setTemplateEngine(templateEngine());
        return viewResolver;
    }

}

WebInitializer.java

package com.example.web;

import com.example.web.config.SpringConfig;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class[] getRootConfigClasses() {
        return null;
    }

    @Override
    protected Class[] getServletConfigClasses() {
        return new Class[]{SpringConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

}

WelcomeController.java

package com.example.web.controller;

import com.example.password.PasswordService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class WelcomeController {

    private final Logger logger = LoggerFactory.getLogger(WelcomeController.class);

    @Autowired
    private PasswordService passwordService;

    @GetMapping("/")
    public String welcome(@RequestParam(name = "query",
            required = false, defaultValue = "123456") String query, Model model) {

        logger.debug("Welcome to example.com... Query : {}", query);

        model.addAttribute("query", query);
        model.addAttribute("hash", passwordService.hash(query));
        model.addAttribute("algorithm", passwordService.algorithm());

        return "index";
    }

}

7.5 Unit Test for Spring MVC.

TestWelcome.java

package com.example.web;

import com.example.web.config.SpringConfig;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@SpringJUnitWebConfig(SpringConfig.class)
@DisplayName("Test Spring MVC default view")
public class TestWelcome {

    private MockMvc mockMvc;

    @Autowired
    private WebApplicationContext webAppContext;

    @BeforeEach
    public void setup() {
        mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext).build();
    }

    @Test
    public void testDefault() throws Exception {

        this.mockMvc.perform(
                get("/"))
                .andExpect(status().isOk())
                .andExpect(view().name("index"))
                .andExpect(model().attribute("query", "123456"));
        //.andExpect(model().attribute("sha256", "8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92"))
        //.andExpect(model().attribute("md5", "e10adc3949ba59abbe56e057f20f883e"));

    }

}

7.6 Thymeleaf view.

index.html




Erledigt.

8. Demo

8.1 In parent project, uses the standard mvn compile to compile all the modules, Maven will decide the build order.

Terminal

$ mvn compile

[INFO] Reactor Summary:
[INFO]
[INFO] java-multi-modules 1.0 ............................. SUCCESS [  0.007 s]
[INFO] password ........................................... SUCCESS [  0.539 s]
[INFO] password-sha ....................................... SUCCESS [  0.038 s]
[INFO] web ................................................ SUCCESS [  0.080 s]
[INFO] password-md5 1.0 ................................... SUCCESS [  0.035 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.822 s
[INFO] Finished at: 2018-10-23T15:32:21+08:00
[INFO] ------------------------------------------------------------------------

8.2 Install all the modules into the local repository.

Terminal

$ mvn install

8.3 Compiles the web module only (Maven will find the password module dependency from the local repository, that’s why we need the mvn install

Terminal

$ mvn -pl web compile

8.4 Alternatively, add -am to compile the web module and also its dependency modules (password-sha and password).

Terminal

$ mvn -am -pl web compile

[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] java-multi-modules                                                 [pom]
[INFO] password                                                           [jar]
[INFO] password-sha                                                       [jar]
[INFO] web                                                                [war]

......

[INFO] Reactor Summary:
[INFO]
[INFO] java-multi-modules 1.0 ............................. SUCCESS [  0.009 s]
[INFO] password ........................................... SUCCESS [  0.534 s]
[INFO] password-sha ....................................... SUCCESS [  0.036 s]
[INFO] web 1.0 ............................................ SUCCESS [  0.077 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.780 s
[INFO] Finished at: 2018-10-23T15:36:16+08:00
[INFO] ------------------------------------------------------------------------

8.5 Test the web module with mvn -pl web jetty:run command.

Terminal

$ mvn install

$ mvn -pl web jetty:run

//...
[INFO] 1 Spring WebApplicationInitializers detected on classpath
[INFO] DefaultSessionIdManager workerName=node0
[INFO] No SessionScavenger set, using defaults
[INFO] node0 Scavenging every 600000ms
[INFO] Started ServerConnector@40a8a26f{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
[INFO] Started @6398ms
[INFO] Started Jetty Server

image

image

8.8 Update to md5 algorithm. Starten Sie den Jetty-Server neu.

web/pom.xml

        
        
            com.example.password
            password-md5
            1.0
        

        

image

Quellcode herunterladen

$ git clone https://github.com/example/maven-examples.git
$ cd java-multi-modules
$ mvn install
$ mvn -pl web jetty:run