maven项目聚合与继承、依赖管理、占位符、构建管理、多套配置

本文引用博客地址:https://my.oschina.net/mzdbxqh/blog/846018

本文开始对study-parent模块的pom.xml进行配置。该文件的角色是整个Maven的Parent。文中将对Maven的聚合与继承、依赖管理、占位符、构建管理、多套配置等概念进行解释。以下将pom.xml的元素结构摘录如下:

study-parent/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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">

    <!-- 指定pom版本,所有模块的pom均需指定 -->
    <modelVersion>4.0.0</modelVersion>

    <!-- 本模块的坐标 -->
    <groupId>com.oschina</groupId>
    <artifactId>study-parent</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!-- 打包类型,默认为jar,含有子模块的模块自动被设置为pom -->
    <packaging>pom</packaging>

    <!-- 被聚合的子模块索引 -->
    <modules>
        <module>study-common</module>
        <module>study-plugin</module>
        <module>study-blog</module>
        <module>study-web</module>
    </modules>

    <!-- 更多模块信息,填写模块的描述还有作者信息什么的 -->
    <name>ssm学习项目</name>
    <description>模块描述</description>
    <url>https://my.oschina.net/mzdbxqh/</url>
    <developers>
        <developer>
            <name>mzdbxqh</name>
        </developer>
    </developers>

    <!-- 这里集中定义整个项目里边所有第三方依赖的版本及其他可作用于所有pom的常量 -->
    <properties>
    </properties>

    <!-- 这里集中陈列整个项目需要用到的第三方依赖及其版本 -->
    <dependencyManagement>
        <dependencies>
        </dependencies>
    </dependencyManagement>

    <!-- 这里配置与build过程相关的内容 -->
    <build>
        <defaultGoal>install</defaultGoal>
        <directory>${basedir}/target</directory>
        <finalName>${project.artifactId}-${project.version}</finalName>

        <!-- 这里定义build过程中将引入的资源文件 -->
        <!-- 这里引入的键值对可以用于替换resources里边指定的资源文件中的${}占位符 -->
        <filters>
        </filters>

        <!-- 这里定义build过程中将输出的资源文件,包括源地址/目标地址/需输出资源文件/不输出的资源文件,等 -->
        <resources>
        </resources>

        <!-- 这里定义build过程所需使用的插件 -->
        <plugins>
        </plugins>
    </build>

    <!-- 配置信息集-例如可以给开发环境/测试环境/生产环境预定义三套不同的配置 -->
    <profiles>
    </profiles>

</project>

上面的内容多数由系统自动生成。在原有基础上,我额外还加入了以下内容:

  1. 更多模块信息
  2. properties
  3. ependencyManagement
  4. build
  5. profiles

下面分别对重要节点进行说明:

一、坐标 - Group:ArtifactId:Version

我们可以简单地认为,GroupId:ArtifactId:Version:Packaging四个元素构成了一个maven项目的坐标,用于在Maven仓库中准确地定位一个模块的特定版本。一个GroupId(项目)下面可以有很多个ArtifactId(模块),每个ArtifactId(模块)会有很多个Version(版本),每个Version(版本)一般被Packaging(打包)为jar、war、pom中的一种。

但在被引用的时候,Packaging经常是固定的类型,因此更多的时候我们所关心的仅仅是由GroupId:ArtifactId:Version构成的基本坐标。比如配置parent的时候,坐标确定的对象是一个具体的pom项目;在配置依赖的时候,坐标默认定位了一个具体的jar项目。

以下是:study-parent/study-common/pom.xml/坐标

    <!-- ### groupId,version直接继承自Parent,packaging默认为jar ### -->

    <!-- <groupId>com.oschina</groupId> -->
    <artifactId>study-common</artifactId>
    <!-- <version>1.0-SNAPSHOT</version> -->
    <!-- <packaging>jar</packaging> -->

二、继承与聚合 - Parent/Aggregator

maven里面有两种父子关系:

  1. 亲爹 - 管继承,在sub module(子模块)里面通过parent元素进行配置
  2. 干爹 - 管聚合,在Aggregator(聚合器)里面通过modules元素进行配置

继承与聚合

如图:

甲乙两个儿子需要声明谁是自己亲爹,但是亲爹不用声明自己有儿子。儿子们继承亲爹的可继承属性。

干爹需要声明自己有两个干儿子甲和乙,但干儿子不用关心自己有个干爹,对干爹进行build,干爹会拉上两个干儿子一起,当做一整个聚合项目进行build。

