层次分明井然有条,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang包管理机制(package)EP10

简介: Go lang使用包(package)这种概念元素来统筹代码,所有代码功能上的可调用性都定义在包这个级别,如果我们需要调用依赖,那就“导包”就行了,无论是内部的还是外部的,使用import关键字即可。但事情往往没有那么简单,Go lang在包管理机制上走了不少弯路,虽然1.18版本的包管理已经趋于成熟,但前事不忘后事之师,我们还是需要了解一下这段历史。

Go lang使用包(package)这种概念元素来统筹代码,所有代码功能上的可调用性都定义在包这个级别,如果我们需要调用依赖,那就“导包”就行了,无论是内部的还是外部的,使用import关键字即可。但事情往往没有那么简单,Go lang在包管理机制上走了不少弯路,虽然1.18版本的包管理已经趋于成熟,但前事不忘后事之师,我们还是需要了解一下这段历史。

环境变量

一般情况下,go lang在系统中会依赖两个环境变量,分别是:GOPATH 和 GOROOT,有点类似于Python的解释器目录的概念,GOROOT 这个变量的作用就是为了告诉当前运行的 Go 进程当前 Go安装路径,当要运行的时候去什么位置找GoSDK相关的类。

GOPATH 这个变量的设定是默认所有的项目和引用的第三方包都下载到GOPATH的src目录下,也就是说,你的代码只要不在GOPATH里,就没法编译。我们可以理解这个目录就是项目目录,这一点跟Python区别还是挺大的,Python的pip包管理机制至今都是经典的包依赖设计模式。

GOPATH这种设定方式会导致很多问题,比方说我有很多go lang项目,而每个项目又都有自己的GOPATH目录,那所有依赖的包就都在项目各自目录下,导致重复包过多,反之,如果大家都用一个GOPATH目录,又会带来版本问题,每个项目依赖的包版本不一致,到底怎么进行统筹又是一个问题,这就是GOPATH设定早期被人诟病的原因。

Go modules

针对因为GOPATH这种反人类的设计而导致的包管理乱象,Go lang 1.11 版本的时候推出了新特性 Go modules。

Go modules 是官方推出的依赖管理工具,Go modules 提供了3个重要的功能:

1.go.mod 文件,它和Node的package.json文件的功能相似,都是记录当前项目的依赖关系。

2.机器生成的传递依赖项描述文件 : go.sum。
3.不再有 GOPATH 的反人类限制,所有代码可以位于电脑的任何路径中。

go lang1.18早已经集成了Go modules,但就像golang1.18第一篇精炼教程里写得那样,默认还是反人类的GOPATH模式,你想用,得通过命令手动开启:

go env -w GO111MODULE=on

为了能够向下兼容维护go1.11版本以下的项目,可以设置为兼容模式:

go env -w GO111MODULE=auto

三方包管理

三方包指的是外部开源的一些包,而使用go modules机制管理三方包相对简单,首先新建一个项目目录,比如c:/www/test

cd c:/www/test

进入项目目录后,初始化项目:

go mod init test

系统返回:

C:\Users\liuyue\www\test>go mod init test  
go: creating new go.mod: module test  
  
C:\Users\liuyue\www\test>dir  
  
  
2022/08/12  12:13    <DIR>          .  
2022/08/12  12:13    <DIR>          ..  
2022/08/12  12:13                21 go.mod  
               1 个文件             21 字节  
               2 个目录 228,767,113,216 可用字节

这里注意项目名和目录名称要吻合,go.mod 文件是开启 modules 的必备配置文件。它记录了当前项目引用的包数据信息。go.mod 文件中定义了以下关键词:

module:用于定义当前项目的模块路径
go:用于设置Go 版本信息
require:用于设置一个特定的模块版本
exclude:用于从使用中排除一个特定的模块版本
replace:用于将一个模块版本替换为另一个模块版本

接着,运行go get命令安装三方包,比如说gin框架:

go get github.com/gin-gonic/gin

随后编写main.go文件,也就是main包:

package main  
  
import (  
    "github.com/gin-gonic/gin"  
)  
  
func main() {  
    d := gin.Default()  
    d.GET("/", func(c *gin.Context) {  
        c.JSON(200, gin.H{"message": "hello go 1.18", "data": ""})  
    })  
    d.Run("127.0.0.1:5000")  
}

这里将刚刚安装的三方包导入,然后再main函数中调用。

