Maven Archetypes Part 3: 怎样构建一个多模块工程?

时间:2022-06-08 15:46:03

原文地址

源码下载传送门

欢迎回来

如果你没有阅读前面章节,或对Maven archetypes不熟悉,你会看不懂,XD。

到目前为止,我们已经创建了一个archetype,可以生成能打包为jar且会say Hello word的工程,并且包含了一个单元测试类。另一个场景是你想要创建一个多模块的工程。举个栗子,你想要一个工程,包含一个model和一个webapp模块,或者一个client一个server模块能传输一个共有的类型对象。

为达到这个目的,并不会花费你太多精力。很多的工作我们在之前的例子中已经完成了。

重温前情,然后添加一些新东西

让我们从上一节所创建的archetype工程结构开始:

$ tree
.
|____pom.xml
|____src
| |____main
| | |____resources
| | | |____archetype-resources
| | | | |____pom.xml
| | | | |____src
| | | | | |____main
| | | | | | |____java
| | | | | | | |______packageInPathFormat__
| | | | | | | | |____App.java
| | | | | |____test
| | | | | | |____java
| | | | | | | |______packageInPathFormat__
| | | | | | | | |____AppTest.java
| | | |____META-INF
| | | | |____maven
| | | | | |____archetype-metadata.xml

首先,我们需要将之前能够打包成jar的项目移动到下一层目录。我们的多模块项目的顶层目录装父项目,父项目提供一个pom。

$ mkdir src/main/resources/archetype-resources/__artifactId__-model
$ mv src/main/resources/archetype-resources/src src/main/resources/archetype-resources/__artifactId__-model/
$ mv src/main/resources/archetype-resources/pom.xml src/main/resources/archetype-resources/__artifactId__-model/

你应该注意到了__artifactId__-model。如果你记得第二节的内容,如果使用双下划线在参数的前面和后面,Maven将会将这个参数名替换成对应的参数内容。根据上面的目录结构,如果我们从archetype生成的项目的artifact id为sample-project,这个目录将创建为sample-project-model

创建和重构pom文件

现在我们将根目录的内容移动到子目录去了,在根目录里还没有东西。我们需要创建一个pom,作为多模块项目的parent。现在新建一个文件src/main/resources/archetype-resources/pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">


<modelVersion>4.0.0</modelVersion>

<groupId>${groupId}</groupId>
<artifactId>${artifactId}</artifactId>
<version>${version}</version>
<packaging>pom</packaging>
<name>${artifactId}</name>

<modules>
<module>${artifactId}-model</module>
</modules>

</project>

这个pom和第一节中的pom非常类似,除了与模块关联的列表。注意我们这里用了${artifactId}-model来和刚刚创建的模块对应。

我们也需要更新模块里的pom,来和parent做对应:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>${groupId}</groupId>
<artifactId>${artifactId}</artifactId>
<version>${version}</version>
</parent>
<artifactId>${artifactId}-model</artifactId>
<packaging>jar</packaging>
<name>${artifactId}-model</name>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<encoding>UTF-8</encoding>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit-dep</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>

你可能注意到了,节点<version><groupId>已经被移除。父项目会让他的子模块继承这些值,所以在子模块中不必重复他们。

我们也可以进一步配置。首先,我们可以将build区块移动到父项目中,让所有子模块继承,这样所有子模块拥有同样的compile配置,我们也可以在依赖管理(dependency management)中定义将junit依赖让所有模块依赖版本一致。
在我们做了这些改变后,我们的父项目parent的pom(src/main/resources/archetype-resources/pom.xml)应该是下面这个样子:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">


<modelVersion>4.0.0</modelVersion>

<groupId>${groupId}</groupId>
<artifactId>${artifactId}</artifactId>
<version>${version}</version>
<packaging>pom</packaging>
<name>${artifactId}</name>

<modules>
<module>${artifactId}-model</module>
</modules>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<inherited>true</inherited>
<configuration>
<encoding>UTF-8</encoding>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit-dep</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>

</project>

我们的子模块的pom(src/main/resources/archetype-resources/artifactId-model/pom.xml)应该长这个样:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">


