Go-包依赖管理

包介绍

包的介绍

包(package)是多个Go源码的集合,是一种高级的代码复用方案,Go语言为我们提供了很多内置包,如fmtosio等。

包的定义

​ 一个包可以简单理解为一个存放.go文件的文件夹。 该文件夹下面的所有go文件都要在代码的第一行添加如下代码,声明该文件归属的包。

package 包名

注意事项:

  • 一个文件夹下面直接包含的文件只能归属一个package,同样一个package的文件不能在多个文件夹下。
  • 包名可以不和文件夹的名字一样,包名不能包含 - 符号。
  • 包名为main的包为应用程序的入口包,这种包编译后会得到一个可执行文件,而编译不包含main包的源代码则不会得到可执行文件。

可见性

​ 如果想在一个包中引用另外一个包里的标识符(如变量、常量、类型、函数等)时,该标识符必须是对外可见的(public)。在Go语言中只需要将标识符、结构体中的字段名和接口中的方法名的首字母大写就可以让标识符对外可见了。

package pkg2

import "fmt"

// 包变量可见性

var a = 100 // 首字母小写,外部包不可见,只能在当前包内使用

// 首字母大写外部包可见,可在其他包中使用
const Mode = 1

type person struct { // 首字母小写,外部包不可见,只能在当前包内使用
	name string
}

// 首字母大写,外部包可见,可在其他包中使用
func Add(x, y int) int {
	return x + y
}

func age() { // 首字母小写,外部包不可见,只能在当前包内使用
	var Age = 18 // 函数局部变量,外部包不可见,只能在当前函数内使用
	fmt.Println(Age)
}

导入包

要在代码中引用其他包的内容,需要使用import关键字导入使用的包。

import "包的路径"

注意事项:

  • import导入语句通常放在文件开头包声明语句的下面。
  • 导入的包名需要使用双引号包裹起来。
  • 包名是从$GOPATH/src/后开始计算的,使用/进行路径分隔。
  • Go语言中禁止循环导入包。

单行导入

import "包1"
import "包2"

多行导入

import (
    "包1"
    "包2"
)

自定义包名别名

通常用于导入的包名太长或者导入的包名冲突的情况,为导入的包设置别名。

import 别名 "包的路径"

初始化函数

init()函数介绍

​ 在Go语言程序执行时导入包语句会自动触发包内部init()函数的调用。需要注意的是: init()函数没有参数也没有返回值。 init()函数在程序运行时自动被调用执行,不能在代码中主动调用它。

包初始化执行过程

image-20200720205950473

init()函数执行顺序

​ Go语言包会从main包开始检查其导入的所有包,每个包中又可能导入了其他的包。Go编译器由此构建出一个树状的包引用关系,再根据引用顺序决定编译顺序,依次编译这些包的代码。

在运行时,被最后导入的包会最先初始化并调用其init()函数, 如下图示

包之间的init执行顺序

依赖包管理

Go Modules

go module是Go1.11版本之后官方推出的版本管理工具,并且从Go1.13版本开始,go module将是Go语言默认的依赖管理工具。

安装和激活 Modules 的支持

前置条件

从 Go 1.11 版本才支持 Go Modules。所以,默认 Go 的版本为 >= 1.11。

go version
激活modules

要启用go module支持首先要设置环境变量GO111MODULE,通过它可以开启或关闭模块支持,它有三个可选值:offonauto,默认值是auto

  • GO111MODULE=off禁用模块支持,编译时会从GOPATHvendor文件夹中查找包。

  • GO111MODULE=on启用模块支持,编译时会忽略GOPATHvendor文件夹,只根据 go.mod下载依赖。

  • GO111MODULE=auto,当项目在$GOPATH/src外且项目根目录有go.mod文件时,开启模块支持。

使用 go module 管理依赖后会在项目根目录下生成两个文件go.modgo.sum

GOPROXY代理

Go1.11之后设置GOPROXY命令

export GOPROXY=https://goproxy.cn
# 或
go env -w GOPROXY=https://goproxy.cn,direct