紧接着启动服务:

go run main.go

系统返回:

C:\Users\liuyue\www\test>go run main.go  
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.  
  
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.  
 - using env:   export GIN_MODE=release  
 - using code:  gin.SetMode(gin.ReleaseMode)  
  
[GIN-debug] GET    /                         --> main.main.func1 (3 handlers)  
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.  
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.  
[GIN-debug] Listening and serving HTTP on 127.0.0.1:5000  
[GIN] 2022/08/12 - 12:19:20 |[97;42m 200 [0m|      3.8876ms |       127.0.0.1 |[97;44m GET     [0m "/"  
[GIN] 2022/08/12 - 12:19:21 |[90;43m 404 [0m|            0s |       127.0.0.1 |[97;44m GET     [0m "/favicon.ico"

说明三方包的服务已经启动了,访问http://localhost:5000:

这就是一个go lang项目导入三方包的具体流程。

接着我们打开项目中的go.mod文件:

module test  
  
go 1.18  
  
require (  
    github.com/gin-contrib/sse v0.1.0 // indirect  
    github.com/gin-gonic/gin v1.8.1 // indirect  
    github.com/go-playground/locales v0.14.0 // indirect  
    github.com/go-playground/universal-translator v0.18.0 // indirect  
    github.com/go-playground/validator/v10 v10.10.0 // indirect  
    github.com/goccy/go-json v0.9.7 // indirect  
    github.com/json-iterator/go v1.1.12 // indirect  
    github.com/leodido/go-urn v1.2.1 // indirect  
    github.com/mattn/go-isatty v0.0.14 // indirect  
    github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect  
    github.com/modern-go/reflect2 v1.0.2 // indirect  
    github.com/pelletier/go-toml/v2 v2.0.1 // indirect  
    github.com/ugorji/go/codec v1.2.7 // indirect  
    golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect  
    golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect  
    golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect  
    golang.org/x/text v0.3.6 // indirect  
    google.golang.org/protobuf v1.28.0 // indirect  
    gopkg.in/yaml.v2 v2.4.0 // indirect  
)

三方包gin以及gin所依赖的三方包都一目了然。

内部包管理

内部包指的是项目内部的包,一般情况下就是自己开发的可复用的包,go modules也可以对内部包进行管理,在刚刚创建的test项目中,新建目录my:

C:\Users\liuyue\www\test>mkdir my  
  
C:\Users\liuyue\www\test>dir  
 驱动器 C 中的卷没有标签。  
 卷的序列号是 0A64-32BF  
  
 C:\Users\liuyue\www\test 的目录  
  
2022/08/12  12:39    <DIR>          .  
2022/08/12  12:13    <DIR>          ..  
2022/08/12  12:18             1,046 go.mod  
2022/08/12  12:18             6,962 go.sum  
2022/08/12  12:16               228 main.go  
2022/08/12  12:39    <DIR>          my  
               3 个文件          8,236 字节  
               3 个目录 228,568,178,688 可用字节  
  
C:\Users\liuyue\www\test>

然后再my目录新建一个my.go文件:



package my  
  
import "fmt"  
  
func New() {  
  fmt.Println("我是my包")  
}

这里我们声明包与目录名一致,随后再声明一个New函数。

接着改写main.go内容:

package main  
  
import (  
    "fmt"  
    "test/my"  
)  
  
func main() {  
    fmt.Println("main run")  
    // 使用my  
    my.New()  
}

程序返回:

main run  
我是my包

触类旁通,如果包不在同一个项目下:



├── moduledemo  
│   ├── go.mod  
│   └── main.go  
└── mypackage  
    ├── go.mod  
    └── mypackage.go

这个时候,mypackage也需要进行module初始化,即拥有一个属于自己的go.mod文件,内容如下:

module mypackage  
  
go 1.18

然后我们在moduledemo/main.go中按如下方式导入:

import (  
    "fmt"  
    "mypackage"  
)  
func main() {  
    mypackage.New()  
    fmt.Println("main")  
}

结语

对于 Go lang 的项目来说,如果没有开启 go mod模式,那么项目就必须放在 GOPATH/src 目录下,项目本身也可以看作为一个本地包,可以被其它 GOPATH/src目录下的项目引用,同时也可以被go modules模式的项目引入,因为go modules的原理是先去GOPATH/src目录下寻址,如果没有才去指定目录寻址,但反过来,如果是放在go modules项目中的本地包,GOPATH/src目录下的项目就无法引用,因为GOPATH规定项目都必须得放在GOPATH/src目录下,它只会在GOPATH/src目录下寻址,这是我们需要注意的地方。

相关文章
|
5月前
|
Cloud Native 安全 Java
Go语言深度解析:从入门到精通的完整指南
🌟蒋星熠Jaxonic,Go语言探索者。深耕云计算、微服务与并发编程,以代码为笔,在二进制星河中书写极客诗篇。分享Go核心原理、性能优化与实战架构,助力开发者掌握云原生时代利器。#Go语言 #并发编程 #性能优化
541 43
Go语言深度解析:从入门到精通的完整指南
|
10月前
|
人工智能 安全 算法
Go入门实战:并发模式的使用
本文详细探讨了Go语言的并发模式,包括Goroutine、Channel、Mutex和WaitGroup等核心概念。通过具体代码实例与详细解释,介绍了这些模式的原理及应用。同时分析了未来发展趋势与挑战,如更高效的并发控制、更好的并发安全及性能优化。Go语言凭借其优秀的并发性能,在现代编程中备受青睐。
327 33
|
6月前
|
Cloud Native 安全 Java
Go语言深度解析:从入门到精通的完整指南
🌟 蒋星熠Jaxonic,执着的星际旅人,用Go语言编写代码诗篇。🚀 Go语言以简洁、高效、并发为核心,助力云计算与微服务革新。📚 本文详解Go语法、并发模型、性能优化与实战案例,助你掌握现代编程精髓。🌌 从goroutine到channel,从内存优化到高并发架构,全面解析Go的强大力量。🔧 实战构建高性能Web服务,展现Go在云原生时代的无限可能。✨ 附技术对比、最佳实践与生态全景,带你踏上Go语言的星辰征途。#Go语言 #并发编程 #云原生 #性能优化
|
8月前
|
JSON 人工智能 Go
在Golang中序列化JSON字符串的教程
在Golang中,使用`json.Marshal()`可将数据结构序列化为JSON格式。若直接对JSON字符串进行序列化,会因转义字符导致错误。解决方案包括使用`[]byte`或`json.RawMessage()`来避免双引号被转义,从而正确实现JSON的序列化与反序列化。
473 7
|
8月前
|
Java Shell Maven
【Azure Container App】构建Java应用镜像时候遇无法编译错误:ERROR [build 10/10] RUN ./mvnw.cmd dependency:go-offline -B -Dproduction package
在部署Java应用到Azure Container App时,构建镜像过程中出现错误:“./mvnw.cmd: No such file or directory”。尽管项目根目录包含mvnw和mvnw.cmd文件,但依然报错。问题出现在Dockerfile构建阶段执行`./mvnw dependency:go-offline`命令时,系统提示找不到可执行文件。经过排查,确认是mvnw文件内容异常所致。最终通过重新生成mvnw文件解决该问题,镜像成功构建。
420 2
|
11月前
|
存储 算法 数据可视化
【二叉树遍历入门:从中序遍历到层序与右视图】【LeetCode 热题100】94:二叉树的中序遍历、102:二叉树的层序遍历、199:二叉树的右视图(详细解析)(Go语言版)
本文详细解析了二叉树的三种经典遍历方式:中序遍历(94题)、层序遍历(102题)和右视图(199题)。通过递归与迭代实现中序遍历,深入理解深度优先搜索(DFS);借助队列完成层序遍历和右视图,掌握广度优先搜索(BFS)。文章对比DFS与BFS的思维方式,总结不同遍历的应用场景,为后续构造树结构奠定基础。
531 10
|
存储 Go
Go 语言入门指南:切片
Golang中的切片(Slice)是基于数组的动态序列,支持变长操作。它由指针、长度和容量三部分组成,底层引用一个连续的数组片段。切片提供灵活的增减元素功能,语法形式为`[]T`,其中T为元素类型。相比固定长度的数组,切片更常用,允许动态调整大小,并且多个切片可以共享同一底层数组。通过内置的`make`函数可创建指定长度和容量的切片。需要注意的是,切片不能直接比较,只能与`nil`比较,且空切片的长度为0。
355 3
Go 语言入门指南:切片
|
5月前
|
存储 安全 Java
【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
312 2
|
7月前
|
Cloud Native 安全 Java
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
465 1
|
7月前
|
Cloud Native Go API
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
519 0