package 包的基本概念
说明:go的每一个文件都是属于一个包的,也就是说go是以包的形式来管理文件和项目目录结构的。
包的三大作用
- 区分相同名字的函数,变量等标识符(在不同的包下面,我们文件可以写相同函数名和变量名)
- 当程序文件很多时,可以很好的管理项目(不可能将所有文件都放在一个包里面)
- 控制函数,变量等访问范围,即作用域
包相关说明
- 打包语法 package util
引入包的基本语法
- import "包的路径"
1. 基本复⽤模块单元
以⾸字⺟⼤写来表明可被包外代码访问(函数,结构体,或者struct里面的成员都是可以被包以外的代码访问的)
2. 代码的 package 可以和所在的⽬录不⼀致
同一个目录下,源码文件的包名要保持一致
3. 同⼀⽬录⾥的 Go 代码的package 要保持⼀致
包定义
包是函数和数据的集合,将有相关特性的函数和数据放在统一的文件/目录进行管理,每个包都可以作为独立的单元维护并提供给其他项目进行使用。
之前写代码都是在main包里面写代码的,如果代码写的很多了,就需要按照代码的组织功能进行拆分,可以放在不同的文件里面,不同的文件又可以放到不同的文件夹里面。
在go里面可以认为包就是文件夹,包是函数和数据的集合。
声明
Go 源文件都需要在开头使用 package 声明所在包,包名告知编译器哪些是包的源代码用于
编译库文件,其次包名用于限制包内成员对外的可见性,最后包名用于在包外对公开成员的
访问。(main包是用来编译为二进制程序,非main包编译为库)
同一个目录里面所有go文件的包名都是一致的,以后在发布包的时候就可以将单独的文件夹拷贝给别人去使用。在go里面包是可以单独进行维护的,并且提供给其他项目去使用的。
⚠️:在go同一个目录下面所有文件的包名必须是保持一致的,同一个包中函数是可以调用的。
可以看到可以将main里面代码分为多个文件。
如果后面还要一些其他功能可以放到新建的test文件夹下面,在定义的时候,包名和文件名一样
包名:
- 满足标识符规范,小写英文字母尽量短小
- 同一个文件夹内所有go文件的包名必须一致
- 包名字尽量和文件夹名保持一致
- 在调用的时候先导入包,然后使用包名去调用。
包管理 GOPATH
在找包的时候有标准的包,也有自定义的包,如何去找呢,在go里面是有两种方式的。一种是gopath方式,第二种就是gomode方式。
如果在其他目录下面,使用gopath进行管理,那么就需要配置环境变量gopath指向那个目录,然后在那个目录下面创建src目录,将代码放到里面进行管理。
GOPATH是一个环境变量
lulei@luleideMacBook-Pro ~ % go env GOPATH
/Users/lulei/go
lulei@luleideMacBook-Pro go % ls
bin pkg src
这个目录结构其实就是在gopath模式下面它的项目的一个管理方式。gopath定义了项目开发目录
- bin 二进制文件,放二进制编译的结果
- pkg 库文件,放库编译的结果
- src 全部的原文件
导入包的开始是从src目录开始的,最后是包名。
code/src/math1 这个是目录 fmt.Println(math1.Add(1,2)) 包名调用
现在虽然目录结构有了,但是还需要重新设置go path为实际的路径对应上面的路径。设置为这个目录gopath/code,这个时候会去找包,会在所有的go path/src目录下面遍历,找到math1目录
可以看到在src下面创建的目录testpkg其实就是项目目录, 将所有代码都放在这个项目下面,在里面创建了math文件夹,在这个文件夹里面创建math包 ,在哪个目录下面创建包,那么包名就和文件一样。
每个文件夹下面所有的go文件的包名必须一致,在导入的时候按照大是目录,在调用的时候使用的是包名。
包名
- main包:会编译成二进制程序,main包里的main函数是程序的入口(编译出来为可执行程序)
- 非main包: math在go里面叫库文件,它是不直接运行的,它是给其他人提供一些功能,在调用的时候可以main包去调用也可以是非main包里面去调用。(编译出来为库文件,库文件可以认为是.a文件,.a文件链接二进制程序)
所以在整个程序的调用过程当中是从main入口的,一直会递归的调用非main的包。
上面就是gopath管理的方式。
运行:
- 使用 go build 编译二进制文件
- 使用 go run 运行二进制文件
- 使用 go install 编译并发布二进制文件
- 使用 go install 编译发布库文件
- 使用 go install 编译发布所有二进制和库文件
GOPATH+vendor 机制
第三方包,也就是别人提供的一些包,因为都是开源的,很多开源的包和库,我们可以直接去使用。go里面使用的第三方包基本上都在github上面。
使用第三方包:先下载到本地,然后放到GOPATH/src下面
这里就会出现一个问题
公司里面开发了两个项目,都依赖testpkg/math同一个库,这个库提供了两个版本v1和v2版本。
A项目 v1版本
B项目 v2版本
这样会导致一个问题,在同一台机器上面无法编译。因为testpkg目录下只能存放一个版本。
go 1.5版本提供了vender机制,也就是在项目目录下面创建vender目录,将第三方包下载拷贝到vender目录。
那src下面和vender都有这个包,怎么找呢。
a包里面去使用了,会在a/vender目录下面去找,如果找到了就使用a目录下面的包,如果没有会去找父目录的vender目录下面找包,以此类推,最后找到GOPATH/src/vender目录,如果还没找到就去GOPATH/src下面找,还没找到去GOOPATH,GOROOT当中寻找。
a) vendor
- 依赖外部包过多,在使用第三方包时需要使用 go get 进行下载
- 第三方包在 go get 下载后不能保证开发和编译时版本的兼容性
- 在当前包下的 vendor 目录查找
- 向上级目录查找,直到 GOPATH/src/vendor 目录
- 在 GOPATH 目录查找
- 在 GOROOT 目录查找
- -d:仅下载依赖包
- -u:更新包并安装
- -x:打印执行的命令
- -v:打印构建的包
- -insecure:允许使用 http 协议下载包
- https://godoc.org
- https://gowalker.org/
vendor其实解决的是多个项目依赖同一个路径的包但是版本不一样。
现在有来问题了,版本v1的包放在项目a的vendor目录下,版本v2的包放在项目b的vendor目录下。假设出现了v3版本的包,那么项目a和项目b会怎么做呢?
那么要将两个项目的vendor目录替换掉,但是在替换的过程当中可能会替换出一些问题来。
gomode
所以在go的 1.11版本里面,提供了gomode的机制,所以在go里面通过环境变量信息来控制是不是开启gomode,通过GO111MODULE来控制是否开启gomode,这是控制使用gopath还是使用gomode的方式。
GO111MODULE = on 就是永远使用gomode的方式,也就是必须使用这种方式。如果使用gopath方式就配置为off。
set GO111MODULE=on
还有一种是“”空,这个是默认值。也就是按照程序的结构自己去选择,如何选择的呢?有个GOPATH环境变量还一个go.mod文件,会检查当前项目不在GOPATH目录下面且当前目录有go.mod文件,这个时候使用的是gomode管理方式,否则使用GOPATH管理方式。我们设置了空值,并且代码都在GOPATH下面,所以使用GOPATH管理方式。
- 不用设置 GOPATH,使用GOPATH要不放在一个固定的目录下面,要不得配置环境变量。使用gomode代码可任意放置
- 自动下载依赖管理 ,版本控制 版本文件都会通过go.mod记录,所以升级只需要修改里面记录就行了。GOPROXY 在使用gomode方式的话,会使用GOPROXY 进行代理。
- 不允许使用相对导入
- replace 机制
使用gomode
第一步 初始化模块,一个项目就是一个模块 go mod init 项目名称
项目名称 代码仓库路径/项目名称 其实也就是地址
在go管理包的时候,底层使用的是git/svn,如果使用了第三方包,在运行编译的时候,如果发现第三包不存在,会自动进行下载,其实这个包的名称就是其地址。
下载的包是gomode自动管理的,我们是管理不了的,咋们只需要管理自己项目的目录。gomode下载的包都在这里面。
可以看到版本都在这里面
使用第三方包
提供第三方包
要将你的代码推送到某个地方去,如github上面去,别人就可以使用。
gomod里面写的就要是代码仓库路径+项目名称。
先去GitHub上面创建我的项目github.com/lovekeepcoding/testmath
创建gomod文件
提供包的时候还是和项目名称保持一致。
这样就将包的代码推送到github上面去了,这样就提供了一个包,代码已经写好了,里面只提供了MathPlus函数。
提供第三方包就是定义一个gomod然后推送就行了。
包在本地是没有的,它会先去找模块 "github.com/lovekeepcoding/testmath",没有找到会去下载,这相当于依赖包会去下载到本地。(这不像之前的本地库在gomod管理的目录里面)
运行go rum mian.go 如果第三方依赖会写到gomod文件里面 可以看到依赖和版本
go run依赖的包都会自动下载,但是gopath都是手动下载。
如果是使用第三方包
下载beego的时候还会去下载第三方依赖。可以看到别人写的第三包和你自己写的第三方包是一样的。
自己内网没有依赖第三方库,直接编译就行了
依赖内部其他团队提供的库 内部有代码管理仓库gitlab
外网的包 下载上传到自己的gitlab 然后再去引用
自己正常使用
每次创建一个文件夹就是一个项目
testmod是模块名称,或者说是项目名称。gomod在初始化的时候一般会取一个项目名称。
这就是自己导入自己项目某一个包如何使用。
示例:
main.go
package main
import (
"day1/base/package_test/utils"
"fmt"
)
func main() {
n := utils.Sum(1, 2)
fmt.Println(n)
}
utils.go
package utils
// Sum 函数名称大写 因为要被别的包引用
func Sum(a, b int) int {
return a + b
}