由此衍生出各种用法:

  1. 本教程中的study-parent,既是亲爹 - Parent,又是干爹 - Aggregator(聚合器);既起到属性继承的作用,又起到聚合的作用。小型项目整个一起发布时,考虑使用这种方式。
  2. Parent不配置modules元素来声明Sub Module(子模块),但是Sub Module需要声明Parent。这样各个Sub Module继承自同一个Parent的属性,但是分别进行Build操作。多人协作,每个人负责一个模块时,考虑使用这种方式。
  3. Parent和Aggregator分开,Sub Module声明同一个Parent,但是有多个Aggregator把Sub Module分成了多组,甚至可能会有一个Sub Module同时在几个Aggregator里面的情况。
    整个公司里共用一个Parent,不同的团队分管不同的子系统时,考虑使用这种方式。
    不同模块部署在不同机器上,通过远程调用时,考虑使用这种方式。

按照第3种用法,本示范项目可以转化为下面这种变体:

工程结构变体

三、占位符 - ${X}

POM.xml里面可以通过${}占位符引用一些变量,例如:

<!-- 引用本pom的project根节点下面,version子节点的值 -->
${project.version}

<!-- 引用环境变量-JAVA_HOME的值 -->
${env.JAVA_HOME}

<!-- 引用maven内置参数的值 -->
${project.build.sourceEncoding}

<!-- 引用自定义变量X的值 -->
${X}

Parent的properties节点的作用正是用于自定义变量以及覆盖maven内置参数的值。以下代码除了定义第三方依赖的版本外,还覆盖了几个maven内置参数,其发生作用的原理是:

study-parent/pom.xml继承自Parent Poms,其对于插件中参数的配置,采用了占位符的方式。
例如${project.build.sourceEncoding},在Parent Poms中定义为"GBK"。此处对默认值进行覆盖,即实现了批量变更插件配置的目的。参考链接

以下是:study-parent/pom.xml/properties

    <!-- 这里集中定义整个项目里边所有第三方依赖的版本及其他可作用于所有pom的常量 -->
    <properties>
        <!-- 指定编码方式,maven3以上可以直接识别,低版本需在build节点声明一个<encode>${project.build.sourceEncoding}</encode> -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <!-- 指定源代码和编译代码的java版本 -->
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>

        <!-- spring版本号 -->
        <spring.version>4.3.6.RELEASE</spring.version>

        <!-- aspectj版本号 -->
        <aspectj-version>1.8.9</aspectj-version>

        <!-- mybatis版本号 -->
        <mybatis.version>3.4.2</mybatis.version>
        <mybatis-spring.version>1.3.1</mybatis-spring.version>

        <!-- mysql-connector(数据库驱动)版本号 -->
        <mysql.version>5.1.40</mysql.version>

        <!-- druid(连接池)版本号 -->
        <druid.version>1.0.28</druid.version>

        <!-- junit(测试)版本号 -->
        <junit.version>4.11</junit.version>

        <!-- 日志版本号 -->
        <slf4j.version>1.7.21</slf4j.version>
        <log4j.version>1.2.17</log4j.version>

        <!-- JSP相关版本号 -->
        <jstl.version>1.2</jstl.version>
        <servlet-api.version>2.5</servlet-api.version>
        <jsp-api.version>2.0</jsp-api.version>

        <!-- 其他 -->
        <fastjson.version>1.2.29</fastjson.version>
        <guava.version>21.0</guava.version>

    </properties>

四、依赖管理 - dependencyManagement

Parent的dependencyManagement节点的作用是管理整个项目所需要用到的第三方依赖。只要约定了第三方依赖的坐标(GroupId:ArtifactId:version),后代模块即可通过GroupId:ArtifactId进行依赖的引入。这样能够避免依赖版本的冲突。当然,这里只是进行约定,并不会真正地引用依赖。

