Skip to content

Commit 16ce0cf

Browse files
committed
完善说明文档
1 parent 0be8417 commit 16ce0cf

File tree

1 file changed

+216
-0
lines changed

1 file changed

+216
-0
lines changed

README.md

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,219 @@
44
2. 根据Jar信息(groupId:artifactId:version)将其全部依赖下载到指定的文件夹。
55

66
> 需要注意的是环境变量必须配置:MAVEN_HOME
7+
8+
## 背景
9+
从今年开始,内网很多项目开始使用 Maven 进行 Java 工程的 Jar 包管理,使用 Maven 自然比以前的 lib 文件夹要方便很多,其优点在这里就不罗列了,谁用谁知道。
10+
11+
但是在没有网络的情况下使用 Maven 还是一件很艰难的事情,还好可以在内网搭建一个 Maven 私服。但是想要把整个 Maven 仓库同步到内网也是不可能的,因此一般是需要开发的适合把其依赖项添加到 Maven 仓库。
12+
13+
例如我需要开发 Elasticsearch 工具包,需要用到 ES 相关的 Jar 包,我可以在外网新建一个项目,然后添加我的依赖项,最后将依赖打包到内网。
14+
15+
这里存在的问题就是,全量打包是很简单的,增量打包很困难,你可以`通过每次改变本地仓库地址来实现增量打包`。但是每次手动修改 Maven 的配置文件也是一件很繁琐的事情,那么是否有工具可以帮我们实现增量打包呢?
16+
17+
答案当然是有的,本文介绍的工具将解决增量打包的问题:
18+
1. 根据 pom.xml 文件将其全部依赖下载到指定的文件夹;
19+
2. 根据 Jar 信息(`groupId:artifactId:version`)将其全部依赖下载到指定的文件夹。
20+
21+
## 根据 pom 文件获取其全部依赖
22+
### Apache Maven Invoker
23+
先看一下官方对该 API 的描述:在很多情况下,我们为了避免对系统的 Maven 环境造成污染,亦或是我们想使用用户目录作为 Maven 的工作目录进行项目构建,总之,`我们希望在一个全新的环境中启动 Maven 构建`。我们可以使用此 API 触发 Maven 构建,此 API 可以执行用户提供的命令行,可以捕获命令行运行中的错误信息,同时支持用户自定义输出(InvocationOutputHandler)和输入(InputStream)类。
24+
25+
翻译的效果很差,举个例子:我想根据pom.xml文件获取该文件的全部依赖,借助 Maven 工具,我们只需要在 pom.xml 所在文件夹下使用命令行执行`mvn dependency:tree`即可获取其全部依赖。
26+
27+
现在如果我们想通过程序的方式来实现,借助`Apache Maven Invoker`即可,下面代码展示了如何根据 pom.xml 获取其全部依赖:
28+
```
29+
/**
30+
* 根据POM文件获取全部依赖信息
31+
* @param pomFilePath POM文件路径
32+
* @return 全部依赖信息
33+
* @throws FileNotFoundException MAVEN_HOME或者POM文件不存在
34+
*/
35+
public static List<DependenceInfo> getDependenceListByPom(String pomFilePath) throws FileNotFoundException
36+
{
37+
InvocationRequest request = new DefaultInvocationRequest();
38+
File file = new File(pomFilePath);
39+
if (!file.exists())
40+
{
41+
throw new FileNotFoundException("pom文件路径有误:" + pomFilePath);
42+
}
43+
request.setPomFile(file);
44+
request.setGoals(Collections.singletonList("dependency:tree"));
45+
String output = getInvokeOutput(request);
46+
if (StrUtil.isBlank(output))
47+
{
48+
return null;
49+
}
50+
Matcher matcher = depPattern.matcher(output);
51+
List<DependenceInfo> result = new ArrayList<DependenceInfo>();
52+
while (matcher.find())
53+
{
54+
DependenceInfo dependenceInfo = new DependenceInfo(matcher.group(1),matcher.group(2),matcher.group(3),matcher.group(4));
55+
result.add(dependenceInfo);
56+
}
57+
return result;
58+
}
59+
60+
/**
61+
* 根据请求获取最终的控制台输出文本信息
62+
* @param request 请求
63+
* @return 控制台输出文本
64+
*/
65+
private static String getInvokeOutput(InvocationRequest request) throws FileNotFoundException
66+
{
67+
Invoker invoker = new DefaultInvoker();
68+
String mavenHome = getMavenHome();
69+
if (StrUtil.isBlank(mavenHome))
70+
{
71+
throw new FileNotFoundException("未检测到MAVEN_HOME,请在环境变量中进行配置。");
72+
}
73+
invoker.setMavenHome(new File(mavenHome));
74+
StringBuilderOutputHandler outputHandler = new StringBuilderOutputHandler();
75+
invoker.setOutputHandler(outputHandler);
76+
try
77+
{
78+
InvocationResult result = invoker.execute(request);
79+
if (result.getExitCode() != 0)
80+
{
81+
StaticLog.error("Build failed.-------------->");
82+
StaticLog.error(outputHandler.getOutput());
83+
return null;
84+
}
85+
return outputHandler.getOutput();
86+
}
87+
catch (Exception ex)
88+
{
89+
StaticLog.error(ex);
90+
}
91+
return null;
92+
}
93+
```
94+
95+
可以看到代码新建了一个`dependency:tree`的请求,然后在环境变量找到`MAVEN_HOME`,命令执行后通过自定义的输出类(`StringBuilderOutputHandler`)获取控制台文本内容:
96+
```
97+
import org.apache.maven.shared.invoker.InvocationOutputHandler;
98+
99+
public class StringBuilderOutputHandler implements InvocationOutputHandler
100+
{
101+
private StringBuilder output = new StringBuilder();
102+
103+
public void consumeLine(String s)
104+
{
105+
output.append(s).append("\r\n");
106+
}
107+
108+
public String getOutput()
109+
{
110+
return output.toString();
111+
}
112+
}
113+
```
114+
115+
通过上述代码便可获取 pom 文件的全部外部依赖,接下来根据依赖信息把依赖文件下载下来即可。
116+
117+
### 下载依赖文件
118+
下载依赖文件有两种方法:
119+
1. 自行根据依赖信息从 Maven 仓库下载依赖项的 jar 和 pom.xml 文件,然后建立本地路径,将下载的文件放在相应的位置即可;
120+
2. 借助工具帮助我们下载。
121+
122+
借助工具下载,有两种方法:
123+
1. 之前谷歌到的一种方法,使用`org.eclipse.aether`的一系列工具包,可以实现只需要提供 Jar 包信息即可完成全部依赖下载;
124+
2. 写文章到此刻,我觉得上述获取`dependency:tree`的方式应该也可以下载依赖。
125+
126+
#### 使用 org.eclipse.aether 工具包下载全部依赖
127+
第一种方法整体流程就是设置一下自定义的 Maven 中央仓库地址,然后设置一下本地自定义的仓库目录,设置一下网络代理,最后调用 API 就会自动下载依赖,代码如下:
128+
```
129+
/**
130+
* 根据Artifact信息下载其相关依赖,并存储到指定的文件夹
131+
* @param artifactStr Artifact信息,例如:org.apache.maven.shared:maven-invoker:3.0.1
132+
* @param storePath 存储的路径
133+
* @param theScope Scope
134+
* @return 全部依赖信息
135+
*/
136+
public static List<DependenceInfo> downloadDependency(String artifactStr, String storePath, String theScope)
137+
{
138+
DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
139+
RepositorySystem system = newRepositorySystem(locator);
140+
RepositorySystemSession session = newSession(system, storePath);
141+
142+
Artifact artifact = new DefaultArtifact(artifactStr);
143+
DependencyFilter theDependencyFilter = DependencyFilterUtils.classpathFilter(theScope);
144+
145+
RemoteRepository central = new RemoteRepository.Builder("central", "default", "http://central.maven.org/maven2/").build();
146+
CollectRequest theCollectRequest = new CollectRequest();
147+
theCollectRequest.setRoot(new org.eclipse.aether.graph.Dependency(artifact, theScope));
148+
theCollectRequest.addRepository(central);
149+
150+
DependencyRequest theDependencyRequest = new DependencyRequest(theCollectRequest, theDependencyFilter);
151+
List<DependenceInfo> resultList = new ArrayList<DependenceInfo>();
152+
try
153+
{
154+
DependencyResult theDependencyResult = system.resolveDependencies(session, theDependencyRequest);
155+
for (ArtifactResult theArtifactResult : theDependencyResult.getArtifactResults()) {
156+
Artifact theResolved = theArtifactResult.getArtifact();
157+
DependenceInfo depInfo = new DependenceInfo(theResolved.getGroupId(), theResolved.getArtifactId(), theResolved.getVersion(), JavaScopes.COMPILE);
158+
resultList.add(depInfo);
159+
}
160+
}
161+
catch (Exception e)
162+
{
163+
e.printStackTrace();
164+
}
165+
return resultList;
166+
}
167+
168+
private static RepositorySystem newRepositorySystem(DefaultServiceLocator locator) {
169+
locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
170+
locator.addService(TransporterFactory.class, FileTransporterFactory.class);
171+
locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
172+
return locator.getService(RepositorySystem.class);
173+
}
174+
175+
private static RepositorySystemSession newSession(RepositorySystem system, String storePath) {
176+
DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
177+
LocalRepository localRepo = new LocalRepository(storePath);
178+
session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo));
179+
return session;
180+
}
181+
```
182+
本方法的确可以下载到全部依赖,但是有一个缺点就是会将一些可选的依赖下载下来,如下如所示:
183+
184+
![可选的依赖会被下载](http://img.luooqi.com/FjanwBuEfm1bta1eVo6UKWmRiN-w)
185+
186+
#### 使用 Maven Invoker 下载全部依赖
187+
这个方法是写文章的时候进行验证的,效果很不错,代码简洁高效,是根据 pom 文件下载全部依赖的最佳方案。
188+
189+
其原理和上面的执行`dependency:tree`是一致的,在 Maven 官网查询到使用`dependency:resolve`可以下载到全部依赖到本地文件。那么现在只需要有个地方可以自定义本地仓库即可,幸运的是`Maven Invoker`支持自定义本地仓库:
190+
```
191+
Invoker invoker = new DefaultInvoker();
192+
String mavenHome = getMavenHome();
193+
if (StrUtil.isNotBlank(localRepo))
194+
{
195+
//在此设置本地仓库
196+
invoker.setLocalRepositoryDirectory(new File(localRepo));
197+
}
198+
invoker.setMavenHome(new File(mavenHome));
199+
```
200+
201+
## 总结
202+
如果根据 pom 文件下载依赖则推荐使用`Maven Invoker`方案;如果需要通过 Artifact 信息(`groupId:artifactId:version`)下载依赖则推荐使用`org.eclipse.aether`工具包;详细使用可以参考测试代码。
203+
204+
> 完整代码地址:https://github.com/AnyListen/maven-dependency-downloader
205+
206+
---
207+
`追求高效、有节奏的研发过程; 打造高质量、创新的研发产品。 专注技术、钟情产品!`
208+
209+
欢迎扫码关注『朗坤极客驿站』,遇见更优秀的自己。
210+
211+
![](http://img.luooqi.com/Ft0jWj-Q69B67I8DE9pIBMOa6Bl5)
212+
213+
## 参考
214+
- [maven-invoker](http://maven.apache.org/shared/maven-invoker/usage.html)
215+
- [Maven: get all dependencies programmatically
216+
](https://stackoverflow.com/questions/40813062/maven-get-all-dependencies-programmatically)
217+
- [dependency:tree](https://maven.apache.org/plugins/maven-dependency-plugin/tree-mojo.html)
218+
- [Find all direct dependencies of an artifact on Maven Central
219+
](https://stackoverflow.com/questions/39638138/find-all-direct-dependencies-of-an-artifact-on-maven-central/39641359)
220+
- [List of dependency jar files in Maven
221+
](https://stackoverflow.com/questions/278596/list-of-dependency-jar-files-in-maven)
222+
- [How to download Maven artifacts with Maven >=3.1 and Eclipse Aether](https://www.mirkosertic.de/blog/2015/12/how-to-download-maven-artifacts-with-maven-3-1-and-eclipse-aether/)

0 commit comments

Comments
 (0)