Maven进阶与搭建私服

一、POM文件

pom.xml,全称Project Object Model,项目对象模型。可以把它理解成这个项目的身份证加说明书,身份证告诉别人我是谁,说明书告诉Maven该怎么构建我。

在pom.xml中,有三个基本元素构成了Maven的坐标:

groupId:公司域名反写加项目名,比如com.qcby。

artifactId:项目名或模块名,比如SchoolShop。

version:版本号,比如0.0.1-SNAPSHOT。

这三个元素合起来全球唯一,就像经纬度定位一个地点,Maven用坐标定位一个构件(JAR或WAR包)。本地仓库的目录结构正是按照groupId/artifactId/version来组织的,你打开.m2/repository文件夹就能看到这种层级关系。

版本号里的SNAPSHOT表示快照版,意思是还在开发中、没定稿的版本。对应的RELEASE则是正式发布版。SNAPSHOT可以反复覆盖上传,RELEASE一旦发布就不应该再改。我们一般使用这两个。

二、依赖管理

1. 依赖是什么

写Java代码时,我们要做单元测试要引入JUnit,操作数据库要引入MyBatis,写Web接口要引入Spring Boot。这些外部的JAR包就是依赖。

Maven的依赖管理解决了三个痛点:第一,不用手动下载JAR包,Maven自动从仓库拉取;第二,不用管依赖的依赖,比如你引入Spring Boot,它依赖的几十个包Maven会自动帮你下载,这叫依赖传递;第三,不用愁版本冲突,Maven有一套仲裁机制。

在pom.xml中声明依赖的写法如下:

复制代码
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.9</version>
    <scope>test</scope>
</dependency>

2. 依赖范围(scope)

Maven在编译、测试、运行三个场景下需要不同的classpath。scope标签用来控制某个依赖在哪个阶段生效。

compile(默认):编译、测试、运行都有效,几乎所有业务包都用这个。

test:只对测试有效,编译主代码和运行时无效,典型例子是JUnit。

provided:编译和测试有效,运行时无效,典型例子是Servlet API,因为Tomcat容器自己已经提供了。

runtime:编译时不需要,但测试和运行时需要,比如JDBC驱动,编码时只需接口,运行时才需要具体实现。

开发时写HttpServletRequest需要servlet-api.jar才能编译通过,但部署到Tomcat时Tomcat自己就有这个包,如果打包时把servlet-api也打进去,反而会和Tomcat的版本冲突。所以provided就是告诉Maven:编译和测试时我给你,打包和运行时你就别带了,容器会提供。

3. 依赖传递

如果A依赖B,B又依赖C,那么A是否自动依赖C?不一定,要看B对C的依赖范围。

规则是这样:如果B对C是compile范围,A会继承C,但范围可能会降级;如果B对C是test或provided范围,传递就断了,A不会依赖C;如果B对C是runtime范围,且A对B是compile,则C会以runtime范围传递给A。

实际开发中常遇到的痛点:你引入了一个第三方包,结果它带进来一堆你不需要的传递依赖,甚至引起版本冲突。这时可以用exclusions把特定的传递依赖排除掉。例如:

复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>

这样就把默认的日志框架排除掉了,你可以换用自己的日志实现。

4. 可选依赖和依赖阻断

如果你写了一个公共库,内部依赖了某个包,但你希望使用你这个公共库的项目不自动继承这个依赖,这时可以使用optional true。设置了optional后,别人依赖你的项目时,不会被动引入这个包,需要他们自己显式声明。

另外,如果你完全不想让某个传递依赖被带进来,也可以直接在dependency中配置exclusions。

三、仓库

Maven仓库就是存放构件(JAR、WAR、POM等)的地方。当你执行mvn compile时,Maven会从仓库中读取所需的依赖。

仓库分为三类:

本地仓库:默认在用户目录下的.m2/repository。第一次执行Maven命令时创建,所有下载的依赖都会先存到这里。

中央仓库:Maven官方维护的公共仓库,地址是repo1.maven.org,包含了绝大多数开源Java构件。

远程仓库:除了中央仓库之外的私服或镜像,比如阿里云镜像、公司私服。

当项目需要一个依赖时,Maven的查找顺序是:本地仓库 -> 私服(如果有配置)-> 中央仓库。本地没有就去远程下载,下载后缓存到本地,下次直接用。

可以通过修改settings.xml中的localRepository标签来改变本地仓库的位置,所有项目共享这个目录,便于管理。

四、生命周期

Maven最巧妙的设计之一就是生命周期。它抽象出一套构建流程,你只需要告诉它"我要打包",它就会自动执行从编译、测试到打包的所有前置步骤。

Maven有三套独立的生命周期:

1. Clean生命周期

阶段顺序:pre-clean -> clean -> post-clean。执行mvn clean会删除target目录,清理上一次构建的残留。

2. Default生命周期

这是最完整也最常用的一套,包含二十多个阶段,其中我们最常打交道的几个是:

compile:编译src/main/java下的源码,命令是mvn compile。

test:执行单元测试,命令是mvn test。

package:打包成JAR或WAR,命令是mvn package。

install:安装到本地仓库,命令是mvn install。

deploy:部署到远程私服,命令是mvn deploy。

最重要的铁律:执行任何一个阶段,它之前的所有阶段都会自动按顺序执行。比如你执行mvn package,Maven会自动先compile、再test、最后package。你不需要手动敲多个命令。

3. Site生命周期

阶段顺序:pre-site -> site -> post-site -> site-deploy。用于生成API文档和报告,一般用得不频繁。

生命周期本身是抽象的,实际工作由插件完成。比如compile阶段绑定的是maven-compiler-plugin,test阶段绑定的是maven-surefire-plugin。这种设计让你可以方便地替换或扩展插件行为。

