介绍包的相关概念以及使用方法…
在golang
中,所有源文件都属于一个包,golang
的包具有以下特性:
- 包可以被其他包引用
- 每个
golang
程序只有一个main
包 - 包的主要用途是提高代码的可复用性
一、GOPATH
环境变量
1.什么是GOPATH
?
golang
包的使用依赖一个重要的环境变量:GOPATH
,无论是自己的代码还是第三方代码,都以GOPATH
作为工作目录和一套完整的工程目录规则。GOPATH
目录约定有三个子目录,主要用于:
src
:存放各种包源代码,src里的一个目录就是一个包pkg
:存放编译源代码后的类库bin
:存放编译源代码后的可执行程序
GOPATH
允许多个目录,设置多个目录时,需要使用环境变量分隔符来区分。Unix/Linux
的环境变量分隔符是英文输入法下的冒号:
,windows
的环境变量分隔符是英文分号;
使用GOPATH
时,golang会在以下目录中搜索包:
GOROOT/src
:该目录保存了Go标准库里代码GOPATH/src
:该目录保存了应用自身的代码和第三方依赖的代码
2.设置GOPATH环境变量
在Unix/Linux
中,使用sudo vim/etc/profile
打开系统profile
文件,并追加以下内容设置GOPATH
环境变量,同时设置GO111MODULE
为关闭状态,GO111MODULE
环境变量我们在文章中后面部分说明
$ export GOPATH=/Users/pan/go # 把自己的golang项目目录作为GOPATH的变量
$ export GO111MODULE=off # 关闭golang 11.1 版本以后的模块功能
使用source /etc/profile
命令重新加载环境变量
3.包的使用
3.1 main
包与包的引入
- 引入系统包
我们可以通过package
关键字定义一个包,使用import
引入其他包,如果需要引入多个包,可以将各个包声明在括号内
package main
import (
// 引入系统的包
"fmt"
)
func main() {
// 调用系统包的函数
fmt.Println("Hello World")
}
在以上代码中,我们通过main
关键字定义了main
包,主函数main
必须位于main
包下。程序中引入了fmt
包,该包由golang
本身提供。
- 引入自定义包
我们在GOPATH/src
目录下创建两个文件:
文件1,库文件位于jkdev.cn/test/mypkg/lib.go
,添加以下代码
package mypkg
import "fmt"
func TestFunc() {
fmt.Println("call testFunc")
}
文件2,主文件jkdev.cn/test/main.go
,添加以下代码
package main
// 引入自定义包
import "jkdev.cn/test/mypkg"
func main() {
// 调用自定义包的函数
mypkg.TestFunc()
}
自定义的包由我们自己定义,但要注意的是,一个目录对应一个包,同时,包名应当和目录名一致。引入包时,路径相对应GOPATH/src
,我们可以根据自己的程序的功能模块划分不同的包
3.2 包的作用域
- 包内:在同一个包内定义的函数、变量、常量、结构体,可以被包内的所有其他代码任意访问,它们属于包内公开。
- 包外:如果函数、变量、常量、结构体位于不同的包下,如果将它们的首字母使用大写标识,表示它们是公开访问的。对于结构体字段。如果想要在包外进行访问,还要让结构体字段变量名使用首字母大写。
二、编译
golang
中go build
命令主要用于编译代码。在包的编译过程中,若有必要,会同时编译与之相关联的包。
假设我们的设置GOPATH
是/Users/pan/go
,我们在GOPATH
之下的src
创建创建代码test
目录,并添加lib.go
和main.go
两个文件。此时,源文件相对于GOPATH
的路径如下。接下来我们对这两个go源文件进行编译
go GOPATH目录
├─src 源码目录
│ ├─test 项目目录
│ ├─lib.go 公共函数文件
│ └─main.go 主函数文件
lib.go
代码如下:
package main
import "fmt"
func testFunc() {
fmt.Println("call testFunc")
}
main.go
代码如下:
package main
func main() {
testFunc()
}
示例中的两个源文件都属于
main
包,所以在main.go
文件中能正常引入lib.go
文件
1.go build
1.1 无参数编译
无参数编译不依赖GOPATH
,需要在源码目录下进行编译
# 进入源码目录
$ cd ~/go/src/test
# 编译代码
$ go build
我们还可以-o
指定编译后输出的可执行文件的文件名,如下
$ go build -o main
1.2 go build
文件列表
使用“go build
+文件列表”编译时,文件列表中的每个文件必须是同一个包的Go
源码
# 进入源码目录
$ cd ~/go/src/test
# 编译
$ go build lib.go main.go
1.3 go build
包
包的路径相对于GOPATH
,“go build
+包”编译时,源文件需要存放在GOPATH/src
下的路径,但是我们可以在任意有读写权限的目录执行编译命令,如下
# test 相对于GOPATH/src
$ go build test
2.go install
与go build
具有相似功能的是go install
命令,go install
用来生成库和工具并存放于GOPATH
中。
- 一是编译包文件(无
main
包),将编译后的包文件放到pkg
目录下(GOPATH/pkg
)。 - 二是编译生成可执行文件(有
main
包),将可执行文件放到bin
目录(GOPATH/bin
)
3.go build
和 go install
对比
相同点:
- 都能生成可执行文件
不同点:
& | 包文件 | 可执行文件目录 |
---|---|---|
go build | 不能生成包文件 | 生成可执行文件在当前目录下 |
go install | 可以生成包文件 | 生成可执行文件在bin目录下($GOPATH/bin ) |
三、Go模块
golang
在1.11版本引入Go Modules
,也叫做Go模块,模块依赖于上文提到的GO111MODULE
环境变量。它是golang
官方提供的包管理工具,通过Go Modules
,我们可以不必将项目放在GOPATH
上
1.GO111MODULE
环境变量
用环境变量GO111MODULE
设置开启或者关闭模块支持,它有三个可选值:off
、on
、auto
,默认是auto
GO111MODULE=off
:无模块支持,go
会从GOPATH
寻找包。GO111MODULE=on
:模块支持,根据go.mod
文件下载依赖GO111MODULE=auto
:在$GOPATH/src
外面且项目根目录有go.mod
文件时,自动开启模块支持
在使用模块的时候,GOPATH
是无意义的,但golang
还是会把下载的依赖存储于GOPATH
之下
GO111MODULE
配置参考Go
常用命令示例 | Gitee
2.使用go模块
使用Go Modules
时,我们先将GO111MODULE
设置为on
或者auto
,Go Modules
和GOPATH
不同,不需要包含固定的三个字目录。一个空的源代码目录,只要初始化好go.mod
文件,就可以定义一个Module
。我们可以使用以下命令创建一个新的Module
# 创建一个空的工作目录
mkdir -p ~/Work/go/test
# 进入工作目录
cd ~/Work/go/test
# 初始化一个module
go mod init testmodule
我们可以看到自动生成的go.mod
文件的内容如下
module testmodule
go 1.16
2.1 下载依赖
Go Modules
会为我们进行包管理,并自动更新go.mod
文件,如果需要引入新的依赖,我们只需要在终端执行go get
包名即可引入第三方包,如
$ go get github.com/satori/go.uuid
执行命令后,Go Modules
会从我们在安装golang
时设置的GOPROXY
镜像仓库地址去下载对应的包,安装完成之后我们看到go.mod
已经发生变化,如下
$ module testmodule
go 1.16
require github.com/satori/go.uuid v1.2.0 # indirect
我们也可以先编辑好go.mod
文件,然后在终端使用go mod download
,golang
会自动帮我们下载依赖包到本地。如果我们需要引入多个包,将所有包名卸载括号内,如下代码
module testmodule
go 1.16
require (
github.com/satori/go.uuid v1.2.0 // indirect
gorm.io/gorm v1.21.11 // indirect
)
2.2 更新依赖关系
使用go mod tidy
更新依赖关系,该命令拉取缺少的模块,移除不用的模块。Go Modules
可以轻易地进行一个包的依赖管理和版本控制,go build
和go install
将自动使用go.mod
中的依赖关系,减少了GOPATH
管理时的复杂性
四、常见配置错误
1、导入非标准库
开发中,想使用一个【不是标准库module】的内容,这个库可能是我们自己定义的util
工具或者其他公共业务的抽离封装等。
非标准库不在GOROOT里(一般我们不会修改GOROOT中的内容)
例如,我们想导入上图中myUtils
包,运行go run xxx
常会遇到以下错误提示:
main.go:5:2: cannot find package "package_xxx" in any of:
/usr/local/go/src/package_xxx (from $GOROOT)
/Users/lianpf/go/src/package_xxx (from $GOPATH)
解决方式有两种:
- 设置
GOPATH
+GO111MODULE=off
- 使用
go.mod
且go
版本大于13
1.1 设置GOPATH
+ GO111MODULE=off
- 前提:设置
GOPATH
,保障上图中go_package
项目在$GOPATH/src
开发。 - 设置:
GO111MODULE=off
原理:go
的编译器会在$GOPATH/src
下面寻找对应的模块,src
下的每一个目录都可对应一个模块,目录中的目录也可是一个模块。
所以,当我们需要访问模块myUtils
时,只需要使用如下代码:
import (
"fmt"
"go_package/myUtils" // $GOPATH/src层级下的目录结构
)
1.2 使用go.mod
且go
版本大于13
在
Golang1.11
版本前,自定义包必须把项目放在GOPATH
目录。Go1.11
版本后使用go mod
管理项目,不需要非得把项目放到GOPATH
指定目录下。Go1.13
后可以彻底不要GOPATH
了
这里设置:
GO111MODULE=on
第一步:初始化一个go.mod
# go_package 可以是任意的名称(建议和项目目录名保持一致)
$ go mod init go_package
go.mod
文件示例:
第二步:引入模块myUtils
,以go_package
(即上一步初始化定义的package name
)开头,后接模块路径。示例:
import (
"fmt"
"go_package/myUtils" // go_package是上一步初始化定义的package name
)
运行,查看效果。
参考
最后, 希望大家早日实现:成为编程高手的伟大梦想!
欢迎交流~
本文版权归原作者曜灵所有!未经允许,严禁转载!对非法转载者, 原作者保留采用法律手段追究的权利!
若需转载,请联系微信公众号:连先生有猫病,可获取作者联系方式!