前言

无论什么Python还是Golang当你的代码量达到一定程度时,就需要使用package来分类、组织我们的代码文件。

 

go-package概念

在项目开发过程中组织代码的一中方式。包是多个Go文件的集合,是一种高级的代码复用方案,Go语言为我们提供了很多内置包,如fmtosio等。

在目前我使用的go1.11.5中每1个.go文件都需要存放在Gopath路径下的1个文件中(包)。

package main

 .go文件最开始声明这是1个main包,也就是程序的入口。

 

初识 go-package

在go语言中1个文件夹可以称为1个包,在文件夹(包)可以创建多个.go文件。

在同1个文件夹(包)中的.go文件必须pakage关键字指定同1个包名。

 

在go语言中包分为2类

1.main包:使用main包可以让我们写的程序编译成1个可执行的二进制文件,所以main包就是程序的入口。

2.非main包:可以规范和分类我们在程序执行时所需要的功能

如果多个.go文件在同1个文件夹(包)中,那它们就可以直接使用相互的功能,无需导入包。 

在包同定义的功能首字母大写,才可以被外部包所调用,否则只能在同1个包中使用。

 

 

使用package

定义package

一般来说除了1个程序入口(main包)之外,在Go项目中其他都是我们程序开发人员自由定义的(工具包),以便调用。

mai包可以生成1个可执行文件。

我们使用package关键字定义1个包,

如果当前package想要被其他包调用,那么该工具包中的标识符(变量名、函数名、struct名、interface名)首字母大写表示可见性(对外可见)。

 

go语言 jar包 go语言的包管理_go语言 jar包

 在Go中1个文件夹就是1个package, 所以package中的.go文件一般会和package同名。  

 programmer.go

package devlepment

import "fmt"

//Programer 大写才能被外部其他包调用
func Programer(name string) {
	fmt.Printf(`
		I'm %s  a programmer work in devlepment department
		I interest in programing.in my free time I like seing some Science fiction films ranther than porn.
		`, name)
}

 

init函数

包中的init函数在调用该包时自动执行

package resources

import "fmt"

//init:导入包时自动执行
func init(){
	fmt.Println("导入resources包时自动执行")
	receptionist("May")
}

func receptionist(nam string){
	fmt.Println(`Good morning.may I help you? `)
}

 

init()函数执行顺序

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

Golang中1个包可以包含1个init函数,不一样的是这个init函数需要自己定义。

调用包时会最先初始化并调用包中mian函数的init()函数, 如下图示:

go语言 jar包 go语言的包管理_go语言 jar包_02

 

 

 

导入package

调用Go里面的包需要从Go project的src目录后面的文件夹开始import。

go语言 jar包 go语言的包管理_Go_03

 

 hello/package_practice/devlepment

 

package main

import (
	//匿名调用包:只调用执行包中init函数中的代码,不使用(pymysql)
	_ "hello/package_practice/resources"
	//别名:包所在的路径
	devlepment "hello/package_practice/devlepment"
	sales "hello/package_practice/sales"
	finnace "hello/package_practice/finance"

)

func main(){
	devlepment.Programer("Sam")
	finnace.Contant("Sally")
	sales.Salesman("Todd")
	
	
	
}

 

  

go mod第三方依赖包管理

为什么需要go mod?

go语言 jar包 go语言的包管理_go语言 jar包_04

 

Golang中所有的项目、项目依赖的第三方库,默认情况下全部放在go path/src这1个目录下。

如果要使用N个不同版本的第三方依赖库,应该怎么解决?有没有Python env这种环境隔离机制?

 

1.godep依赖包管理

Go语言从v1.5开始开始引入vendor模式,如果项目目录下有vendor目录,那么go工具链会优先使用vendor内的包进行编译、测试等。

 

安装

go get github.com/tools/godep

 

基本命令

godep save     将依赖项输出并复制到Godeps.json文件中
godep go       使用保存的依赖项运行go工具
godep get      下载并安装具有指定依赖项的包
godep path     打印依赖的GOPATH路径
godep restore  在GOPATH中拉取依赖的版本
godep update   更新选定的包或go版本
godep diff     显示当前和以前保存的依赖项集之间的差异
godep version  查看版本信息

 

使用godep

在项目目录下执行godep save命令,会在当前项目中创建Godepsvender两个文件夹。

Godeps文件夹:有一个Godeps.json的文件,里面记录了项目所依赖的包信息。 

vender文件夹:是项目依赖的包的源代码文件。

 

vender机制

Go1.5版本之后开始支持,能够控制Go语言程序编译时依赖包搜索路径的优先级。

如果项目require某个依赖包,如果你有vendor目录。

首先会在项目根目录下的vender文件夹中查找,如果没有找到再去$GOAPTH/src目录下查找。

 

godep开发流程

  1. 保证程序能够正常编译
  2. 执行godep save保存当前项目的所有第三方依赖的版本信息和代码
  3. 提交Godeps目录和vender目录到代码库。
  4. 如果要更新依赖的版本,可以直接修改Godeps.json文件中的对应项

 