<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>${groupId}</groupId>
<artifactId>${artifactId}</artifactId>
<version>${version}</version>
</parent>

<artifactId>${artifactId}-model</artifactId>
<packaging>jar</packaging>
<name>${artifactId}-model</name>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit-dep</artifactId>
</dependency>
</dependencies>

</project>

更新archetype的metadata

到现在为止,我们所有的更新都不会有什么结果,知道我们更新了archetype的metadata。现在就让我们来开启这神奇的一页:

<archetype-descriptor name="sample-archetype">
<fileSets>
<fileSet filtered="true">
<directory></directory>
<includes>
<include>pom.xml</include>
</includes>
</fileSet>
<fileSet filtered="true">
<directory>__artifactId__-model</directory>
<includes>
<include>pom.xml</include>
</includes>
</fileSet>
<fileSet filtered="true">
<directory>__artifactId__-model/src/main/java</directory>
<includes>
<include>**/*.java</include>
</includes>
</fileSet>
<fileSet filtered="true">
<directory>__artifactId__-model/src/test/java</directory>
<includes>
<include>**/*.java</include>
</includes>
</fileSet>
</fileSets>
</archetype-descriptor>

创建webapp模块

我们已经添加了第一个模块。接下来,我们我们将创建第二个模块:

新建文件夹:

mkdir -p src/main/resources/archetype-resources/__artifactId__-webapp/src/main/java/__packageInPathFormat__

如果我们创建webapp,我们需要servlet api。让我们在parent pom中添加一个scope为provided的依赖。我们也需要添加一个带有version的model模块,这样,我们可在webapp里引入他的时候不用指定版本:

<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit-dep</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${groupId}</groupId>
<artifactId>${artifactId}-model</artifactId>
<version>${version}</version>
</dependency>
</dependencies>
</dependencyManagement>

我们也需要添加webapp模块到我们parent项目pom的modules列表中:

<modules>
<module>${artifactId}-model</module>
<module>${artifactId}-webapp</module>
</modules>

现在,让我们来创建webapp模块的pom(src/main/resources/archetype-resources/artifactId-webapp/pom.xml)。我们将添加servlet-api的依赖,和我们第一个模块model的依赖,还有Maven Jetty plugin,这样我们可以在创建项目后从Maven启动它:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">


<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>${groupId}</groupId>
<artifactId>${artifactId}</artifactId>
<version>${version}</version>
</parent>

<artifactId>${artifactId}-webapp</artifactId>
<packaging>war</packaging>
<name>${artifactId}-webapp</name>

<build>
<plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.26</version>
</plugin>
</plugins>
</build>

<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</dependency>
<dependency>
<groupId>${groupId}</groupId>
<artifactId>${artifactId}-model</artifactId>
</dependency>
</dependencies>

</project>

我们还需要在src/main/webapp/WEB-INF路径中添加web.xml。让我们先创建web.xml所在目录:

mkdir -p src/main/resources/archetype-resources/__artifactId__-webapp/src/main/webapp/WEB-INF
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">


<servlet>
<display-name>${artifactId}</display-name>
<servlet-name>${artifactId}</servlet-name>
<servlet-class>${package}.Servlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>${artifactId}</servlet-name>
<url-pattern>/${artifactId}</url-pattern>
</servlet-mapping>

</web-app>

我们可以过滤并替换这个文件中的${artifactId}等参数。
我们也需要一个基础的servlet类去匹配在web.xml文件中的定义。在路径src/main/resources/archetype-resources/__artifactId__-webapp/src/main/java/__packageInPathFormat__/Servlet.java创建一个类,以下是Servlet.java中内容:

package ${package};

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Servlet extends HttpServlet {

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

response.setStatus(200);
PrintWriter writer = response.getWriter();
writer.write(App.message());
}

}

更新archetype的metadata

我们需要再一次更新src/main/resources/META-INF/maven/archetype-metadata.xml,将webapp模块的内容加入。这里是最终的archetype-metadata.xml文件的样子:

