Maven多模块项目搭建

项目搭建

  Maven中有模块的概念,项目与模块不一定是一对一关系
  如SpringFramework这一项目,其对应的Maven模块有spring-core、spring-context等,因此一个项目往往会被划分为很多模块
    单模块项目而言,模块名=项目名(也就没有了模块的概念)
    多模块项目而言,模块名≠项目名
 
(以IDEA为例)

1、单模块项目搭建

1.1、搭建步骤
  File--> New--> Project --> Spring Initializr
  • Group(groupId):定义项目属于哪个组(组织或公司)
    • 如com.xiaomi.mitv
  • Artifact(artifactId):项目(模块)名称,是当前项目在组中的唯一标识。
    • 推荐的做法是使用实际项目名称作为artifact的前缀, 如appstore项目下的升级模块:appstore-upgrade。这样做的好处是方便寻找实际构件(jar包)默认情况下,Maven默认生成的构件会以artifactId作为开头,如appstore-upgrade-1.0.jar
    • Group+Artifact实际对应Java包的结构,是src/java/main目录中Java的目录结构,如com.xiaomi.mitv.appstore

    

 

1.2 、延伸
  • Maven坐标
    • Maven其中一个核心的作用就是管理项目的依赖,引入我们所需的各种jar包等
    • 为了能自动化地解析任何一个Java构件,Maven必须将这些jar包或者其他资源进行唯一标识,这是管理项目的依赖的基础,也就是我们要说的坐标
    • 包括我们自己开发的项目,也是要通过坐标进行唯一标识的,这样才能与其它项目中进行依赖引用
    • 在Maven仓库中存储所有Maven项目共享的构件,每个构件都有一个唯一的坐标,对应在仓库中的唯一存储路径,该路径与坐标的大致关系为:groupId/artifactId/version/artifactId-version.packaging
 
  • pom、jar包和war包:
    • pom
      • 一般作为父工程存在,父工程主要是进行统一的版本申明,并不定义具体的依赖关系,常见于多模块或者说聚合工程中使用
    • jar
      • Java Archive(Java归档文件),jar包就是java的类进行编译生成的class文件打包的压缩包,包里面就是一些class文件
      • 在声明了main方法的类,并在jar文件中的META_INF/MANIFEST.MF文件中配置了Main-Class之后,是可以直接用 java -jar xxx.jar 通过内置的tomcat运行的,比较方便,简单
    • war:
      • Web application Archive,与jar基本相同,但它通常表示这是一个Java的Web应用程序的包,是可以直接运行的web模块
      • Web项目有一个web资源目录,默认位置为src/main/webapp,其中需要包括WEB-INF目录,里面包含class文件、web.xml配置文件及前端页面文件,依赖的jar包在WEB-INF下的lib目录下
      • war包需要发布到一个容器里面,如将war包放在tomcat的webapps目录下,直接启动tomcat即可
 
1.3、项目结构介绍
  
  • src/main/java:项目主代码目录,项目的主代码最终会被打包到最终的构件中(jar包)
  • src/main/resources:项目主资源文件目录
  • src/main/test:项目测试代码目录,测试代码不会被打包
  • target:Maven构建的所有输出都在target目录中,项目主代码编译至target/classes,测试代码编译至target/test-classes
  • .idea存放项目的配置信息,包括历史记录,版本控制信息等(不会提交,可以设置隐藏)
  • .gitignore:用git做版本控制时 用这个文件控制那些文件或文件夹 不被提交(不用git的话可删除 没影响)
  • HELP.md:项目的帮助文档(不需要的话可删除 没影响)
  • mvnw(Maven wrapper):linux上处理mevan版本兼容问题的脚本(可删除 没影响)
  • mvnw.cmd:windows 上处理mevan版本兼容问题的脚本(可删除 没影响)
  • .mvn:Maven-wrapper.properties文件中记录你要使用的Maven版本,当用户执行mvnw clean 命令时,发现当前用户的Maven版本和期望的版本不一致,那么就下载期望的版本,然后用期望的版本来执行mvn命令(可删除)
  • appstore.iml:每个导入IDEA的项目都会生成一个项目同名的 .iml文件 用于保存你对这个项目的配置 (删了程序重新导入后还会生成 但由于配置丢失可能会造成程序异常)
 

2、多模块项目搭建