2.go module依赖包管理

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

 

0.如果你使用的是go 1.13之前的版本,那么在使用go mod之前需要手动开启对go mod的支持:

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

  1. GO111MODULE=off禁用模块支持,编译时会从GOPATHvendor文件夹中查找包。
  2. GO111MODULE=on启用模块支持,编译时会忽略GOPATHvendor文件夹,只根据 go.mod下载依赖。
  3. GO111MODULE=auto,当项目在$GOPATH/src外且项目根目录有go.mod文件时,开启模块支持。

简单来说,设置GO111MODULE=on之后就可以使用go module了,以后就没有必要在GOPATH中创建项目了,并且还能够很好的管理项目依赖的第三方包信息。

 

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

go.mod:有的像node.js的pakage.json存储当前项目所有依赖库的版本

go.sum:所有依赖的集合

 

我使用的就是go1.13所以不需要每次都开启go mod模式。

D:\goproject\src>go env
set GO111MODULE=on
set GOPROXY=https://goproxy.cn

 

go mod命令

我们使用go mod无非处于2种目的

目的1.我自己开发的项目生成go.mod让别人同步我的依赖包

目的2.别人写得项目,我跟他的依赖包版本保持一致。

go mod get
go mod download    从github或者指定代理下载第三方依赖---->本地cache(默认为$GOPATH/pkg/mod目录)
go mod edit        编辑go.mod文件
go mod graph       打印模块依赖图
go mod init        初始化当前文件夹, 创建go.mod文件
go mod tidy        整理依赖(增加缺少的module,删除无用的module)校验go mod中设置的版本和D:\goproject\pkg\mod中的版本是否一致!
go mod vendor      将依赖复制到vendor下
go mod verify      校验依赖
go mod why         解释为什么需要依赖

 

自己生成go.mod流程

D:\goproject\src\go相关模块\kafka>SET GO111MODULE=on

D:\goproject\src\go相关模块\kafka>SET GOPROXY=https://goproxy.cn

D:\goproject\src\go相关模块\kafka>go mod init
go: creating new go.mod: module go相关模块/kafka

D:\goproject\src\go相关模块\kafka>go mod download
go: finding github.com/Shopify/sarama v1.19.0

  

go mod文件语法

go.mod文件记录了项目所有的依赖信息,其结构大致如下:

module jd.com/logagent //当前包名

go 1.13   //go版本

require ( //在这里指定第三方依赖库的版本信息
	github.com/Shopify/sarama v1.19.0  
	github.com/davecgh/go-spew v1.1.1 // indirect 
	github.com/eapache/go-resiliency v1.2.0 // indirect
	github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect
	github.com/eapache/queue v1.1.0 // indirect
	github.com/golang/snappy v0.0.1 // indirect
	github.com/hpcloud/tail v1.0.0
	github.com/pierrec/lz4 v2.5.2+incompatible // indirect
	github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect
	gopkg.in/fsnotify.v1 v1.4.7 // indirect
	gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
)

replace语法

在国内访问golang.org/x的各个包都需要FQ,你可以在go.mod中使用replace替换成github上对应的库。

replace (
	golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac => github.com/golang/crypto v0.0.0-20180820150726-614d502a4dac
	golang.org/x/net v0.0.0-20180821023952-922f4815f713 => github.com/golang/net v0.0.0-20180826012351-8a410e7b638d
	golang.org/x/text v0.3.0 => github.com/golang/text v0.3.0
)

  

go get

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

  1. 运行go get -u将会升级到最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号)
  2. 运行go get -u=patch将会升级到最新的修订版本
  3. 运行go get package@version将会升级到指定的版本号version

如果下载所有依赖可以使用go mod download命令。

 

3.运行1个gin项目

我使用go 1.14版本结合go mod,在go path之外运行1个gin项目。

mkdir zhanggen
cd zhanggen
go mod init zhanggen
ls
pwd  //在/zhanggen目录下多个1个go.mod文件
cd zhanggen
SET GOPROXY=https://goproxy.cn  //设置代理加速下载

go mod环境已经准备完毕 ,开始写代码吧。

package main

import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    r.Run() // listen and serve on 0.0.0.0:8080
}

代码写完了就开始运行代码吧。

D:\zhanggen>go run main.go
go: finding module for package github.com/gin-gonic/gin
go: found github.com/gin-gonic/gin in github.com/gin-gonic/gin v1.6.3
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /ping                     --> main.main.func1 (3 handlers)
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080

 

4.依赖包版本迭代 

修改go.mod文件中的require版本即可。

 

go语言 jar包 go语言的包管理_依赖包_05

 

 5.重启

go mod第三方包管理比Python还方便,不用自己去 pip install requirement.txt了。

运行时自动根据go.mod更新。

D:\zhanggen>go run main.go
go: downloading github.com/gin-gonic/gin v1.6.2  自动去下载和切换版本了 6666
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

 

 

see also