<archetype-descriptor name="sample-archetype">
<fileSets>
<fileSet filtered="true">
<directory></directory>
<includes>
<include>pom.xml</include>
</includes>
</fileSet>
<fileSet filtered="true">
<directory>__artifactId__-model</directory>
<includes>
<include>pom.xml</include>
</includes>
</fileSet>
<fileSet filtered="true">
<directory>__artifactId__-model/src/main/java</directory>
<includes>
<include>**/*.java</include>
</includes>
</fileSet>
<fileSet filtered="true">
<directory>__artifactId__-model/src/test/java</directory>
<includes>
<include>**/*.java</include>
</includes>
</fileSet>
<fileSet filtered="true">
<directory>__artifactId__-webapp</directory>
<includes>
<include>pom.xml</include>
</includes>
</fileSet>
<fileSet filtered="true">
<directory>__artifactId__-webapp/src/main/java</directory>
<includes>
<include>**/*.java</include>
</includes>
</fileSet>
<fileSet filtered="true">
<directory>__artifactId__-webapp/src/main/webapp/WEB-INF</directory>
<includes>
<include>web.xml</include>
</includes>
</fileSet>
</fileSets>
</archetype-descriptor>

让我们来最后审视项目结构,奏是这个造型:

$ tree
.
|____pom.xml
|____src
| |____main
| | |____resources
| | | |____archetype-resources
| | | | |______artifactId__-model
| | | | | |____pom.xml
| | | | | |____src
| | | | | | |____main
| | | | | | | |____java
| | | | | | | | |______packageInPathFormat__
| | | | | | | | | |____App.java
| | | | | | |____test
| | | | | | | |____java
| | | | | | | | |______packageInPathFormat__
| | | | | | | | | |____AppTest.java
| | | | |______artifactId__-webapp
| | | | | |____pom.xml
| | | | | |____src
| | | | | | |____main
| | | | | | | |____java
| | | | | | | | |______packageInPathFormat__
| | | | | | | | | |____Servlet.java
| | | | | | | |____webapp
| | | | | | | | |____WEB-INF
| | | | | | | | | |____web.xml
| | | | |____pom.xml
| | | |____META-INF
| | | | |____maven
| | | | | |____archetype-metadata.xml

安装archetype,生成项目然后运行webapp

现在先install archetype,然后去要生成项目的目录,然后生成它:

$ mvn clean install
$ cd ..
$ mvn archetype:generate -DarchetypeCatalog=local
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] >>> maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom >>>
[INFO]
[INFO] <<< maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom <<<
[INFO]
[INFO] --- maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Interactive mode
[INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.archetypes:maven-archetype-quickstart:1.0)
Choose archetype:
1: local -> com.theotherian.archetype:sample-archetype (sample-archetype)
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 1: 1
Define value for property 'groupId': : com.theotherian
Define value for property 'artifactId': : sample-project
Define value for property 'version': 1.0-SNAPSHOT: :
Define value for property 'package': com.theotherian: :
Confirm properties configuration:
groupId: com.theotherian
artifactId: sample-project
version: 1.0-SNAPSHOT
package: com.theotherian
Y: :
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: sample-archetype:1.0-SNAPSHOT
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: com.theotherian
[INFO] Parameter: artifactId, Value: sample-project
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: com.theotherian
[INFO] Parameter: packageInPathFormat, Value: com/theotherian
[INFO] Parameter: package, Value: com.theotherian
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: groupId, Value: com.theotherian
[INFO] Parameter: artifactId, Value: sample-project
[WARNING] Don't override file /Users/isimpson/sample-projects/sample-project/pom.xml
[INFO] project created from Archetype in dir: /Users/isimpson/sample-projects/sample-project
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 14.574s
[INFO] Finished at: Sat May 12 22:14:07 MDT 2012
[INFO] Final Memory: 7M/81M
[INFO] ------------------------------------------------------------------------

帅呆了~我们有了一个项目,然后编译他!

$ cd sample-project
$ mvn clean install

现在artifacts已经安装,让我们来跑webapp

$ cd sample-project-webapp
$ mvn jetty:run-war

现在,如果一切正确,你将在浏览器中以地址http://localhost:8080/sample-project-webapp/sample-project看到“Hello world!”


一口气翻3篇,终于翻完了,第一次翻译,艾玛累死了。