以下是:study-parent/pom.xml/dependencyManagement

    <!-- 这里集中陈列整个项目需要用到的第三方依赖及其版本 -->
    <dependencyManagement>
        <dependencies>
            <!-- ##### Spring核心包 ##### -->
            <!-- Spring核心工具包 -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
                <version>${spring.version}</version>
                <exclusions>
                    <!-- Spring官方建议不要用这个 -->
                    <exclusion>
                        <groupId>commons-logging</groupId>
                        <artifactId>commons-logging</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <!-- 负责Bean管理,是Spring的IoC和DI的实现包 -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-beans</artifactId>
                <version>${spring.version}</version>
            </dependency>
             <!-- Spring的运行时环境 -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <!-- Spring对第三方的支持包,包括缓存/邮件/任务调度/模板引擎等 -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context-support</artifactId>
                <version>${spring.version}</version>
            </dependency>

            <!-- ##### Spring AOP ##### -->
            <!-- 负责为Spring bean实现切面编程(对普通bean/final修饰的类无效,我们也用不上) -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aop</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <!-- Spring对AspectJ框架的支持包 -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
                <version>${spring.version}</version>
            </dependency>

            <!-- ##### Spring web #####-->
            <!-- Spring的mvc框架 -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <!-- Spring的web增强实现 -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-web</artifactId>
                <version>${spring.version}</version>
            </dependency>

            <!-- ##### Test ##### -->
            <!-- Spring的test支持组件,可以为诸如JUnit框架提供Spring的上下文环境 -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-test</artifactId>
                <version>${spring.version}</version>
                <scope>test</scope>
            </dependency>
            <!-- JUnit测试框架 -->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
                <scope>test</scope>
            </dependency>

            <!-- ##### Spring数据访问及集成 ##### -->
            <!-- Spring对jdbc的简化封装 -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <!-- Spring对事务控制的实现模块 -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-tx</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <!-- Spring对orm框架的支持包(然而不支持Mybatis) -->
            <!--<dependency>-->
            <!--<groupId>org.springframework</groupId>-->
            <!--<artifactId>spring-orm</artifactId>-->
            <!--<version>${spring.version}</version>-->
            <!--</dependency>-->

            <!-- ##### 数据库相关 ##### -->
            <!-- Mybatis框架 -->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>${mybatis.version}</version>
            </dependency>
            <!-- Mybatis与spring的适配器.Spring没有对mybatis的支持包,mybatis就自己写了个 -->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
                <version>${mybatis-spring.version}</version>
            </dependency>
            <!-- MySql驱动 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <!-- 连接池 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>${druid.version}</version>
            </dependency>

            <!-- ##### 日志 ##### -->
            <!-- 桥接包,用slf4j顶上spring-core包中被排除掉的commons-logging -->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>jcl-over-slf4j</artifactId>
                <version>${slf4j.version}</version>
            </dependency>
            <!-- slf4j的核心包(它只是一套接口) -->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>${slf4j.version}</version>
            </dependency>
            <!-- slf4j对log4j12的支持包,把slf4j的接口转嫁到log4j上去实现 -->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
                <version>${slf4j.version}</version>
            </dependency>
            <!-- log4j的核心包(它才是真正干活的日志框架) -->
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${log4j.version}</version>
            </dependency>

            <!-- ##### jsp相关 ##### -->
            <!-- JSP标准标签库 -->
            <dependency>
                <groupId>jstl</groupId>
                <artifactId>jstl</artifactId>
                <version>${jstl.version}</version>
            </dependency>
            <!-- servlet-api和jsp-api都是由tomcat等servlet容器负责提供的包,这里引入只用于开发,不进行打包 -->
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>servlet-api</artifactId>
                <version>${servlet-api.version}</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>jsp-api</artifactId>
                <version>${jsp-api.version}</version>
                <scope>provided</scope>
            </dependency>

            <!-- ##### 其他 ##### -->
            <!-- json处理器 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>${fastjson.version}</version>
            </dependency>
            <!-- 包括了若干google的java项目广泛依赖的核心库 -->
            <!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>${guava.version}</version>
            </dependency>

        </dependencies>
    </dependencyManagement>

五、构建配置 - build

build节点的内容可以分为三部分,BaseBuild、Resources、Plugins,下面分开说。

1. BaseBuild

以下是:study-parent/pom.xml/build/BaseBuild

        <!-- 执行build任务时,如果没有指定目标,将使用的默认值,如:在命令行中执行mvn,则相当于执行mvn install -->
        <defaultGoal>install</defaultGoal>
        <!-- build目标文件的存放目录 -->
        <directory>${basedir}/target</directory>
        <!-- build目标文件的文件名 -->
        <finalName>${project.artifactId}-${project.version}</finalName>

以下是:study-parent/study-web/pom.xml/build/BaseBuild

        <!-- 这里定义build过程中将引入的资源文件 -->
        <!-- 这里引入的键值对可以用于替换resources里边指定的资源文件中的${}占位符 -->
        <filters>
            <filter>${basedir}/src/main/resources/filters/${profile.active}.properties</filter>
        </filters>

注意:这里把filters放在了study-web这个包,是因为我准备把所有的资源文件都放在study-web包里。假如这里把filters的内容配置在study-parent包,因为所有子模块都继承了父模块的build属性,结局就是每个子模块都会到自己包下的src/main/resources/filters目录下找dev.properties文件,显然与本意相悖。

2. Resources

