时间:2022-07-15 08:35:52 | 栏目:Golang | 点击:次
package 包名
语句,通过该语句声明源码文件所在的包main.main
,它是所有 Go 可执行程序的用户层执行逻辑的入口函数package main
想要引用别的包的代码,必须同样以包的方式进行引用package main func main() { // 用户层执行逻辑 ... ... }
Go 语言要求:可执行程序的 main 包必须定义 main 函数,否则 Go 编译器会报错
main 包是不可以像标准库 fmt 包那样被导入(Import)的
按照 Go 的可见性规则(小写字母卡头的标识符为非导出标识符),非 main包中自定义的 main 函数仅限于包内使用
package pkg1 import "fmt" func Main () { main() } func main(){ fmt.Println("main func for pkg1") }
不过对于 main 包的main 函数来说,还需要明确一点,就是它虽然是用户层逻辑的入口函数,但它却不一定是用户层第一个被执行的函数。这是为什么呢?这跟 Go 语言的另一个函数 init 有关
和 main.main 函数一样,init 函数也是一个无参数无返回值的函数
func init() { // 包初始化逻辑 ... ... }
三步走
init 函数就好比 Go 包真正投入使用之前唯一的“质检员”,负责对包内部以及暴露到外部的包级数据(主要是包级变量)的初始状态进行检查
有些包级变量需要一个比较复杂的初始化过程,有些时候,使用它的类型零值或通过简单初始化表达式不能满足业务逻辑要求,而 init 函数则非常适合完成此项工作,标准库 http 包中就有这样一个典型示例
package main import ( "os" "strings" ) var ( http2VerboseLogs bool // 初始化默认值 false http2logFrameWrites bool http2logFrameReads bool http2inTests bool ) func init() { e := os.Getenv("GODEBUG") if strings.Contains(e, "http2debug=1") { http2VerboseLogs = true // 在 init 中对 http2VerboseLogs 的值进行重置 } if strings.Contains(e, "http2debug=2") { http2logFrameWrites = true http2logFrameReads = true http2inTests = true } }
http 包在init 函数中,就根据环境变量 GODEBUG 的值,对这些包级开关变量进行了复杂的初始化,从而保证了这些开关变量在 http 包完成初始化后,可以处于合理状态
来看一段使用 lib/pq 包访问 PostgreSQL 数据库的代码 ?
package main import ( "database/sql" "log" _ "github.com/lib/pq" ) func main() { db, err := sql.Open("postgres", "user=pqgotest dbname=pqgotest sslmode=ver) if err != nil { log.Fatal(err) } age := 21 rows, err := db.Query("SELECT name FROM users WHERE age = $1", age) } 复制代码
这里是以空导入_
的方式导入 lib/pq 包的,main 函数中没有使用 pq 包的任何变量、函数或方法,这样就实现了对 PostgreSQL数据库的访问
在 pq 包的 conn.go 源码文件中的 init 函数
func init() { sql.Register("postgres", &Driver{}) }
好处:这种通过在 init 函数中注册自己的实现的模式,就有效降低了 Go 包对外的直接暴露,尤其是包级变量的暴露,从而避免了外部通过包级变量对包状态的改动
工厂设计模式
从标准库 database/sql 包的角度来看,这种“注册模式”实质是一种工厂设计模式的实现,sql.Open
函数就是这个模式中的工厂方法,它根据外部传入的驱动名称“生产”出不同类别的数据库实例句柄
package main import ( "fmt" "image" _ "image/gif" _ "image/jpeg" _ "image/png" "os" ) func main() { // 支持 png、jpeg、gif width, height, err := imageSize(os.Args[1]) if err != nil { fmt.Println("get image size error:", err) return } fmt.Printf("image size: [%d,%d]\n", width, height) } func imageSize(imageFile string) (int, int, error) { // 打开图片文件 f, _ := os.Open(imageFile) defer f.Close() // 对文件进行解码,得到图片实例 img, _, err := image.Decode(f) if err != nil { return 0, 0, err } // 返回图片区域 b := img.Bounds() return b.Max.X, b.Max.Y, nil }
// $GOROOT/src/image/png/reader.go func init() { image.RegisterFormat("png", pngHeader, Decode, DecodeConfig) } // $GOROOT/src/image/jpeg/reader.go func init() { image.RegisterFormat("jpeg", "\xff\xd8", Decode, DecodeConfig) } // $GOROOT/src/image/gif/reader.go func init() { image.RegisterFormat("gif", "GIF8?a", Decode, DecodeConfig) }