【Golang】包

Go语言是使用包来组织源代码的,包(package)是多个 Go 源码的集合,是一种高级的代码复用方案

Go语言中为我们提供了很多内置包,如 fmt、os、io 等。

任何源代码文件必须属于某个包,同时源码文件的第一行有效代码必须是package pacakgeName 语句,通过该语句声明自己所在的包

1、包的基本概念

Go语言的包借助了目录树的组织形式,一般包的名称就是其源文件所在目录的名称

虽然Go语言没有强制要求包名必须和其所在的目录名同名,但还是建议包名和所在目录同名,这样结构更清晰

包可以定义在很深的目录中,包名的定义是不包括目录路径的,但是包在引用时一般使用全路径引用

包的习惯用法:

  • 包名一般是小写的,使用一个简短且有意义的名称
  • 包名一般要和所在的目录同名,也可以不同,包名中不能包含- 等特殊符号
  • 包名为 main 的包为应用程序的入口包,编译不包含 main 包的源码文件时不会得到可执行文件
  • 一个文件夹下的所有源码文件只能属于同一个包,同样属于同一个包的源码文件不能放在多个文件夹下

2、包的导入

要在代码中引用其他包的内容,需要使用 import 关键字导入使用的包。具体语法如下:

import "包的路径"

注意事项:

  • import 导入语句通常放在源码文件开头包声明语句的下面;
  • 导入的包名需要使用双引号包裹起来;

包的导入有两种写法,分别是单行导入和多行导入

//单行导入
import "包 1 的路径"
import "包 2 的路径"

//多行导入
import (
    "包 1 的路径"
    "包 2 的路径"
)

注意:

包的绝对路径就是GOROOT/src/GOPATH后面包的存放路径

3、包的引用格式

  • 标准引用格式

    package main
    import "fmt"
    func main() {
        fmt.Println("hello world")
    }
    

    此时可以用fmt.作为前缀来使用 fmt 包中的方法,这是常用的一种方式

  • 自定义别名引用格式

    package main
    import F "fmt"
    func main() {
        F.Println("hello world")
    }
    

    其中 F 就是 fmt 包的别名,使用时我们可以使用F.来代替标准引用格式的fmt.来作为前缀使用 fmt 包中的方法

  • 省略引用格式

    package main
    import . "fmt"
    func main() {
        //不需要加前缀 fmt.
        Println("hello world")
    }
    

    这种格式相当于把 fmt 包直接合并到当前程序中,在使用 fmt 包内的方法是可以不用加前缀fmt.,直接引用

  • 匿名引用格式

    package main
    import (
        _ "database/sql"
        "fmt"
    )
    func main() {
        fmt.Println("hello world")
    }
    

    在引用某个包时,如果只是希望执行包初始化的 init 函数,而不使用包内部的数据时,可以使用匿名引用格式

    匿名导入的包与其他方式导入的包一样都会被编译到可执行文件中

    使用标准格式引用包,但是代码中却没有使用包,编译器会报错。如果包中有 init 初始化函数,则通过import _ "包的路径" 这种方式引用包,仅执行包的初始化函数,即使包没有 init 初始化函数,也不会引发编译器报错

4、init初始化函数

在每一个go 源文件中,都可以定义若干个如下格式的函数

func init() {
	......
}

这种特殊的函数不接收任何参数没有任何返回值,我们也不能在代码中主动调用它,当程序启动的时候,init函数会按照他们申明的顺序自动执行。

一个包的初始化过程是按照代码中引入的顺序来进行的,所有在该包中申明的init函数都将被串行调用,并且 仅调用执行一次。每一个包初始化的时候都是都是先执行依赖包中的init函数,再执行当前包中申明的init函数确保在程序的main函数在执行时所有依赖包都已经初始化完成
在这里插入图片描述
每一个包的初始化都是先从初始化包级别变量开始的。例如:

package main

import "fmt"

var x int8 = 10

const pi = 3.14

func init() {
  fmt.Println("x:", x)
  fmt.Println("pi:", pi)
  sayHi()
}

func sayHi() {
	fmt.Println("Hello World!")
}

func main() {
	fmt.Println("你好,世界!")
}