以下是: study-parent/study-web/pom.xml/build/Resources

        <!-- 这里定义build过程中将打包的资源文件,包括源地址/目标地址/需打包资源文件/不打包的资源文件,等 -->
        <resources>
            <resource>
                <!-- 定义这部分资源文件的源目录(以下为默认值) -->
                <directory>${basedir}/src/main/resources</directory>

                <!-- 是否进行过滤,过滤的话,上面引入的filters/*.properties里面定义的键值对,会替换本部分资源文件内的占位符 -->
                <!-- 例如上面filter.properties里边有一个my.password=123456 -->
                <!-- 然后resources/jdbc.properties里边有一个jdbc.password=${my.password} -->
                <!-- build之后,target/META-INF/jdbc.properties里边就会有jdbc.password=123456 -->
                <filtering>true</filtering>

                <!-- 指定目录下需要输出的资源文件 -->
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>

                <!-- 指定目录下不需要输出的资源文件(优先级高于include) -->
                <excludes>
                    <exclude>filters/*.properties</exclude>
                </excludes>
            </resource>
        </resources>

理由同filter,resources节点配置在study-web包下面。

以下是:study-parent/study-blog/study-blog-mapper/pom.xml/build/Resources

        <resources>
            <resource>
                <!-- 定义这部分资源文件的源目录(这里特指Mybatis的mapper.xml) -->
                <directory>${basedir}/src/main/java</directory>
                <filtering>false</filtering>
                <!-- 指定目录下需要输出的资源文件 -->
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        </resources>

在这个包配置resources节点是为了打包Mybatis的mapper文件。

3. Plugins

实际上,maven自身只是定义了生命周期,本身并没有做任何实现。但它把各个生命周期的不同阶段,绑定了不同插件的不同目标(Goal)。如果没有特别的需要,不用再在Plugins节点内引用这些插件。例如:

clean绑定goal

后面章节会进行具体介绍。

另外,在properties节点已经提过,maven内置的插件在设定参数时,大部分使用了${}这种占位符来引用maven内置的变量。而properties节点对编码方式、源文件jdk版本和编译版本进行了覆盖,将编码方式设为utf8,jdk设为1.8,故plugins节点不需要再显式地引用这些插件并指定以上参数。

        <!-- 这里定义build过程所需使用的插件 -->
        <plugins>
            <plugin>
                <!-- tomcat7插件 -->
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <!-- webapp路径和端口,最终访问地址就是http://域名:端口/path -->
                    <path>${server.path}</path>
                    <port>${server.port}</port>

                    <!-- uri所用编码,只对tomcat:run有效,因为远程部署要在tomcat服务器的配置文件上修改才行 -->
                    <uriEncoding>${project.build.sourceEncoding}</uriEncoding>

                    <!-- tomcat自带了一个manager应用,可以通过它进行内存检测/远程部署项目等功能 -->
                    <url>${deploy.url}</url>

                    <!-- manager应用的账号密码,远程部署用 -->
                    <username>${server.username}</username>
                    <password>${server.password}</password>
                </configuration>
            </plugin>
        </plugins>

注意,在配置tomcat7插件的时候,其参数均使用${}占位符,将在下一节进行定义。

六、多套环境配置 - profile

maven中的profile是一种扩展配置机制,她就好比扑克牌,你可以打出一张牌,或者打出一组牌,来告诉maven你的意图。在开发过程中,比方说我们要运行项目,可能会有首次运行进行初始化和后续正常运行两种配置。我们进行项目部署,可能会有开发环境、调试环境和生产环境的差别。此时使用profile来预定义多套配置,让maven应用于不同的阶段、不同的环境,将会非常有用。

以下是:study-parent/pom.xml/profiles

    <!-- 配置信息集-例如可以给开发环境/测试环境/生产环境预定义三套不同的配置 -->
    <profiles>
        <profile>
            <id>dev</id>
            <properties>
                <!-- 定义当前被激活的profile名称,用于build.filter节点引入同名properties文件 -->
                <!-- 即是有${basedir}/src/main/filters/dev.properties,存放开发环境下数据库连接信息等配置 -->
                <profile.active>dev</profile.active>
                
                <server.path>/</server.path>
                <server.port>8080</server.port>
                <server.url>http://127.0.0.1:8080/manager/text</server.url>
                <username>username</username>
                <password>password</password>
            </properties>

            <activation>
                <!-- 这套配置设为默认激活 -->
                <activeByDefault>true</activeByDefault>
            </activation>
        </profile>

        <profile>
            <id>test</id>
            <properties>
                <profile.active>test</profile.active>
                <server.path>/</server.path>
                <server.port>8080</server.port>
                <server.url>http://测试环境IP:8080/manager/text</server.url>
                <username>测试环境管理账号</username>
                <password>测试环境管理密码</password>
            </properties>
        </profile>

        <profile>
            <id>prod</id>
            <properties>
                <profile.active>prod</profile.active>
                <server.path>/</server.path>
                <server.port>8080</server.port>
                <server.url>http://生产环境IP:8080/manager/text</server.url>
                <username>生产环境管理账号</username>
                <password>生产环境管理密码</password>
            </properties>
        </profile>
    </profiles>

Parent模块的pom就配置到这里,完整的代码以Github项目上的为准。

猜你喜欢

转载自blog.csdn.net/liqi_q/article/details/80530248