Go1.13之后GOPROXY默认值为https://proxy.golang.org,在国内是无法直接访问(需要窗墙)。因此,在国内推荐使用https://goproxy.cn

管理命令

go mod download : 下载依赖的module到本地cache(默认为$GOPATH/pkg/mod目录)
go mod edit     : 编辑go.mod文件
go mod graph    : 打印模块依赖图
go mod init     : 初始化当前文件夹, 创建go.mod文件
go mod tidy     : 增加缺少的module,删除无用的module
go mod vendor   : 将依赖复制到vendor下
go mod verify   : 校验依赖
go mod why      : 解释为什么需要依赖
module依赖信息文件
go.mod文件

文件记录结构

module goetcd

go 1.13

require (
	github.com/coreos/etcd v3.3.22+incompatible // indirect
	github.com/coreos/go-semver v0.3.0 // indirect
	github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf // indirect
	github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
	github.com/gogo/protobuf v1.3.1 // indirect
	github.com/golang/protobuf v1.4.2 // indirect
	github.com/google/go-cmp v0.5.0 // indirect
	github.com/google/uuid v1.1.1 // indirect
	go.etcd.io/etcd v3.3.22+incompatible // indirect
	go.uber.org/zap v1.15.0 // indirect
	golang.org/x/net v0.0.0-20200707034311-ab3426394381 // indirect
	golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed // indirect
	golang.org/x/text v0.3.3 // indirect
	google.golang.org/grpc v1.31.0 // indirect
)

replace google.golang.org/grpc => google.golang.org/grpc v1.26.0
  • module:用于定义包名
  • require:用于定义依赖包及其版本
  • indirect:表示间接引用
  • replace:用于替换对应的依赖包
go.sum文件

文件结构

cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/coreos/etcd v3.3.22+incompatible h1:AnRMUyVdVvh1k7lHe61YEd227+CLoNogQuAypztGSK4=
github.com/coreos/etcd v3.3.22+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=

记录相关依赖包版本信息及其哈希值

依赖版本
  • go mod支持语义化版本号:go get foo@v1.2.3

  • go mod支持git的分支或tag:go get foo@master

  • go mod支持git提交哈希:go get foo@e3702bed2

下载依赖(go get)

在项目中执行go get命令可以下载依赖包,并且还可以指定下载的版本。

  • 运行go get -u将会升级到最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号)
  • 运行go get -u=patch将会升级到最新的修订版本
  • 运行go get package@version将会升级到指定的版本号version
整理依赖(go mod tidy)

在代码中删除依赖代码后,相关的依赖库并不会在go.mod文件中自动移除。这种情况下我们可以使用go mod tidy命令更新go.mod中的依赖关系。

go mod edit
格式化

可以手动修改go.mod文件,所以有些时候需要格式化该文件。Go提供了一下命令:

go mod edit -fmt
添加依赖项
go mod edit -require=golang.org/x/text
移除依赖项

如果只是想修改go.mod文件中的内容,那么可以运行go mod edit -droprequire=package path,比如要在go.mod中移除golang.org/x/text包,可以使用如下命令:

go mod edit -droprequire=golang.org/x/text

关于go mod edit的更多用法可以通过go help mod edit查看。

项目中使用go module

既有项目

如果需要对一个已经存在的项目启用go module,可以按照以下步骤操作:

  1. 在项目目录下执行go mod init,生成一个go.mod文件。
  2. 执行go get,查找并记录当前项目的依赖,同时生成一个go.sum记录每个依赖库的版本和哈希值。
新项目

对于一个新创建的项目,我们可以在项目文件夹下按照以下步骤操作:

  1. 执行go mod init 项目名命令,在当前项目文件夹下创建一个go.mod文件。
  2. 手动编辑go.mod中的require依赖项或执行go get自动发现、维护依赖。

实战

创建项目

创建项目并进入根目录:

