Maven - So erstellen Sie ein Multi-Modul-Projekt
In diesem Tutorial zeigen wir Ihnen, wie Sie mit Maven ein Multi-Modul-Projekt mit vier Modulen verwalten können:
-
Passwortmodul - Nur Schnittstelle.
-
Password md5-Modul - Implementierung des Passwortmoduls, MD5-Passwort-Hashing.
-
Passwort sha-Modul - Implementierung des Passwortmoduls, SHA-Passwort-Hashing.
-
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:
-
Maven 3.5.3
-
JDK 8
-
Spring 5.1.0.RELEASE
-
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
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
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.
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.
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.
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.
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
8.8 Update to md5 algorithm. Starten Sie den Jetty-Server neu.
web/pom.xml
com.example.password password-md5 1.0
Quellcode herunterladen
$ git clone https://github.com/example/maven-examples.git
$ cd java-multi-modules
$ mvn install
$ mvn -pl web jetty:run