2.1、为什么搭建多模块项目?
  • 对项目按功能、业务模块划分,使项目结构更清晰,降低项目耦合性
    • 如电视商店项目,将电视商店的接口服务和升级服务拆分为单独的模块
  • 抽取公共模块,实现一处开发多处引用,提高代码复用率和开发效率,更利于项目后期的维护和升级
    • 如抽取common模块,放置共用的配置类、工具类、常量、枚举等
  • 子模块的pom文件会继承父工程的pom文件,依赖jar包版本统一交由父pom来管理
 
2.2 、多模块项目建议
  • 一个项目的子模块都应该使用相同的groupId
  • 如果它们一起开发和发布,还应该使用同样的version
  • 一个项目的子模块的artifactId还应该使用一致的前缀,以方便同其他项目区分
  • 子模块的目录名称应当与其artifactId一致,即artifactId=项目名
  • 子模块的包名,如groupId为com.xiaomi.mitv,artifactId为appstore-common,那么此子模块的包名应为 com.xiaomi.mitv.appstore.common
 
2.3、多模块项目搭建示例:
2.3.1、新建父模块
  • File-->New-->Project-->Maven

  • Next

  • Finish
  • 删除src目录
因为我们创建的是父模块,只作为聚合管理模块,其中还包含子模块,因此不需要src目录,删掉即可
  • 修改pom.xml,将打包方式改为 pom:<packaging>pom</packaging>
 
2.3.2、添加公共子模块
仅作为可独立部署模块的依赖,以common模块为例
  • 在父模块上右键 New-->Module-->Maven
  • 无需勾选archetype,Next

  • 选择Parent,然后填写模块名即可,GroupId、ArtifactId、Version会自动填充
  • Finish,父模块pom.xml中会自动在modules标签内添加新增的子模块
    <parent>
        <artifactId>appstore</artifactId>
        <groupId>com.xiaomi.mitv</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    
    <artifactId>appstore-common</artifactId>

    parent元素声明了父模块,parent下的子元素artifactId,groupId,version指定了父模块的坐标,这三个元素是必须的

    元素relativePath元素表示父父模块pom的相对路径,默认值为 ../pom.xml,也就是说,Maven默认父pom在上一层目录下

    父模块与子模块的目录结构不一定是父子关系,也可以是平行目录结构。这时,需要修改relativePath元素的值,指定父pom的位置,父模块pom中module元素的值也需要做相应的修改,以指向正确的子模块目录

  • 若不需要,如common模块,可删除resources和test 目录
 
2.3.3、添加子模块(可独立部署SpringBoot模块)
  • 在父模块上右键 New-->Module-->Spring Initializr

注意:SpringBoot子模块不会自动填充Group、Version等
  • Group与父模块保持一致
  • Artifact以父模块名作为前缀
  • Version与父模块保持一致
  • Next
    • 选择SpringBoot版本和需要的依赖

  • Next
    • 确认模块名和路径,一般在父模块目录下

  • Finish
    • 模块创建完成,可删除mvnw、mvnw.cmd、.mvn
  • 修改父模块pom
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.5</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<groupId>com.xiaomi.mitv</groupId>
<artifactId>appstore</artifactId>
<packaging>pom</packaging>
<version>1.0</version>

<modules>
    <module>appstore-common</module>
    <module>appstore-upgrade</module>
</modules>
    • 添加parent:spring-boot-starter-parent
    • 手动添加刚创建的子moudle:appstore-upgrade
  • 修改pom
<parent>
    <artifactId>appstore</artifactId>
    <groupId>com.xiaomi.mitv</groupId>
    <version>1.0</version>
</parent>

<artifactId>appstore-upgrade</artifactId>
<version>1.0</version>
<name>appstore-upgrade</name>
<description>电视应用商店应用升级服务</description>

<dependencies>
    <dependency>
        <groupId>com.xiaomi.mitv</groupId>
        <artifactId>appstore-common</artifactId>
        <version>1.0</version>
    </dependency>
</dependencies>

  

    • 修改parent为父模块
    • 可删除groupId元素和properties元素,从父pom继承
    • 添加appstore-common模块依赖
 
2.3.4、多模块目录结构
 
2.4、多模块项目介绍
多模块项目由管理一组子模块的聚合器 pom 构建,也就是父模块的pom
2.4.1、父模块
一个多模块的项目有一个父模块,也叫聚合模块,它仅仅包含一个pom文件,不像其他模块有src/main/java等目录,聚合模块仅仅是帮助聚合其他模块的工具,它本身并无实质的内容
父模块的pom文件中packaging标签值为pom:
<packaging>pom</packaging>

 

