在之前的博客中详细介绍了通过 Maven
实现项目的依赖管理,以及实现工程的编译构建。
熟话说巧妇难为无米之炊,实现依赖管理的前提都是需要中央仓库或本地仓库存在对应的依赖,如果依赖不存在也只能望洋兴叹。
但这类场景在开发中却又并不少见,对于某个功能涉及的依赖包经过一顿网上冲浪,终于找到了依赖文件,但依赖并未上传中央仓库无法直接集成依赖。
1. 系统依赖
幸运的是 Maven
的依赖导入提供了本地文件的引用导入,在定义依赖时将 scope
作用域定义为 system
同时通过 systemPath
用于指定依赖文件的路径。
例如下述示例即读取 D:/repo/demo-one-1.0-SNAPSHOT.jar
目录文件作为依赖包,而非从默认配置的 Maven 仓库中检索查找。如此一来,即便某个依赖文件不存在于仓库之中,仍能正常引入工程。
<dependency>
<groupId>xyz.ibudai</groupId>
<artifactId>demo-one</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>system</scope>
<systemPath>D:/repo/demo-one-1.0-SNAPSHOT.jar</systemPath>
</dependency>
2. 仓库结构
在继续下一步的介绍之前,让我们先来看一下 Maven
仓库对于依赖文件是以何种结构进行管理?
在定义一个 Maven
工程时,我们都知道 groupId
,artifactId
与 version
三者是必不可少,分别声明了依赖的所属、名称以及版本信息,例如下述示例所展示:
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>xyz.ibudai</groupId>
<artifactId>demo-one</artifactId>
<version>1.0-SNAPSHOT</version>
</project>
那你是否又好奇这个模块又是如何存储在 Maven
仓库之中?
实际上存储结构也简单明了,打开 Maven
配置的仓库目录中,可以看到依赖的存储路径是以 groupId
,artifactId
与 version
三者作为目录路径存储,同时针对 groupId
中的 .
按次序拆分为多个目录。
例如上述定义的模块通过 install
命令后将生成的文件目录结构如下,而文件名默认以 <artifactId>-<version>.jar
的命名规则存在。
3. 工程编译
了解了上述概念后,让我们来看一下 Maven
又是如何对工程进行编译打包。
默认 Maven
在打包构建时并不会将模块所依赖的模块一同打包进 jar 可执行文件,而是只会打包当前工程 src/main
包路径的代码文件。因此,若需要将工程的依赖一并打包通常需要利用到 Assembly
等构建插件,在之前的博客中分享过了如何使用这里就不再重复展开。
这里就直接贴出完整的 Assembly
插件打包配置,内容如下:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<finalName>${project.artifactId}-${project.version}-all</finalName>
<appendAssemblyId>false</appendAssemblyId>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<attach>false</attach>
<archive>
<manifest>
<!-- 替换为主类完整限定名 -->
<mainClass>fully.qualified.MainClass</mainClass>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
在工程 pom.xml
文件中添加上述配置之后执行 packge
命令即可在 target
目录下生成 xxx-all.jar
可执行文件,文件内包含了工程的源码以及所有依赖的模块。
但如果你仔细观察的话,文件的确是将依赖的模块一并打包,但是刚才提到的 scope=system
依赖却并没有出现在打包后的文件中。
是的,你并没有看错,Assembly
插件将会跳过 scope=system
依赖模块,那又该如何处理?
这里就要回到刚才介绍的依赖文件存储结构了,不仅文件的生成是基于 groupId
,artifactId
与 version
三者,在定义 dependency
时 Maven
同样也是由此为依据在仓库中检索文件。但解决方案就有了,只要根据此规则为 scope=system
依赖创建同样的目录,那即可直接引用依赖无需将作用域定义于 system
。
最简单的方式就是手动创建目录,但 Maven
对此提供了更方便的方式,命令模板如下:
mvn install:install-file \
-Dfile=<path-to-file> \
-DgroupId=<my-groupId> \
-DartifactId=<my-artifactId> \
-Dversion=<my-version> \
-Dpackaging=<my-packaging> \
-DlocalRepositoryPath=<path-to-repo>
例如之前提到 demo-one-1.0-SNAPSHOT.jar
文件,通过下述命令即可在 Maven
仓库中生成对应的结构的目录。
mvn install:install-file \
-Dfile= \
-DgroupId=xyz.ibudai \
-DartifactId=demo-one \
-Dversion=1.0-SNAPSHOT \
-Dpackaging=jar \
-DlocalRepositoryPath=D:/repo/demo-one-1.0-SNAPSHOT.jar
那问题也迎刃而解,此时在项目中依赖 demo-one
模块则需要通过 system
执行,和其它依赖一样引用即可。与此同时,再通过 Assembly
插件也可正常实现打包构建。
<dependency>
<groupId>xyz.ibudai</groupId>
<artifactId>demo-one</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
上述的方式适用于本地的项目编译构建,依赖文件在本地仓库中是生成了,但线上私服仓库中仍然不存在此依赖文件。
因此,若需要实现在线编译,同样需要通过 deploy
命令将依赖推送到私服,命令格式如下:
mvn deploy:deploy-file \
-Dfile=<path-to-file> \
-DgroupId=<my-groupId> \
-DartifactId=<my-artifactId> \
-Dversion=<my-version> \
-Dpackaging=<my-packaging> \
-DrepositoryId=<repository-id> \
-Durl=<repository-url>