ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java 转 go 学习 - 项目管理

Java 转 go 学习 - 项目管理 文章目录包管理1.1 go.mod1.2 go.sum1.3 初始化一个项目1.4 三种编译方式1.4.1 File 模式1.4.2 Package 模式1.4.3 Directory 模式1.5 查看依赖信息包管理Go 的包管理体系经历了「无官方管理 → 第三方工具混战 → 官方标准化」的演进过程核心是解决依赖隔离、版本控制、环境统一三大问题。GOPATHGO 1.0 发布仅仅提供GOPATH环境变量机制所有项目和依赖必须放在GOPATH/src目录下固定目录结构GOPATH/{src/bin/pkg}依赖通过go get下载但仅拉取最新版本无版本号概念也无法指定版本项目共享GOPATH下的依赖不同项目依赖同一个包的不同版本会冲突.vendorGO 1.5 引入GO 1.6 默认核心作用是本地化存储项目依赖的源代码副本解决依赖版本一致性问题比如 Java 的lib版本本质上是让项目自带依赖避免版本不一样导致的编译或者运行异常.- 项目根目录下的vendor文件夹会优先被go编译器识别存放当前项目的专属依赖.GO MODULEGo 1.11正式引入、Go 1.13成为默认依赖管理工具的官方依赖管理方案默认开启 GO111MODULEautoGO 1.16成为默认模式废弃 GOPATH 默认彻底解决了传统 GOPATH 模式的核心痛点全局依赖、版本冲突、多版本共存难。它的核心目标是让每个项目拥有独立的依赖版本管理实现依赖的可复现、可追溯、可隔离。1.1 go.mod一个包含 go.mod 文件的目录就是一个 Module是 Go 依赖管理的基本单元比如下面1.3节生成的 go.modmodulego-learn-1go1.25.0require github.com/gin-gonic/gin v1.12.0require(github.com/bytedance/gopkg v0.1.3// indirectgithub.com/bytedance/sonic v1.15.0// indirectgithub.com/bytedance/sonic/loader v0.5.0// indirectgithub.com/cloudwego/base64x v0.1.6// indirectgithub.com/gabriel-vasile/mimetype v1.4.12// indirectgithub.com/gin-contrib/sse v1.1.0// indirectgithub.com/go-playground/locales v0.14.1// indirectgithub.com/go-playground/universal-translator v0.18.1// indirectgithub.com/go-playground/validator/v10 v10.30.1// indirectgithub.com/goccy/go-json v0.10.5// indirectgithub.com/goccy/go-yaml v1.19.2// indirectgithub.com/json-iterator/gov1.1.12// indirectgithub.com/klauspost/cpuid/v2 v2.3.0// indirectgithub.com/leodido/go-urn v1.4.0// indirectgithub.com/mattn/go-isatty v0.0.20// indirectgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd// indirectgithub.com/modern-go/reflect2 v1.0.2// indirectgithub.com/pelletier/go-toml/v2 v2.2.4// indirectgithub.com/quic-go/qpack v0.6.0// indirectgithub.com/quic-go/quic-gov0.59.0// indirectgithub.com/twitchyliquid64/golang-asm v0.15.1// indirectgithub.com/ugorji/go/codec v1.3.1// indirectgo.mongodb.org/mongo-driver/v2 v2.5.0// indirectgolang.org/x/arch v0.22.0// indirectgolang.org/x/crypto v0.48.0// indirectgolang.org/x/net v0.51.0// indirectgolang.org/x/sys v0.41.0// indirectgolang.org/x/text v0.34.0// indirectgoogle.golang.org/protobuf v1.36.10// indirect)module项目模块名称为go-learn-1相当于项目的唯一 ID在项目内导入自己的包时需要以这个名称为前缀比如 import “go-learn-1/utils”Go Modules 基于这个名称识别项目边界管理依赖.require声明项目依赖的模块.indirect注释表示这是间接依赖不是直接导入的而是 gin 框架本身依赖的包.1.2 go.sumgo.sum 是依赖的 “哈希校验清单”自动生成记录每个依赖版本的加密哈希值作用校验依赖完整性下载依赖时Go 会对比本地哈希与 go.sum 中的值防止依赖被篡改中间人攻击.锁定版本确保同一模块在不同环境下载的依赖版本完全一致.比如github.com/gin-gonic/gin v1.9.1h1:k4675tC5Uf2H54fx43Cfd1UoTWpAa8lS8DMhBjAprogithub.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAPKLuI3SUcPTeU每行格式模块路径 版本 哈希值h1: 开头是二进制哈希go.mod 哈希单独列.为什么需要两种哈希值h1 开头的是用于校验源码完整性而 go.mod 命令开头的是轻量校验依赖声明。简单来说就是 h1 是源码文件生成的用于确保源码没有被篡改而 go.mod 不需要下载源码只通过go.mod生成哈希这种就是只校验依赖清单比如执行go mod graphGo 只需要知道各个模块的依赖关系不会下载完整源码此时只校验 go.mod 的哈希即可如果校验通过就可以直接从 go.mod 里面读出依赖大幅减少网络请求。那如果哈希值被修改了比如我们修改 go.sum 里面的一个哈希这种情况下 go 会探测到缓存GOMODCACHE里面的依赖的哈希和 go.sum 不一致编译失败比如下面的例子我们修改下一个依赖的 hash将后面的 去掉下面就是1.3节初始化项目之后生成的go.sumgithub.com/bytedance/gopkg v0.1.3h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/Mgithub.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJeJgyCzdjSc477QF3p7ubbtiKARP3TxducMgithub.com/bytedance/sonic v1.15.0h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSEgithub.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0kgithub.com/bytedance/sonic/loader v0.5.0h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgEgithub.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRogithub.com/cloudwego/base64x v0.1.6h1:t11wG9AECkCDk5fMSoxmufanudBtJ/HemLstXDLI2Mgithub.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPUgithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsIc5H38github.com/davecgh/go-spew v1.1.1h1:vj9j/u1bqnvCEfJOwUhtlOARqs3rkHYY13jYWTU97cgithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsIc5H38github.com/gabriel-vasile/mimetype v1.4.12h1:e9hWvmLYvtp846tLHam2oqitpguFiYCKbn0w9jyqwgithub.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3sgithub.com/gin-contrib/sse v1.1.0h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05wgithub.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwMgithub.com/gin-gonic/gin v1.12.0h1:b3YAbrZtnf8N//yjKeU2MQsh2mY5htkZidOM7O0wG8github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLcgithub.com/go-playground/assert/v2 v2.2.0h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHClYKSsYSR8sgithub.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnbZQfWdccd7VUvScMdVu0Titje2rxJ4github.com/go-playground/locales v0.14.1h1:EWaQ/wswjilfKLTECiXz7Rh3BjFhfDFKv/oXslEjJA......1.3 初始化一个项目创建项目使用的是 goland初始化的时候指定文件路径以及 GOROOT左边选第一个 Go默认就是 GO MODULE如果选第二个就是通过 GOPATH 管理的项目.项目创建成功之后会生成 go.mod 和一个 main 文件.下面改下 main 文件.// main.go示例代码导入gin框架packagemainimportgithub.com/gin-gonic/ginfuncmain(){r:gin.Default()r.GET(/,func(c*gin.Context){c.String(200,Hello Go Module!)})r.Run(:8080)}通过go get获取依赖默认最新版本也可以指定版本gogetgithub.com/gin-gonic/gin// 格式go get 模块路径版本号// go get github.com/gin-gonic/ginv1.8.1 // 指定版本 1.8.1// go get github.com/gin-gonic/ginlatest // 升级到最新版// go get github.com/gin-gonic/ginmaster // 拉取master分支自动生成伪版本然后通过go mod tidy清理不需要的依赖通过删除go.mod中没有被代码引用的依赖同时补充代码中使用但是go.mod中没有声明的依赖保持go.mod/go.sum简洁.gomod tidy下面直接运行 main 方法如果是用的 go 命令就需要使用go build先编译再通过go run main.go运行 main 方法// 编译gobuild-ogo-learn-1// 运行 main 方法gorun main.go接下来浏览器访问 localhost:8080输出如下1.4 三种编译方式goland 编译项目的时候提供了三种方式下面看看这三种方式有什么不同File单个.go源文件命令等价go build main.go仅仅编译当前选中的文件同包的其他文件不会被自动包含依赖同包的代码会报错undefined就是用来测试单个 main 文件.Package编译某个go包比如go build go-learn-1/learn-1/pkg编译该包下所有 .go 文件除测试文件自动处理同包依赖这种模式是正式 Go 项目的标准包结构main 包在指定目录.Directory编译本地文件系统的目录路径比如go build D:\go-code\go-learn-1\learn-1\directory和 Package 一致因为 Go 中目录与包一一对应会编译该目录下所有 .go 文件但是这种模式下所有的 go 文件必须要定义成同一个 package 才能运行比如下面项目结构中的 pkg 如果用 Directory 模式去编译就没办法通过因为 app.go 和 util.go 不是在同一个包下的.下面就是几个例子首先项目结构如下1.4.1 File 模式learn-1/single/main.go 的代码如下packagemainimportfmtfuncmain(){fmt.Println(✅ Running as [Single]: learn-1/single)}将 goland 配置改成 File 编译指定这个 main 文件输出结果如下✅ Running as[Single]:learn-1/single1.4.2 Package 模式package 模式下可以指定某个包进行编译下面我们指定编译包为go-learn-1/learn-1/pkg.app.go 代码如下packagecmdfuncGetHello2()string{return✅ Running as [pkg]: learn-1/pkg/cmd/app.go}util.go 代码如下packagecmdfuncGetHello1()string{return✅ Running as [pkg]: learn-1/pkg/cmd/util.go}注意一个目录只能又一个 main 包main 包对应可执行文件同一个目录下是不能生成多个可执行文件的同时非同包的情况下引用其他包的方法需要通过 import 引入比如下面的go-learn-1/learn-1/pkg里面定义的两个方法.packagemainimport(fmtgo-learn-1/learn-1/pkg/cmd)funcmain(){fmt.Println(cmd.GetHello1())fmt.Println(cmd.GetHello2())}执行结果如下✅ Running as[pkg]:learn-1/pkg/cmd/util.go✅ Running as[pkg]:learn-1/pkg/cmd/app.go1.4.3 Directory 模式将模式改成 Directory指定目录为 D:\go-code\go-learn-1\learn-1\directory.app.go 文件如下packagemainfuncGetHello2()string{return✅ Running as [directory]: learn-1/directory/app.go}util.go 文件如下packagemainfuncGetHello1()string{return✅ Running as [directory]: learn-1/directory/util.go}main.go 文件如下packagemainimport(fmt)funcmain(){fmt.Println(GetHello1())fmt.Println(GetHello2())}输出结果如下✅ Running as[directory]:learn-1/directory/util.go✅ Running as[directory]:learn-1/directory/app.go### 1.4.4 总结表项对比维度FilePackageDirectory指定目标单个.go源文件Go 包的导入路径如go-learn-1/learn-1/pkg本地文件系统的目录路径如D:\go-code\go-learn-1\learn-1\directory等价命令go build main.gogo build go-learn-1/learn-1/pkggo build D:\go-code\go-learn-1\learn-1\directory依赖处理仅编译当前选中文件同包其他文件不会被自动包含依赖同包代码会报 undefined 错误编译该包下所有 .go 文件除测试文件自动处理同包依赖和 Package 一致因为 Go 中目录与包一一对应会编译该目录下所有 .go 文件适用场景快速测试无同包依赖的单文件 Demo正式 Go 项目标准包结构main 包在指定目录不想手动输入包路径直接通过 IDE 浏览选择目录可运行性必须是main包且包含 main() 函数若为main包可运行若为库包仅编译检查无法运行同 Package必须是main包才能运行1.5 查看依赖信息最后来看一些命令可以查询相关的依赖信息go mod graph查看当前模块的所有依赖go mod why github.com/gin-gonic/gin查看为什么依赖这个库go list -m github.com/gin-gonic/gin查看依赖版本go list -m all列出所有依赖以及版本go 查找依赖的时候优先级从高到底是项目根目录的vendor/→GOMODCACHEgo env GOMODCACHE 查看默认 $GOPATH/pkg/mod→ 报错.依赖下载流程解析 go.mod 中的依赖 → 检查 GOMODCACHE 是否已有该版本有则直接用→ 无则从远程仓库下载 → 校验哈希对比 go.sum→ 存入 GOMODCACHE.版本缓存:GOMODCACHE中按模块路径版本存储不同版本互不干扰例github.com/gin-gonic/ginv1.8.1 和 github.com/gin-gonic/ginv1.9.1 是两个独立目录.
返回列表