真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

golang中的init初始化函數(shù)

0.1、索引

https://waterflow.link/articles/

創(chuàng)新互聯(lián)建站長期為成百上千客戶提供的網(wǎng)站建設(shè)服務(wù),團隊從業(yè)經(jīng)驗10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為大姚企業(yè)提供專業(yè)的成都網(wǎng)站制作、成都做網(wǎng)站,大姚網(wǎng)站改版等技術(shù)服務(wù)。擁有10余年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。

1、概念

1.1、源文件里的代碼執(zhí)行順序

init 函數(shù)是用于初始化應(yīng)用程序狀態(tài)的函數(shù)。 它不接受任何參數(shù)并且不返回任何結(jié)果(一個 func() 函數(shù))。 初始化包時,將初始化包中的所有常量和變量聲明。 然后,執(zhí)行初始化函數(shù)。 下面是一個初始化主包的例子:

package main

import "fmt"

// 1
var a = func() int {
	fmt.Println("var a")
	return 0
}()

// 2
func init()  {
	fmt.Println("init")
}

// 1
var b = func() int {
	fmt.Println("var b")
	return 1
}()

// 3
func main() {
	fmt.Println("main")
}

上面代碼的初始化順序是:

  1. 初始化常量/變量(雖然b在init函數(shù)后面,但是會首先初始化)
  2. 初始化init函數(shù)
  3. 執(zhí)行main函數(shù)

我們看下打印的結(jié)果:

go run 2.go
var a
var b
init
main

1.2、不同包的init函數(shù)執(zhí)行順序

初始化包時會執(zhí)行一個 init 函數(shù)。 在下面的例子中,我們定義了兩個包,main 和 redis,其中 main 依賴于 redis。 首先, 2 .go 是主包:

package main

import (
	"fmt"
	"go-demo/100gomistakes/2/redis"
)

// 2
func init()  {
	fmt.Println("main init")
}

// 3
func main() {
	err := redis.Store("ni", "hao")
	fmt.Println(err)
}

我們可以看到main包中調(diào)用了redis包的方法。

我們再看下redis包中的內(nèi)容:

package redis

import "fmt"

// 1
func init()  {
	fmt.Println("redis init")
}

func Store(key, value string) error {
	return nil
}

因為main依賴redis,所以先執(zhí)行redis包的init函數(shù),然后是main包的init,然后是main函數(shù)本身。上面的代碼中標(biāo)明了執(zhí)行順序。

1.3、同一個包不同文件init執(zhí)行順序

我們可以為每個包定義多個初始化函數(shù)。 當(dāng)我們這樣做時,包內(nèi)的 init 函數(shù)的執(zhí)行順序是基于源文件的字母順序。 例如,如果一個包包含一個 a.go 文件和一個 b.go 文件,并且都有一個 init 函數(shù),則首先執(zhí)行 a.go init 函數(shù)。

但是如果我們把文件a.go改為ca.go,則會先執(zhí)行b.go的init函數(shù)。

所以我們不應(yīng)該依賴包中初始化函數(shù)的順序。 實際上,這可能很危險,因為可以重命名源文件,從而可能影響執(zhí)行順序。

下圖說明了這個問題:

1.4、同一個文件中的init函數(shù)

當(dāng)然,我們還可以在同一個源文件中定義多個初始化函數(shù):

package main

import (
	"fmt"
	"go-demo/100gomistakes/2/redis"
)


func init()  {
	fmt.Println("main init1")
}

func init()  {
	fmt.Println("main init2")
}


func main() {
	err := redis.Store("ni", "hao")
	fmt.Println(err)
}

執(zhí)行順序是按照源文件順序執(zhí)行的,我們看下打印的結(jié)果:

go run 2.go
redis2 init
redis init
main init1
main init2

1.5、以副作用方式執(zhí)行的init函數(shù)

我們開發(fā)的時候經(jīng)常會使用gorm查詢數(shù)據(jù)庫,所以我們經(jīng)常會看到在初始化db連接的時候會有下面的代碼:

