每天5分钟-玩转golang 第4节 包

305 阅读7分钟

基本概念

  • 包是函数和数据的集合 将有相关特性的函数和数据放在统一的文件/目录 中进行管理
  • 每个包都可以作为独立的单元进行维护 并提供给其他项目进行使用

一、声明

  • Go 源文件都需要在开头使用 package 声明所在哪个包
  • 包名使用简短的小写字母,常与所在目录名保持一致,一个包中可以由多个 Go 源文件,但 必须使用相同包名

工作目录结构说明:

  • bin: 用于放置发布的二进制程序
  • pkg: 用于放置发布的库文件
  • src: 用于放置源代码

二、导入&调用

1. 声明两个包,路径分别为 gpkgname/pkg01 和 gpkgname/pkg02

gv/src/gpkgname/pkg01/module.go

package pkg01
var Name string = "pgk01"

gv/src/gpkgname/pkg02/module.go

package pkg02
var Name string = "pgk02"

2. 创建gv/src/gpkgmain/main.go 调用gpkgname/pkg01和gpkgname/pkg02

gv/src/gpkgmain/main.go

package main

import (
	"fmt"
	"gpkgname/pkg01"
	"gpkgname/pkg02"
)

func main(){
	fmt.Println("gpkgmain")
	fmt.Println(pkg01.Name)
	fmt.Println(pkg02.Name)
}

目录结构是这样的

[root@happy gv]# tree /Users/wangfei/docker/go/chapter08/
/Users/wangfei/docker/go/chapter08/
└── gv
    ├── bin
    └── src
        ├── gpkgmain
        │   └── main.go
        └── gpkgname
            ├── pkg01
            │   └── module.go
            └── pkg02
                └── module.go

3.将chapter08/gv目录添加到GOPATH环境变量中

[root@happy gv]#  GOPATH=/Users/wangfei/docker/go/chapter08/gv
[root@happy gv]# echo $GOPATH
/Users/wangfei/docker/go/chapter08/gv

4.编译二进制文件

  • 命令:go build gpkgmain
  • 说明:编译路径 gpkgmain 下的包,main 包,则在当前目录产生以 pkgmain 命名的 二进制程序
[root@happy gv]# go build gpkgmain 

[root@happy gv]# ll
total 2032
drwxr-xr-x 2 root root    4096 Nov 15 10:36 bin
-rwxr-xr-x 1 root root 2068379 Nov 15 10:36 gpkgmain
drwxr-xr-x 3 root root    4096 Nov 15 10:37 pkg
drwxr-xr-x 4 root root    4096 Nov 15 10:33 src

目录结构是这样的
tree /Users/wangfei/docker/go/chapter08/
/Users/wangfei/docker/go/chapter08/
└── gv
    ├── gpkgmain

5.运行二进制文件

[root@happy gv]# ./gpkgmain
gpkgmain
pgk01
pgk02

6.编译并发布二进制文件

  • 命令:go install gpkgmain
  • 说明:编译并发布路径 gv/src/gpkgmain下的main 包,会将编译后的二进制文件 pkgmain拷贝到 gv/bin 目录
[root@happy gv]# go install gpkgmain

[root@happy gv]# ls /Users/wangfei/docker/go/chapter08/gv/bin/
gpkgmain

7. 编译发布库文件

  • 命令:go install gpkgname/pkg01
  • 说明:编译并发布路径 gpkgname/pkg01 下的包,非 main 包,将编译的以包名 命名的库文件拷贝到 pkg/GOOS_GOARCH 目录下, (linux是pkg/linux_amd64 GOOS_GOARCH会根据平台类型改变)
[root@happy gv]# go install gpkgname/pkg01
[root@happy gv]# go install gpkgname/pkg02

生成后的路径是这样的
[root@happy gv]# tree /Users/wangfei/docker/go/chapter08/gv
/Users/wangfei/docker/go/chapter08/gv
├── pkg
│   └── linux_amd64
│       └── gpkgname
│           ├── pkg01.a
│           └── pkg02.a

8.编译发布所有二进制和库文件

  • 命令:go install ./...
  • 说明:编译并发布当前路径下的所有二进制程序和库文件
  • 注意:Go 语言不允许交叉导入包
[root@happy gv]# go install ./...

三、导入形式

1. 绝对路径导入

  • 在 GOPATH 目录中查找包
import "fmt"
import "gpkgname/pkg01"

2. 相对路径导入

  • 在当前文件所在的目录查找
import "./gpkgname/pkg02"

3. 点导入

  • 在调用点 导入包中的成员时可以直接使用成员名称进行调用(省略包名)
import . "fmt"

func main(){
	Println("Hello World!")
}

4.别名导入

  • 当导入不同路径的相同包名时,用别名对导入的包进行重命名,避免冲突