五、继承与聚合

当项目拆分出多个模块(如common、dao、service、web)时,每个模块都有自己的pom.xml。如果没有统一管理,版本号、插件配置会散落在各处,维护起来极其痛苦。

1. 继承

创建一个父工程,它的packaging必须为pom。将公共配置集中到父工程中,子模块通过parent标签继承。

父工程可以做两件事:

第一,直接声明依赖,写在dependencies里,所有子模块自动继承这些依赖,适用于所有模块都必须用的包(如Lombok、日志)。

第二,通过dependencyManagement统一版本。子模块不会自动引入这些依赖,但如果子模块声明了相同的groupId和artifactId,则可以省略版本号,由父工程统一管控。这是最推荐的做法,既能统一版本,又不会强制给子模块添加不需要的依赖。

父工程示例:

复制代码
<dependencyManagement>
    <dependencies>
        <dependency>groupId org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.7.6</version>
            <type>pom</type>
           <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

子模块引用时就不需要写版本号了。

2. 聚合

聚合解决的是每次都要逐个模块去构建的麻烦。在父工程的modules中列出所有子模块,然后对父工程执行mvn install,Maven会按照模块间的依赖关系依次构建所有子模块。

继承和聚合通常一起使用,一个父工程既当版本管家,又当构建总管。

六、私服(Nexus)

1. 为什么需要私服

假设团队有10个人,每个人都在本地从中央仓库下载依赖,同一个JAR被下载10次,浪费带宽和时间。更关键的是,自己团队开发的公共模块怎么共享?总不能每次都把JAR包发邮件吧。

私服的两个核心作用:对外,私服充当中央仓库的代理,团队成员统一从私服下载依赖,私服首次下载后会缓存,后续走内网,速度飞快;对内,自己开发的模块发布到私服,其他人像引用第三方包一样引用它。

2. Nexus的仓库类型

Nexus将仓库分为三类:

Hosted(宿主仓库):存放本公司自己开发的构件,分为releases(正式版)和snapshots(快照版)两个仓库。版本号带SNAPSHOT的上传到snapshots,不带的上传到releases。

Proxy(代理仓库):代理外部公共仓库(如中央仓库、阿里云),私服没有的构件通过它去下载并缓存。

Group(仓库组):将多个仓库合成一个统一的访问地址,客户端只需配置这一个地址。比如把releases、snapshots、central组成一个public组。

3. Nexus安装与启动

下载Nexus 2.x或3.x压缩包,解压到无中文路径的目录。修改conf/nexus.properties可配置端口(默认8081)、数据存储目录等。以管理员身份运行cmd,进入bin目录,执行nexus.bat install注册为Windows服务,然后nexus.bat start启动。浏览器访问http://localhost:8081/nexus,默认账号admin,密码admin123登录。

4. 将项目发布到私服

第一步:在Maven的settings.xml中配置上传账号密码:

复制代码
<servers>
    <server>
        <id>releases</id>
        <username>admin</username>
        <password>admin123</password>
    </server>
    <server>
        <id>snapshots</id>
        <username>admin</username>
        <password>admin123</password>
    </server>
</servers>

第二步:在项目的pom.xml中配置分发地址:

复制代码
<distributionManagement>
    <repository>
        <id>releases</id>
        <url>http://localhost:8081/nexus/content/repositories/releases/</url>
    </repository>
    <snapshotRepository>
        <id>snapshots</id>
        <url>http://localhost:8081/nexus/content/repositories/snapshots/</url>
    </snapshotRepository>
</distributionManagement>

注意:id必须与settings.xml中server的id一致。

第三步:执行mvn deploy。Maven会根据版本号是否带SNAPSHOT自动决定上传到哪个仓库。

5. 从私服下载依赖

在settings.xml中配置镜像,将所有请求拦截并指向私服:

复制代码
<mirror>
    <id>nexusmaven</id>
    <mirrorOf>*</mirrorOf>
    <url>http://localhost:8081/nexus/content/groups/public/</url>
</mirror>

整个团队的依赖下载都走私服,提高工作效率。

6. 第三方JAR发布到私服

当遇到官方仓库没有的第三方JAR,可以用以下命令直接上传:

复制代码
mvn deploy:deploy-file -DgroupId=com.alibaba -DartifactId=fastjson -Dversion=1.1.37 -Dpackaging=jar -Dfile=fastjson-1.1.37.jar -Durl=http://localhost:8081/nexus/content/repositories/thirdparty/ -DrepositoryId=thirdparty

在settings.xml中配置thirdparty对应的server账号密码。

感谢浏览我的博客,希望这篇博客对你有所帮助。

相关推荐
大不点wow1 小时前
Java序列化与反序列化:让对象走出JVM
java·开发语言·jvm
噢,我明白了1 小时前
Java中日期和字符串的处理
java·开发语言·日期
dkbnull1 小时前
Spring Boot请求处理组件对比详解
java·spring boot
jun_bai1 小时前
Orthanc服务器使用java上传dicom影像文件
java·运维·服务器
顺风尿一寸1 小时前
记一次 Spring AOP 与定时任务引发的死锁排查
java
用户446139430272 小时前
从单体到微服务:我们项目的拆分思路和踩坑记录
java
TDengine (老段)2 小时前
TDengine 免费版说明
java·大数据·数据库·物联网·时序数据库·tdengine
码上有光2 小时前
异常和智能指针
java·大数据·c++·servlet·异常·智能指针
学计算机的计算基3 小时前
操作系统内存管理全解:虚拟内存、页表、COW、malloc、OOM一篇搞定
java·笔记·算法