天天看点

十一、Go包管理

十一、包管理

11.1、package

Go语言是使用包来组织源代码的,包(package)是多个 Go 源码的集合,是一种高级的代码复用方案。Go语言中为我们提供了很多内置包,如 fmt、os、io 等。任何源代码文件必须属于某个包,同时源码文件的第一行有效代码必须是package pacakgeName 语句,通过该语句声明自己所在的包。

Go语言的包借助了目录树的组织形式,一般包的名称就是其源文件所在目录的名称,虽然Go语言没有强制要求包名必须和其所在的目录名同名,但还是建议包名和所在目录同名,这样结构更清晰。

11.1.1、包的基本使用

导入包的语法:

import "包的路径"    // 双引号
           

(1)内部模块导入

导入包路径规则:

Go 程序首先在 GOROOT/src 目录中寻找包目录,如果没有找到,则会去 GOPATH/src 目录中继续寻找。比如 fmt 包是位于 GOROOT/src 目录的 Go 语言标准库中的一部分,它将会从该目录中导入。

目录结构

├─mysite
        ├─api
        │      http.go
        │      rpc.go
        │
        └─main
                main.go
           
// http.go
package api

import "fmt"

func HttpRequest(){
    fmt.Println("HttpRequest方法")
}

// rpc.go
package api

import "fmt"

func RpcRequest(){
    fmt.Println("RpcRequest方法")
}


// main.go
package main

import "fmt"   //标准库包
import "mysite/api"


func main() {

    fmt.Println("hi,yuan!")
    api.HttpRequest()
    api.RpcRequest()
}

           

1、包名一般是小写的,见名知意,包名中不能包含- 等特殊符号。

2、包名规范上要和所在的目录同名,也可以不同。比如package api改为package newApi,代码改动为

import "mysite/api" // 注意文件夹api的名字不改动,这里导入的就是文件夹名
func main() {
  newApi.HttpRequest()  // 使用的package的名字
  newApi.RpcRequest()
}
           

3、包名为 main 的包为应用程序的入口包,编译不包含 main 包的源码文件时不会得到可执行文件。

4、一个文件夹下的所有源码文件只能属于同一个包,同样属于同一个包的源码文件不能放在多个文件夹下。

5、一个包下的不同文件不能含有同名函数。

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

7、环境变量GO111MODULE=off

8、将mysite剪切到src外的任何位置,都会导包失败。

(2)外部模块导入

当需要一个外部包时需要下载到本地,命令是

go get "远程包" 
go get命令会依赖git clone拉取下载指定的包,并将下载的包进行编译,然后安装到$GOPATH/src下。
           

比如

package main

import (
    "fmt"
    "mysite/api"   // 内部包导入
)
import "github.com/jinzhu/now"   // 外部包导入      


func main() {
    
    
    api.HttpRequest()
    api.RpcRequest()
    
    fmt.Println(now.BeginningOfMinute())

}
           

远程包介绍:

十一、Go包管理
go get "github.com/jinzhu/now"
           

git-clone拉取下载存放在gopath/src目录:

十一、Go包管理

然后编译器再执行import "github.com/jinzhu/now"就可以从 $GOPATH/src 下搜索找到这个包了。

11.1.2、包的导入格式

// 一次导入多个包
import (
    "fmt"
    "mysite/api"
)

// 设置包的别名
import F "fmt"

// 省略引用格式
import . "mysite/api"
HttpRequest()

// 匿名导入 :在引用某个包时,如果只是希望执行包初始化的 init 函数,而不使用包内部的数据时,可以使用匿名引用格式
import _ "包名"
import (
      "database/sql"
      _ "github.com/go-sql-driver/mysql"
  )  
           

11.1.3、包的加载顺序

init()函数会在每个包完成初始化后自动执行,并且执行优先级比main函数高。

注意:

1、一个包可以有多个 init 函数,包加载时会执行全部的 init 函数,但并不能保证执行顺序,所以不建议在一个包中放入多个 init 函数,将需要初始化的逻辑放到一个 init 函数里面。

2、包不能出现环形引用的情况,比如包 a 引用了包 b,包 b 引用了包 c,如果包 c 又引用了包 a,则编译不能通过。

3、包的重复引用是允许的,比如包 a 引用了包 b 和包 c,包 b 和包 c 都引用了包 d。这种场景相当于重复引用了 d,这种情况是允许的,并且 Go 编译器保证包 d 的 init 函数只会执行一次。

4、init()函数没有参数也没有返回值。 init()函数在程序运行时自动被调用执行,不能在代码中主动调用它。

十一、Go包管理

11.2、go module

module是一个相关Go包的集合,它是源代码更替和版本控制的单元。

11.2.1、Go mod命令

/*
download    download modules to local cache (下载依赖的module到本地cache))
edit        edit go.mod from tools or scripts (编辑go.mod文件)
graph       print module requirement graph (打印模块依赖图))
init        initialize new module in current directory (再当前文件夹下初始 
            化一个新的module, 创建go.mod文件))
tidy        add missing and remove unused modules (增加丢失的module,去掉未 
            用的module)
vendor      make vendored copy of dependencies (将依赖复制到vendor下)
verify      verify dependencies have expected content (校验依赖)
why         explain why packages or modules are needed (解释为什么需要依 
            赖)*/
           

Mod Cache 路径默认在$GOPATH/pkg 下面:$GOPATH/pkg/mod

**

11.2.2、go mod流程**

同样是mysite项目,现在从src中剪切到其它任何位置,会编译报错。用go module来解决这个问题。

(1) 首先将你的版本更新到最新的Go版本(>=1.11)

(2)通过go命令行,进入到你当前的工程目录下,在命令行设置临时环境变量set GO111MODULE=on

(3)go mod init在当前目录下生成一个go.mod文件,执行这条命令时,当前目录不能存在go.mod文件(有删除)。

go mod init xxx  // xxx即声明的模块名
           

该命令会在当前目录下生成go.mod文件,文件中声明了模块名称。

module xxx

go 1.16

以后相关依赖可以声明在这里,就像python的requirements。