在这里插入图片描述
在上面的代码中,我们了解了Go语言中包的定义及包的初始化过程,这让我们能够在开发时按照自己的需要定义包。
同时我们还学到了如何在我们的代码中引入其它的包,不过在本小节的所有示例中我们都是引入Go内置的包。
现代编程语言大多都允许开发者对外发布包/库,也支持开发者在自己的代码中引入第三方库。
这样的设计能够让广大开发者一起参与到语言的生态环境建设当中,把生态建设的更加完善

  • 注意:

    • 一个包可以有多个 init 函数,包加载时会执行全部的 init 函数,但并不能保证执行顺序,所以不建议在一个包中放入多个 init 函数,将需要初始化的逻辑放到一个 init 函数里面。
    • 包不能出现环形引用的情况,比如包 a 引用了包 b,包 b 引用了包 c,如果包 c 又引用了包 a,则编译不能通过。
    • 包的重复引用是允许的,比如包 a 引用了包 b 和包 c,包 b 和包 c 都引用了包 d。这种场景相当于重复引用了 d,这种情况是允许的,并且 Go 编译器保证包 d 的 init 函数只会执行一次

5、go module

在Go语言早期的版本中,我们编写Go项目代码时所依赖的所有第三方包都需要保存在GOPATH这个目录下。这样的依赖管理存在致命的缺陷—不支持版本管理,同一个依赖包只能存在一个版本的代码,可是我们本地的多个项目完全可能依赖同一个第三方包的不同版本

因此就需要go module来解决这个棘手的问题

go module go 1.11版本发布的依赖管理方案。从go 1.14版本开始推荐在生产环境使用,于go 1.16版本默认开启

使用go module之前需要设置环境变量:

  1. GO111MODULE=on
  2. GOPROXY=https://goproxy.io,directopen in new window
  3. GOPROXY=https://goproxy.cn,direct(国内的七牛云提供)

GO111MODULE 有三个值:off, on和auto(默认值)

  • GO111MODULE=off,go命令行将不会支持module功能,寻找依赖包的方式将会沿用旧版本那种通过vendor目录或者GOPATH模式来查找。
  • GO111MODULE=on,go命令行会使用modules,而一点也不会去GOPATH目录下查找。
  • GO111MODULE=auto,默认值,go命令行将会根据当前目录来决定是否启用module功能。这种情况下可以分为两种情形:
    • 当前目录在GOPATH之外且该目录包含go.mod文件 开启
    • 当处于 GOPATH 内且没有 go.mod 文件存在时其行为会等同于 GO111MODULE=off
  • 如果不使用 Go Modules, go get 将会从模块代码的 master 分支拉取
  • 而若使用 Go Modules 则你可以利用 Git Tag 手动选择一个特定版本的模块代码

go module 有以下命令

命令说明
go mod downloaddownload modules to local cache(根据go.mod文件下载依赖包)
go mod editedit go.mod from tools or scripts(编辑go.mod)
go mod graphprint module requirement graph (打印模块依赖图)
go mod initinitialize new module in current directory(在当前目录初始化mod,生成go.mod文件)
go mod tidyadd missing and remove unused modules(将项目文件中引入的依赖与go.mod进行比对,拉取缺少的模块,移除不用的模块)
go mod vendormake vendored copy of dependencies(将依赖复制到vendor下)
go mod verifyverify dependencies have expected content (校验一个依赖包是否被篡改过)
go mod whyexplain why packages or modules are needed(解释为什么需要依赖)
  • 常用的有 init tdiy edit

GOPROXY

  • 这个环境变量主要是用于设置Go代理模块(Go module proxy),其作用是用于Go在后续拉取模块版本时能够脱离原始的VCS模式,直接通过镜像站点快速拉取。
  • GOPROXY 的默认值是:https://proxy.golang.org,direct,由于某些原因国内无法正常访问该地址,所以我们通常需要配置一个可访问的地址。目前社区使用比较多的有两个https://goproxy.cn和https://goproxy.io,当然如果你的公司有提供GOPROXY地址那么就直接使用。设置GOPAROXY的命令如下:
    go env -w GOPROXY=https://goproxy.cn,direct
    GOPROXY 允许设置多个代理地址,多个地址之间需使用英文逗号 “,” 分隔。最后的 direct 是一个特殊指示符,用于指示 Go 回源到源地址去抓取(比如 GitHub 等)。当配置有多个代理地址时,如果第一个代理地址返回 404 或 410 错误时,Go 会自动尝试下一个代理地址,当遇见 direct 时触发回源,也就是回到源地址去抓取