package main
import f "fmt"

func main(){
	f.Println("Hello World!")
}

5. 下划线导入

  • Go 不允许包导入但未使用, 在某些情况下需要初始化包,使用空白符作为别名进行导入,从而使得包中的初始化函数可以执行
package main
import _ "fmt"

四、成员可见性 和 函数

1.成员可见性

  • Go 语言使用名称首字母大小写来判断对象(常量、变量、函数、类型、结构体、方法等)的访问权限
  • 首字母大写标识包外可见(公开的) 否则仅包内可访问(内部的)

2.main 包与 main 函数

  • main包 用于声明 告知编译器将包编译为二进制可执行文件
  • main包中的main函数 是程序的入口,无返回值,无参数

3.init 函数

  • init 函数是初始化包使用,无返回值,无参数
  • init 函数在 import 包时自动会被调用(const->var->init)

pkg01/module.go

package pkg01

import "fmt"

// Public
var Name string = "pkg01"

// private
var version string = "v2.0"

// init
func init(){
	fmt.Println("version is :", version)
}

main.go

package main

import (
	"fmt"
	"gpkgname/pkg01"
	"gpkgname/pkg02"
)

func init(){
	fmt.Println("main init")
}

func main(){
	fmt.Println("gpkgmain")
	fmt.Println(pkg01.Name) //调用pkg01包中的成员Name
	fmt.Println(pkg02.Name) //调用pkg02包中的成员Name
}

五、Go包管理

  • Go1.11 版本􏰀供 Go modules 机制对包进行管理,同时保留 GOPATH 和 vendor 机制, 临时环境变量 GO111MODULE 进行控制
  • GO111MODULE的三个值
    • GO111MODULE为off, 构建项目始终在GOPATH和vendor目录搜索目标程序依赖包
    • GO111MODULE为on, 始终使用Gomodules机制, 在GOPATH/pkg/mod目录搜索目标程序依赖包
    • GO111MODULE为auto(默认)

GOPATH+vendor 机制

1.vendor

  • 将项目依赖包拷贝到项目下的 vendor 目录,在编译时使用项目下 vendor 目录中的包进行编译
  • 解决问题:
    • 依赖外部包过多,在使用第三方包时需要使用go get进行下载
    • 第三方包在go get下载后 不能保证开发和编译时版本的兼容性

2.搜索顺序

  • 在当前包下的vendor目录查找
  • 向上级目录查找,直到GOPATH/src/vendor目录
  • 在GOPATH目录查找
  • 在GOROOT目录查找

3.第三方包

  • 可以借助 go get 工具下载和安装第三方包及其依赖
  • 需要安装与第三方包匹配的代码 管理工具,比如 git、svn 等
GOPATH=/Users/wangfei/docker/go/chapter08/gv

go get -x github.com/astaxie/beego

常用参数:

  • -d:仅下载依赖包
  • -u:更新包并安装
  • -x:打印执行的命令
  • -v:打印构建的包
  • -insecure:允许使用http协议下载包

第三方包查找地址:

Go modules 机制

优势:

  • 不用设置GOPATH,代码可任意放置
  • 自动下载依赖管理
  • 版本控制
  • 不允许使用相对导入
  • replace机制

初始化模块

  • 命令: go mod init modname

当前模块下的包 对于当前模块下的包导入时需要使用 modname+packagename

第三方包

  • 在使用 go mod tidy、go build、go test、go list 命令会自动将第三方依赖包写入到 go.mod 文件中
  • 同时下载第三方依赖包到 GOPATH/pkg/mod/cache 目录,并在当前模块目录生成一个构建状态跟踪文件 go.sum
  • 文件中记录当前 module 所有的顶层和间接依赖, 以及这些依赖的校验和

常用命令

  • go mod tidy:整理依赖模块(添加新增的,删除未使用的)
  • go mod vendor: 将依赖模块拷贝到模块中的 vendor 目录
  • go build: 编译当前模块
  • go build ./...: 编译当前目录下的所有模块
  • gobuild-mod=vendor:使用当前模块下的vendor目录中的包进行编译
  • go mod download: 仅下载第三方模块
  • go mod grapha: 打印所有第三方模块
  • go list -m -json all:显示所有模块信息
  • go mod edit: 修改 go.mod 文件 -require=pakcage@version - replace=old_package@version=new_package@version 可以使用-replace 功能将包替换为本地包,实现相对导入

六、标准包

Go 􏰀供了大量标准包,可查看:golang.google.cn/pkg/

godoc 工具

  • 使用 godoc 命令可以在本地启动 golang 网站,用于本地查看帮助手册
    • godoc --http=localhost:8888
  • go list std:查看所有标准包
  • go doc packagename:查看包的帮助信息
  • go doc packagename.element:查看包内成员帮助信息