[2025-06-13] 添加Java使用Maven及Gitlab教程
All checks were successful
Publish to Confluence / confluence (push) Successful in 1m0s

This commit is contained in:
liuxiaohua 2025-06-13 16:49:05 +08:00
parent f96e972efe
commit f69e5f7a4f

View File

@ -0,0 +1,444 @@
<!-- Space: qifu -->
<!-- Parent: 后端技术&知识&规范 -->
<!-- Parent: 技术方案 -->
<!-- Parent: 基建 -->
<!-- Parent: 07-技术实现 -->
<!-- Title: 20250613-Java使用Maven及Gitlab -->
<!-- Macro: :anchor\((.*)\):
Template: ac:anchor
Anchor: ${1} -->
<!-- Macro: \!\[.*\]\((.+)\)\<\!\-\- width=(.*) \-\-\>
Template: ac:image
Url: ${1}
Width: ${2} -->
<!-- Macro: \<\!\-\- :toc: \-\-\>
Template: ac:toc
Printable: 'false'
MinLevel: 2
MaxLevel: 4 -->
<!-- Include: 杂项/声明文件.md -->
<!-- :toc: -->
# Java使用Maven及Gitlab
## Java使用Maven
### 所需依赖
```xml
<dependencies>
<!-- Maven Embedder 核心 -->
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-embedder</artifactId>
<version>3.9.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.maven/maven-compat -->
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-compat</artifactId>
<version>3.9.6</version>
</dependency>
<!-- 网络传输支持 -->
<dependency>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>maven-resolver-transport-http</artifactId>
<version>1.9.18</version>
</dependency>
<dependency>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>maven-resolver-connector-basic</artifactId>
<version>1.9.18</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.maven/maven-resolver-provider -->
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-resolver-provider</artifactId>
<version>3.9.6</version>
</dependency>
<!-- 配置文件支持 -->
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-settings-builder</artifactId>
<version>3.9.6</version>
</dependency>
</dependencies>
```
### 使用Archetype生成项目
```java
package org.jeecg.modules.devops.archetype;
import lombok.extern.slf4j.Slf4j;
import org.apache.maven.cli.MavenCli;
import org.jeecg.modules.devops.model.YuanMengOnlineService;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@Slf4j
public class MavenUtils {
private static final String ARCHETYPE_GROUP_ID = "com.yuanmeng.engine";
private static final String ARCHETYPE_ARTIFACT_ID = "engine-archetype-web";
private static final String ARCHETYPE_VERSION = "2.0.2-SNAPSHOT";
public static Path createProject(YuanMengOnlineService archetypeInfo) {
try {
// 配置项目目录
Path projectDir = Files.createTempDirectory(archetypeInfo.getArchetypeArtifactId());
log.info("项目生成目录: {}", projectDir);
// 创建 settings.xml 文件
Path settingsFile = createTempSettingsFile();
// 下面这行代码不能缺少,否则无法执行
configureSystemProperties(settingsFile);
// 使用 Maven Embedder 生成项目
generateMavenProject(projectDir.toFile(), settingsFile, archetypeInfo);
Path path = projectDir.resolve(archetypeInfo.getArchetypeArtifactId());
log.info("✅ 项目成功创建于: {}", path);
return path;
} catch (Exception e) {
log.error("❌ 出现错误: {}, {}", e.getMessage(), archetypeInfo, e);
throw new RuntimeException("项目初始化失败:" + e.getMessage());
}
}
private static void generateMavenProject(File baseDir, Path settingsFile, YuanMengOnlineService archetypeInfo) {
// String[] argsClean = new String[]{
// "-X", "dependency:resolve", "-s", settingsFile.getAbsolutePath()
// };
// int i = new MavenCli().doMain(argsClean, baseDir.getAbsolutePath(), System.out, System.err);
// boolean del = FileUtil.del("/tmp/repository");
// log.info("delete tmp directory: {}", del);
// 构建 Maven 参数
String[] mvnArgs = new String[]{
"archetype:generate",
"-DinteractiveMode=false",
"-DarchetypeGroupId=" + ARCHETYPE_GROUP_ID,
"-DarchetypeArtifactId=" + ARCHETYPE_ARTIFACT_ID,
"-DarchetypeVersion=" + ARCHETYPE_VERSION,
"-DgroupId=" + archetypeInfo.getArchetypeGroupId(),
"-DartifactId=" + archetypeInfo.getArchetypeArtifactId(),
"-Dversion=" + archetypeInfo.getArchetypeVersion(),
"-Dapp=" + archetypeInfo.getArchetypeApp(),
"-Dpackage=" + archetypeInfo.getArchetypePackage(),
"-B", // 开启批处理模式
"-s", settingsFile.toString()
};
log.info("Maven 项目生成指令:{}", String.join(" ", mvnArgs));
// 使用 Maven Embedder
MavenCli cli = new MavenCli();
int result = cli.doMain(mvnArgs, baseDir.getAbsolutePath(), System.out, System.err);
if (result != 0) {
throw new RuntimeException("Maven 项目生成失败 (退出码: " + result + ")");
}
log.info("✅ Maven 项目生成成功");
}
private static Path createTempSettingsFile() {
try {
// 创建临时 settings.xml 文件
Path settingsPath = Files.createTempFile("maven-settings", ".xml");
// 构建 settings.xml 内容
String settingsContent = buildSettingsXml();
// 写入文件
Files.write(settingsPath, settingsContent.getBytes());
// 设置文件删除钩子(程序退出时删除)
settingsPath.toFile().deleteOnExit();
return settingsPath;
} catch (IOException e) {
throw new RuntimeException("Failed to create temp settings.xml", e);
}
}
private static String buildSettingsXml() {
return "<settings xmlns=\"http://maven.apache.org/SETTINGS/1.2.0\"\n" +
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
" xsi:schemaLocation=\"http://maven.apache.org/SETTINGS/1.2.0 https://maven.apache.org/xsd/settings-1.2.0.xsd\">\n" +
" <localRepository>/tmp/repository</localRepository>\n" +
" <servers>\n" +
" <server>\n" +
" <id>private-repo</id>\n" +
" <username>developer</username>\n" +
" <password>deve@123</password>\n" +
" </server>\n" +
" </servers>\n" +
" <mirrors>\n" +
" <mirror>\n" +
" <id>private-repo</id>\n" +
" <name>Private Repository Mirror</name>\n" +
" <url>http://120.79.68.191:8081/repository/maven-public/</url>\n" +
" <mirrorOf>*</mirrorOf>\n" +
" </mirror>\n" +
" </mirrors>\n" +
" <profiles>\n" +
" <profile>\n" +
" <id>default</id>\n" +
" <repositories>\n" +
" <repository>\n" +
" <id>private-repo</id>\n" +
" <url>http://120.79.68.191:8081/repository/maven-public/</url>\n" +
" <releases><enabled>true</enabled></releases>\n" +
" <snapshots><enabled>true</enabled></snapshots>\n" +
" </repository>\n" +
" </repositories>\n" +
" <pluginRepositories>\n" +
" <pluginRepository>\n" +
" <id>private-repo</id>\n" +
" <url>http://120.79.68.191:8081/repository/maven-public/</url>\n" +
" <releases><enabled>true</enabled></releases>\n" +
" <snapshots><enabled>true</enabled></snapshots>\n" +
" </pluginRepository>\n" +
" </pluginRepositories>\n" +
" </profile>\n" +
" </profiles>\n" +
" <activeProfiles>\n" +
" <activeProfile>default</activeProfile>\n" +
" </activeProfiles>\n" +
"</settings>";
}
private static void configureSystemProperties(Path settingsPath) {
System.setProperty("maven.multiModuleProjectDirectory", System.getProperty("user.dir"));
// 使用自定义 settings.xml
System.setProperty("maven.user.settings", settingsPath.toString());
// 配置超时(可选)
System.setProperty("maven.wagon.http.readTimeout", "300000"); // 5分钟
System.setProperty("maven.wagon.http.connectionTimeout", "120000");
// 禁用 SSL 验证(如果私服使用 HTTP
System.setProperty("maven.wagon.http.ssl.insecure", "true");
System.setProperty("maven.wagon.http.ssl.allowall", "true");
// 启用调试日志(可选)
System.setProperty("org.slf4j.simpleLogger.log.org.apache.maven.cli", "info");
}
}
```
----
## Java操作Git及Gitlab
### 依赖引入
```xml
<dependencies>
<!-- https://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit -->
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>6.10.1.202505221210-r</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.gitlab4j/gitlab4j-api -->
<dependency>
<groupId>org.gitlab4j</groupId>
<artifactId>gitlab4j-api</artifactId>
<version>5.3.0</version>
</dependency>
</dependencies>
```
### Java代码调用
#### Gitlab调用工具类
```java
package org.jeecg.modules.devops.archetype;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.gitlab4j.api.GitLabApi;
import org.gitlab4j.api.GitLabApiException;
import org.gitlab4j.api.models.*;
import java.util.List;
/**
* insert description here
*
* @author liuxiaohua
* @since 2025-06-11
*/
@Slf4j
public class GitlabUtils {
private static volatile GitLabApi gitLabApi;
public static synchronized void init(ArchetypeProperties archetypeProperties) {
if (gitLabApi == null) {
synchronized (GitlabUtils.class) {
if (gitLabApi == null) {
gitLabApi = new GitLabApi(archetypeProperties.getGitlabUrl(), archetypeProperties.getAccessToken());
}
}
}
}
@SneakyThrows
public static void deleteProject(Long projectId) {
gitLabApi.getProjectApi().deleteProject(projectId);
}
@SneakyThrows
public static Project createProject(String group, String project, String desc) {
Group groupInfo = getGroupId(group);
if (groupInfo == null || groupInfo.getId() == null) {
throw new RuntimeException("获取分组信息失败");
}
Project gitLabRepository = createRepository(groupInfo.getId(), project, desc);
if (gitLabRepository == null || gitLabRepository.getId() == null) {
throw new RuntimeException("创建仓库失败");
}
return gitLabRepository;
}
@SneakyThrows
public static ProtectedBranch protectedBranch(Long projectId, Integer retryTimes) {
try {
// 获取已保护的分支
List<ProtectedBranch> protectedBranches = gitLabApi.getProtectedBranchesApi().getProtectedBranches(projectId);
log.info("protected branches: {}", JSONObject.toJSONString(protectedBranches));
// 保护分支
if (CollUtil.isNotEmpty(protectedBranches)
&& protectedBranches.stream().map(ProtectedBranch::getName).anyMatch("master"::equalsIgnoreCase)) {
// 取消旧的配置
unprotectedBranch(projectId);
}
// 添加分支保护
return gitLabApi.getProtectedBranchesApi()
.protectBranch(projectId, "master", AccessLevel.NONE, AccessLevel.MAINTAINER);
} catch (GitLabApiException e) {
log.warn("protected branch failed: {}", e.getMessage());
}
if (retryTimes < 3) {
return protectedBranch(projectId, ++retryTimes);
}
log.warn("protected branch failure: {}", retryTimes);
throw new RuntimeException(ArchetypeUtils.WARN_MESSAGE_1);
}
private static void unprotectedBranch(Long projectId) {
try {
gitLabApi.getProtectedBranchesApi().unprotectBranch(projectId, "master");
log.info("unprotected branch master success");
} catch (Exception e) {
log.warn("unprotected branch error: {}", e.getMessage());
}
}
/**
* 根据分组路径获取分组ID
*/
private static Group getGroupId(String groupPath) throws GitLabApiException {
return gitLabApi.getGroupApi().getGroup(groupPath);
}
/**
* 在 GitLab 创建新仓库
*/
private static Project createRepository(Long groupId,
String projectName,
String desc) {
try {
Project project = new Project()
.withName(projectName)
.withDefaultBranch("master")
.withDescription(desc)
.withNamespaceId(groupId)
.withVisibility(Visibility.PRIVATE); // 私有库用 PRIVATE
return gitLabApi.getProjectApi().createProject(project); // 返回 HTTPS 仓库地址
} catch (Exception e) {
log.warn("create project error: {}", e.getMessage());
}
return null;
}
}
```
#### Git调用工具类
```java
package org.jeecg.modules.devops.archetype;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.springframework.stereotype.Component;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* insert description here
*
* @author liuxiaohua
* @since 2025-06-11
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class GitUtils {
/**
* 初始化本地仓库并推送到 GitLab
*/
public static void pushLocalCodeToGitLab(String remoteUrl,
String codePath,
ArchetypeProperties archetypeProperties) {
File localPath = new File(codePath);
CredentialsProvider credentials = new UsernamePasswordCredentialsProvider(
archetypeProperties.getUsername(), archetypeProperties.getAccessToken());
try (Git git = Git.init().setDirectory(localPath).call()) {
// 添加所有文件
git.add().addFilepattern(".").call();
// 提交更改
git.commit()
.setMessage(String.format("\uD83C\uDF89 [%s] 初始化提交",
new SimpleDateFormat("yyyy-MM-dd").format(new Date())))
.call();
// 添加远程仓库
git.remoteAdd()
.setName("origin")
.setUri(new URIish(remoteUrl))
.call();
// 推送到 GitLab
git.push()
.setRemote("origin")
.setCredentialsProvider(credentials)
.setForce(true)
.setPushAll()
.call();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
```