GOPRIVATE

  • 设置了GOPROXY 之后,go 命令就会从配置的代理地址拉取和校验依赖包。
  • 当我们在项目中引入了非公开的包(公司内部git仓库或 github 私有仓库等),此时便无法正常从代理拉取到这些非公开的依赖包,这个时候就需要配置 GOPRIVATE 环境变量
  • GOPRIVATE用来告诉 go 命令哪些仓库属于私有仓库,不必通过代理服务器拉取和校验
    GOPRIVATE 的值也可以设置多个,多个地址之间使用英文逗号 “,” 分隔。我们通常会把自己公司内部的代码仓库设置到 GOPRIVATE 中,例如:
    go env -w GOPRIVATE="git.mycompany.com"
    这样在拉取以git.mycompany.com为路径前缀的依赖包时就能正常拉取了。
    此外,如果公司内部自建了 GOPROXY 服务,那么我们可以通过设置 GONOPROXY=none,允许通内部代理拉取私有仓库的包。

使用go get命令下载指定版本的依赖包:

执行go get 命令,在下载依赖包的同时还可以指定依赖包的版本。

  • 运行go get -u命令会将项目中的包升级到最新的次要版本或者修订版本;
  • 运行go get -u=patch命令会将项目中的包升级到最新的修订版本;
  • 运行go get [包名]@[版本号]命令会下载对应包的指定版本或者将对应包升级到指定的版本。

提示:go get [包名]@[版本号]命令中版本号可以是 x.y.z 的形式,例如 go get foo@v1.2.3,也可以是 git 上的分支或 tag,例如 go get foo@master,还可以是 git 提交时的哈希值,例如 go get foo@e3702bed2

  • 项目中使用

    在 GOPATH 目录下新建一个目录,并使用go mod init初始化生成 go.mod 文件

    go.mod 文件一旦创建后,它的内容将会被 go toolchain 全面掌控,go toolchain 会在各类命令执行时,比如go getgo buildgo mod等修改和维护 go.mod 文件

    go.mod 提供了 module、require、replace 和 exclude 四个命令:

    • module 语句指定包的名字(路径);
    • require 语句指定的依赖项模块;
    • replace 语句可以替换依赖项模块;
    • exclude 语句可以忽略依赖项模块。

    初始化生成的 go.mod 文件如下所示:

    在这里插入图片描述

添加依赖

新建一个 main.go 文件,写入以下代码

package main
import (
    "net/http"
    "github.com/labstack/echo"
)
func main() {
    e := echo.New()
    e.GET("/", func(c echo.Context) error {
        return c.String(http.StatusOK, "Hello, World!")
    })
    e.Logger.Fatal(e.Start(":1323"))
}

执行go mod tidy运行代码会发现 go mod 会自动查找依赖自动下载

在这里插入图片描述

go module 安装 package 的原则是先拉取最新的 release tag,若无 tag 则拉取最新的 commit

go 会自动生成一个 go.sum 文件来记录 dependency tree。

执行脚本go run main.go,就可以运行项目。

可以使用命令go list -m -u all来检查可以升级的 package,使用go get -u need-upgrade-package升级后会将新的依赖版本更新到 go.mod

比如:go get -u github.com/labstack/gommon

也可以使用go get -u升级所有依赖。

使用 replace 替换无法直接获取的 package:

由于某些已知的原因,并不是所有的 package 都能成功下载,比如:golang.org 下的包。

modules 可以通过在 go.mod 文件中使用 replace 指令替换成 github 上对应的库,比如:golang.org 下的包

replace (
    golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a => github.com/golang/crypto v0.0.0-20190313024323-a1f597ede03a
)

modules 可以通过在 go.mod 文件中使用 replace 指令替换成 github 上对应的库,比如:

replace (
    golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a => github.com/golang/crypto v0.0.0-20190313024323-a1f597ede03a
)

go install命令将项目打包安装为可执行文件,在安装在GOPATH的bin目录下,go install执行的项目 必须有main方法

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Suk-god

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值