Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > Go 入口函数和包初始化

Go 的入口函数和包初始化的使用

作者:小菠萝测试笔记

本文主要介绍了Go 的入口函数和包初始化的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

包 package

main.main 函数:Go 应用的入口函数

package main

package main

func main() {    
    // 用户层执行逻辑
    ... ...
}

Go 语言要求:可执行程序的 main 包必须定义 main 函数,否则 Go 编译器会报错

注意

main 包是不可以像标准库 fmt 包那样被导入(Import)的

其他包也可以拥有 main 函数或方法

按照 Go 的可见性规则(小写字母卡头的标识符为非导出标识符),非 main包中自定义的 main 函数仅限于包内使用

package pkg1

import "fmt"

func Main () {
    main()
}

func main(){
    fmt.Println("main func for pkg1")
}

重点

引子

不过对于 main 包的main 函数来说,还需要明确一点,就是它虽然是用户层逻辑的入口函数,但它却不一定是用户层第一个被执行的函数。这是为什么呢?这跟 Go 语言的另一个函数 init 有关

init 函数:Go 包的初始化函数

和 main.main 函数一样,init 函数也是一个无参数无返回值的函数

func init() {
	// 包初始化逻辑
	... ...
}

和 main 函数不一样

init 函数的执行顺序

Go 包的初始化次序

三步走

init 函数的特点

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 包完成初始化后,可以处于合理状态

在 init 函数中实现“注册模式”

来看一段使用 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 函数就是这个模式中的工厂方法,它根据外部传入的驱动名称“生产”出不同类别的数据库实例句柄

通过注册模式实现获取各种格式图片的宽、高

Go 源码

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)
}

到此这篇关于Go 的入口函数和包初始化的使用的文章就介绍到这了,更多相关Go 入口函数和包初始化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文