我们可以通过在一个打包方式为pom的Maven项目中声明任意多的module元素来实现模块的聚合
 
2.4.2、POM的继承
一个项目的POM之间可能会有很多相同的配置,groupId、version、一些相同的jar包依赖、插件等配置,pom的继承能让我们抽取出重复的配置。
另外子模块中会隐式从父模块继承groupId和version两个元素,如果子类这两个元素不同,可以在子POM中显式声明。子artifactId是必须显示声明的。
可继承的POM元素(只列举常见的):
  • groupId:项目组Id
  • version:项目版本
  • distributionManagement:项目的部署配置
  • properties:自定义的Maven属性
  • dependencies:项目的依赖配置
  • dependencyManagement:项目的依赖管理配置
  • repositories:项目的仓库配置
  • build:包括项目的源码目录配置、输出目录配置、插件配置、插件管理配置等
  
2.4.3、反应堆(Reactor)
  • 在一个多模块的Maven项目中,反应堆是指所有模块组成的一个构建结构
    • 对于单模块的项目,反应堆就是该模块本身
    • 对于多模块项目来说,反应堆就包含了各模块之间继承与依赖的关系,从而能够自动计算出合理的模块构建顺序。
  ⚠️注意:在父模块上执行打包,会按照模块的反应堆依次构建各个模块,依次生成每个模块的构件(jar包),有依赖模块,会先构建其依赖模块,其依赖模块无需上传到本地仓库,也可成功构建
  
  • 反应堆的构建顺序
Maven按照父POM中modules标签的顺序读取POM,如果该POM没有依赖模块,那么就构建该模块,否则就先构建其依赖模块,如果该依赖还依赖其他模块,则进一步先构建依赖的依赖。
模块间的依赖关系会将反应堆构成一个有向非循环图(Directed Acyclic Graph,GAP),各个模块是该图的节点,依赖关系构成了有向边。这个图不允许出现循环,也就是Maven不允许循环依赖,当出现模块A依赖于B,而B又依赖于A的情况时,Maven就会报错
 
上面项目的反应堆构建顺序:
[INFO] Reactor Build Order:
[INFO] 
[INFO] appstore                                                           [pom]
[INFO] appstore-common                                                    [jar]
[INFO] appstore-upgrade                                                   [jar]
 
2.4.4、自定义模块之间的依赖
  注意:一个构件只有在本地仓库中,才能由其他Maven项目使用
  appstore-upgrade项目依赖appstore-common模块,因此appstore-common模块必须先安装到本地仓库中
  因此当我们修改了依赖的模块代码,项目启动报错时,应先将被依赖的子模块 mvn clean install到本地仓库中
 
 

3、多环境配置

  在实际项目中,需要面对不同的运行环境,比如开发环境、预览环境、生产环境等,每个运行环境的数据库、Redis服务器等配置都不相同。
  如果每次发布测试、更新生产都需要手动修改相关系统配置。这种方式特别麻烦,费时费力,而且出错概率大
  因此,我们的项目要能支持针对不同的环境并使用对应的配置,实现灵活配置
 
 3.1、Spring Boot方案
  Spring Boot为我们提供了更加简单方便的配置方案来解决多环境的配置问题
 
  3.1.1、创建多环境配置文件
  创建多环境配置文件时,需要遵循Spring Boot允许的命名约定来命令,格式为application-{profile}.properties,其中{profile}为对应的环境标识
  在resources目录下分别创建application-dev.properties、application-pre.properties、application-prd.properties,分别对应开发、预览、生产环境的配置文件
  
  其中application.properties为项目主配置文件,包含项目所需的所有公共配置
  当激活的环境对应的文件中配置项与application.properties中配置项相同时,会覆盖application.properties中的配置项
 
  3.1.2、多环境的切换
  • application.properties配置文件中增加如下配置项:
spring.profiles.active=dev

 

  • IDEA编译器指定项目启动环境
    • 我们可以在IDEA的Run/debug Configuration 页面配置项目启动环境。

  • 命令行启动指定项目环境
通过java -jar命令启动项目时,指定启动环境:
java -jar xxx.jar --spring.profiles.active=dev
 
问题:
  • xml配置文件不能进行多环境配置,如logback.xml
  • 不能根据Redis、MySQL拆分单独的配置文件
  • 一个项目中多模块不能共用配置文件
 