package models

import (
	_ "github.com/go-sql-driver/mysql"
	"github.com/jinzhu/gorm"
)

這種情況就是說,我們沒有強依賴mysql包(沒有直接使用mysql的公共函數(shù))。但是我們需要初始化mysql包里的一些數(shù)據(jù),也就是執(zhí)行mysql包里的init函數(shù)。這個時候我們就可以在這個包的前面加上_。

需要注意的是init函數(shù)不能被當(dāng)作普通函數(shù)調(diào)用,會編譯報錯。

2、使用場景

首先,讓我們看一個使用 init 函數(shù)可能被認為不合適的示例:持有數(shù)據(jù)庫連接池。 在示例的 init 函數(shù)中,我們使用 sql.Open 打開一個數(shù)據(jù)庫。 這個全局變量db會被其他函數(shù)調(diào)用:

package main

import (
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"log"
)

var db *sql.DB

func init() {
	d, err := sql.Open("mysql",
		"root:liufutian@tcp(127.0.0.1:3306)/test")
	if err != nil {
		log.Panic(err)
	}

  err := db.Ping()
	if err != nil {
		log.Panic(err)
	}
  
	db = d
}

func main() {


}

在本例中,我們打開數(shù)據(jù)庫,檢查是否可以 ping 通,然后將其分配給全局變量。

但是這種實現(xiàn)會帶來一些問題:

  1. init 函數(shù)中的錯誤管理是有限的。 實際上,由于 init 函數(shù)不返回錯誤,因此發(fā)出錯誤信號的唯一方法之一就是panic,導(dǎo)致應(yīng)用程序停止。 在我們的示例中,如果打開數(shù)據(jù)庫失敗,無論如何都會停止應(yīng)用程序。 但是,不一定要由包本身來決定是否停止應(yīng)用程序。 有可能調(diào)用者想實現(xiàn)重試或使用回退機制。 在這種情況下,在 init 函數(shù)中打開數(shù)據(jù)庫會阻止客戶端包實現(xiàn)其錯誤處理邏輯。
  2. 如果我們向這個文件添加測試,init 函數(shù)將在運行測試用例之前執(zhí)行,但是我們想要的是創(chuàng)建db連接的時候需要測試。 因此,此示例中的 init 函數(shù)使編寫單元測試變得復(fù)雜。
  3. 該示例需要將數(shù)據(jù)庫連接池分配給全局變量。 全局變量有一些嚴重的缺點; 例如:
    • 任何函數(shù)都可以更改包中的全局變量
    • 單元測試可能會更復(fù)雜,因為依賴于全局變量的函數(shù)將不再被隔離

在大多數(shù)情況下,我們應(yīng)該傾向于封裝一個變量而不是讓它保持全局。

由于這些原因,之前的初始化可能應(yīng)該封裝到一個函數(shù)中處理,如下所示:

func createDB() (*sql.DB, error) {
	d, err := sql.Open("mysql",
		"root:liufutian@tcp(127.0.0.1:3306)/test")
	if err != nil {
		return nil, err
	}
	err = db.Ping()
	if err != nil {
		return nil, err
	}
	return d, nil
}

這樣寫的話,我們解決了之前討論的主要缺點:

  • 是否處理錯誤留給調(diào)用者
  • 可以創(chuàng)建一個集成測試來檢查此功能是否有效
  • 連接池封裝在函數(shù)中

但是這樣就是不能使用init函數(shù)了么?在我們上面的引入mysql驅(qū)動的例子中,說明使用init還是有幫助的:

func init() {
	sql.Register("mysql", &MySQLDriver{})
}

上面的例子,通過注冊提供的驅(qū)動名稱使數(shù)據(jù)庫驅(qū)動程序可用。


分享文章:golang中的init初始化函數(shù)
文章來源:http://weahome.cn/article/dsoidhd.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部