C:\Users\Albin>d:
D:\>cd D:\Practice\Scripts\go\src\gitee.com\bliub\stdgo
D:\Practice\Scripts\go\src\gitee.com\bliub\stdgo>mkdir grpc_demo
D:\Practice\Scripts\go\src\gitee.com\bliub\stdgo>cd grpc_demo
初始化
go mod init grpc_demo
# go: creating new go.mod: module grpc_demo

go mod 初始化,并命名包名为 grpc_demo。同时创建了 go.mod 文件。

获取依赖包
D:\Practice\Scripts\go\src\gitee.com\bliub\stdgo\grpc_demo>go get -u google.golang.org/grpc
go: downloading golang.org/x/net v0.0.0-20190311183353-d8887717615a
go: extracting golang.org/x/net v0.0.0-20190311183353-d8887717615a
go: downloading golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a
go: extracting golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a
go: finding golang.org/x/sys latest
go: finding google.golang.org/genproto latest
go: finding golang.org/x/net latest

D:\Practice\Scripts\go\src\gitee.com\bliub\stdgo\grpc_demo>go get -u github.com/golang/protobuf/protoc-gen-go
查看go.mod文件
module grpc_demo

go 1.13

require (
	github.com/golang/protobuf v1.4.2 // indirect
	golang.org/x/net v0.0.0-20200707034311-ab3426394381 // indirect
	golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed // indirect
	golang.org/x/text v0.3.3 // indirect
	google.golang.org/genproto v0.0.0-20200808173500-a06252235341 // indirect
	google.golang.org/grpc v1.31.0 // indirect
	google.golang.org/protobuf v1.25.0 // indirect
)
升级和降级依赖

应该使用 go get 来完成日常升级和降级依赖项,这将自动更新 go.mod 文件。 或者可以直接编辑 go.mod 文件。

此外,像 go buildgo test 或甚至 go list 这样的命令会根据需要自动添加新的依赖项以满足导入。

要查看所有直接和间接依赖项的可用 minor 和 patch 程序升级:

go list -u -m all

练习题

四则运算

编写一个calc包实现加减乘除四个功能函数,在snow这个包中导入并使用加减乘除四个函数实现数学运算。

新建项目目录并初始化

mkdir -p gitee.com/bliub/stdgo/mod_demo
cd gitee.com/bliub/stdgo/mod_demo
go mod init gitee.com/bliub/stdgo/mod_demo

mkdir -p {calc,snow}

创建包calc并实现四则运算

vi calc/calc.go

package calc

import "fmt"

// 加减乘除四个函数实现数学运算

// Add 加法运算
func Add(x, y int) int {
	return (x + y)
}

// Sub 减法运算
func Sub(x, y int) int {
	return (x - y)
}

// Mul 乘法运算Multiplication
func Mul(x, y int) int {
	return (x * y)
}

// Div 除法运算
func Div(x, y int) int {
	defer func() {
		err := recover() // 以防除数为零的情况
		if err != nil {
			fmt.Print("除数不能为零!!!默认输出结果:")
		}
	}()
	return (x / y)
}

导入并使用加减乘除四个函数

vi snow/main.go

package main

import (
	"fmt"

	m "gitee.com/bliub/stdgo/mod_demo/calc"
)

//编写一个calc包实现加减乘除四个功能函数,在snow这个包中导入并使用加减乘除四个函数实现数学运算。

var (
	i1  = 6
	i2  = 3
	//res = 0
    res int
)

func main() {
	// 调用加法
	res = m.Add(i1, i2)
	fmt.Printf("%d + %d = %d\n", i1, i2, res)
	// 调用加法
	res = m.Sub(i1, i2)
	fmt.Printf("%d - %d = %d\n", i1, i2, res)
	// 调用乘法
	res = m.Mul(i1, i2)
	fmt.Printf("%d * %d = %d\n", i1, i2, res)
	// 调用除法
	res = m.Div(i1, i2)
	fmt.Printf("%d / %d = %d\n", i1, i2, res)
}

运行结果

image-20200813102123136

posted @ 2020-07-20 22:24  KuBee  阅读(947)  评论(0编辑  收藏  举报