好处:
  • 配置简单方便
  • 一次打包,多环境运行
 
 3.2、Maven Profile
  Maven在项目构建的时候就需要能够识别所在的环境并使用对应的配置,生成不同的构件,实现灵活构建,灵活配置
  为了能让构建在不同环境下方便地移植,Maven引入了Profile的概念,通过在构建时激活不同的Profile,生成不同的构件,以实现构建在不同环境下的移植
 
  Profile与我们项目中的环境是一一对应的
 
  使用Profile实现不同环境生成不同构件示例:
  3.2.1、在项目根目录下创建多环境配置文件目录
  
 
3.2.2、在pom中配置profiles
在不同环境下构建时,复制对应环境下的配置文件到 target/classes目录下
<profiles>
    <profile>
        <id>dev</id>
        <properties>
            <active.env>dev</active.env>
        </properties>
        <activation>
            <activeByDefault>true</activeByDefault> <!--默认激活此profile-->
        </activation>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-resources-plugin</artifactId>
                    <executions>
                        <execution>
                            <id>copy-resources</id>
                            <phase>validate</phase>
                            <!--在default生命周期的validate阶段执行copy-resources操作-->
                            <goals>
                                <goal>copy-resources</goal>
                            </goals>
                            <configuration>
                                <!--复制到哪-->
                                <outputDirectory>${basedir}/target/classes</outputDirectory>
                                <!--资源文件属性过滤-->
                                <resources>
                                    <resource>
                                        <directory>../appstore-config/dev</directory>
                                        <filtering>true</filtering>
                                        <!--复制哪些文件-->
                                        <includes>
                                            <include>logback.xml</include>
                                            <include>redis.properties</include>
                                        </includes>
                                    </resource>
                                </resources>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </profile>
    <profile>
        <id>pre</id>
        <properties>
            <active.env>pre</active.env>
        </properties>
        ......
    </profile>
</profiles>
 
3.2.3、激活profile
  • pom中默认激活
        <activation>
            <activeByDefault>true</activeByDefault> <!--默认激活此profile-->
        </activation>
  • IDEA 右侧Maven Profiles勾选

  

  • 命令行激活
mvn clean install -Ppre
 
3.2.4、application.properties中获取自定义配置文件的配置项
  如何在application.properties中获取激活的Profile环境下对应的配置文件内容的属性配置?
  如,想使用SpringBoot的自动配置,在application.properties中配置spring.datasource.xxx即可快速配置数据源,同时,又需要根据环境获取不同的自定义的mysql.properties中的数据源配置
 
  • 3.2.4.1、Maven资源过滤(资源文件中Maven属性解析)
  Maven属性只有在POM中才会被解析
  Maven <properties>中配置了 db.username=xiaomi,那么${db.username}只有在POM中才会被解析为具体的值xiaomi,如果是在 src/main/resources内的配置文件中引用了${db.username},构建的时候,它仍然为${db.username},不会被解析为具体的值xiaomi。
  因此,需要让Maven解析资源文件中的Maven属性
  在pom中配置资源文件的目录,并开启资源属性过滤,如下,即可让maven-resources-plugin插件在将资源文件复制到编译输出目录中时,解析资源文件中的Maven属性
 
<build>
    <resources>
        <resource>
            <directory>${basedir}/src/main/resources</directory>
            <filtering>true</filtering>
            <!--只对资源目录下的某些文件开启Maven属性过滤-->
            <includes>
                <include>xxx.properties</include>
            </includes>
        </resource>
    </resources>
</build>
 
  SpringBoot 1.3或更高版本,将Maven属性占位符修改为了 @xxx@,要想继续使用${}占位符,需要在POM properties中指定<resource.delimiter>${}</resource.delimiter>
  SpringBoot项目中默认为application.properties文件开启了Maven属性解析
 
3.2.4.2、自定义资源文件加载到pom
接上面的问题,如何在application.properties获取对应环境下mysql.properties的属性配置
<build>
    <filters>
        <filter>../appstore-config/${active.env}/mysql.properties</filter>
    </filters>
</build>
filter标签的作用相当于将指定环境下的配置文件中的配置加载到POM文件中,类似于POM文件中的properties标签
再加上开启资源过滤,那么在/src/main/resources中的文件就可以通过使用${},来获取mysql.properties中的属性配置了
 
 
END.
posted @ 2022-11-16 10:27  杨岂  阅读(6201)  评论(0编辑  收藏  举报