欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

Go能實(shí)現(xiàn)AOP嗎?

hello~大家好,我是小樓,今天分享的話題是Go是否能實(shí)現(xiàn)AOP?

成都創(chuàng)新互聯(lián)公司主要從事網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作、網(wǎng)頁設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)金門,十年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):13518219792

背景

寫Java的同學(xué)來寫Go就特別喜歡將兩者進(jìn)行對(duì)比,就經(jīng)??吹郊夹g(shù)群里討論,比如Go能不能實(shí)現(xiàn)Java那樣的AOP???Go寫個(gè)事務(wù)好麻煩啊,有沒有Spring那樣的@Transactional注解???

遇到這樣的問題我通常會(huì)回復(fù):沒有、實(shí)現(xiàn)不了、再見。

直到看了《Go語言底層原理剖析》這本書,開始了一輪認(rèn)真地探索。

Java是如何實(shí)現(xiàn)AOP的

AOP概念第一次是在若干年前學(xué)Java時(shí)看的一本書《Spring實(shí)戰(zhàn)》中看到的,它指的是一種面向切面編程的思想。注意它只是一種思想,具體怎么實(shí)現(xiàn),你看著辦。

AOP能在你代碼的前后織入代碼,這就能做很多有意思的事情了,比如統(tǒng)一的日志打印、監(jiān)控埋點(diǎn),事務(wù)的開關(guān),緩存等等。

可以分享一個(gè)我當(dāng)年學(xué)習(xí)AOP時(shí)的筆記片段:

在Java中的實(shí)現(xiàn)方式可以是JDK動(dòng)態(tài)代理字節(jié)碼增強(qiáng)技術(shù)。

JDK動(dòng)態(tài)代理是在運(yùn)行時(shí)動(dòng)態(tài)地生成了一個(gè)代理類,JVM通過加載這個(gè)代理類再實(shí)例化來實(shí)現(xiàn)AOP的能力。

字節(jié)碼增強(qiáng)技術(shù)可以多嘮叨兩句,當(dāng)年學(xué)Java時(shí)第一章就說Java的特點(diǎn)是「一次編譯,到處運(yùn)行」。

但當(dāng)我們真正在工作中這個(gè)特性用處大嗎?好像并不大,生產(chǎn)中都使用了同一種服務(wù)器,只編譯了一次,也都只在這個(gè)系統(tǒng)運(yùn)行。做到一次編譯,到處運(yùn)行的技術(shù)底座是JVM,JVM可以加載字節(jié)碼并運(yùn)行,這個(gè)字節(jié)碼是平臺(tái)無關(guān)的一種二進(jìn)制中間碼。

似乎這個(gè)設(shè)定帶來了一些其他的好處。在JVM加載字節(jié)碼時(shí),字節(jié)碼有一次被修改的機(jī)會(huì),但這個(gè)字節(jié)碼的修改比較復(fù)雜,好在有現(xiàn)成的庫可用,如ASM、Javassist等。

至于像ASM這樣的庫是如何修改字節(jié)碼的,我還真就去問了Alibaba Dragonwell的一位朋友,他回答ASM是基于Java字節(jié)碼規(guī)范所做的「硬改」,但做了一些抽象,總體來說還是比較枯燥的。

由于這不是本文重點(diǎn),所以只是提一下,如果想更詳細(xì)地了解可自行網(wǎng)上搜索。

Go能否實(shí)現(xiàn)AOP?

之前用「扁鵲三連」的方式回復(fù)Go不能實(shí)現(xiàn)AOP的基礎(chǔ)其實(shí)就是我對(duì)Java實(shí)現(xiàn)AOP的思考,因?yàn)镚o沒有虛擬機(jī)一說,也沒有中間碼,直接源碼編譯為可執(zhí)行文件,可執(zhí)行文件基本沒法修改,所以做不了。

但真就如此嗎?我搜索了一番。

運(yùn)行時(shí)攔截

還真就在Github找到了一個(gè)能實(shí)現(xiàn)類似AOP功能的庫gohook(當(dāng)然也有類似的其他庫):

https://github.com/brahma-adshonor/gohook

看這個(gè)項(xiàng)目的介紹:

運(yùn)行時(shí)動(dòng)態(tài)地hook Go的方法,也就是可以在方法前插入一些邏輯。它是怎么做到的?

通過反射找到方法的地址(指針),然后插入一段代碼,執(zhí)行完后再執(zhí)行原方法。聽起來很牛X,但它下面有個(gè)Notes:

使用有一些限制,更重要的是沒有完全測(cè)試,不建議生產(chǎn)使用。這種不可靠的方式也就不嘗試了。

AST修改源碼

這種方式就是我在看《Go語言底層原理剖析》第一章看到的,其實(shí)我之前的文章也有寫過關(guān)于AST的,《Cobar源碼分析之AST》。

AST即抽象語法樹,可以認(rèn)為所有的高級(jí)編程語言都可以抽象為一種語法樹,即對(duì)代碼進(jìn)行結(jié)構(gòu)化的抽象,這種抽象可以讓我們更加簡(jiǎn)單地分析甚至操作源碼。

Go在編譯時(shí)大概分為詞法與語法分析、類型檢查、通用 SSA 生成和最后的機(jī)器代碼生成這幾個(gè)階段。

其中詞法與語法分析之后,生成一個(gè)AST樹,在Go中我們能調(diào)用Go提供的API很輕易地生成AST:

fset := token.NewFileSet()
// 這里file就是一個(gè)AST對(duì)象
file, err := parser.ParseFile(fset, "aop.go", nil, parser.ParseComments)

比如這里我的aop.go文件是這樣的:

package main

import "fmt"

func main() {
	fmt.Println(execute("roshi"))
}

func execute(name string) string {
	return name
}

想看生成的AST長(zhǎng)什么樣,可調(diào)用下面的方法:

ast.Print(fset, file)

由于篇幅太長(zhǎng),我截個(gè)圖感受下即可:

當(dāng)然也有一些開源的可視化工具,但我覺得大可不必,想看的話Debug看下file的結(jié)構(gòu)。

至于Go AST結(jié)構(gòu)的介紹,也不是本文的重點(diǎn),而且AST中的類型很多很多,我建議如果你想看的話直接Debug來看,對(duì)照源碼比較清晰。

我們這里就實(shí)現(xiàn)一個(gè)簡(jiǎn)單的,在execute方法執(zhí)行之前添加一條打印before的語句,接上述代碼:

const before = "fmt.Println(\"before\")"
...

exprInsert, err := parser.ParseExpr(before)
if err != nil {
	panic(err)
}

decls := make([]ast.Decl, 0, len(file.Decls))

for _, decl := range file.Decls {
	fd, ok := decl.(*ast.FuncDecl)
	if ok {
		if fd.Name.Name == "execute" {
			stats := make([]ast.Stmt, 0, len(fd.Body.List)+1)
			stats = append(stats, &ast.ExprStmt{
				X: exprInsert,
			})
			stats = append(stats, fd.Body.List...)
			fd.Body.List = stats
			decls = append(decls, fd)
			continue
		} else {
			decls = append(decls, decl)
		}
	} else {
		decls = append(decls, decl)
	}
}

file.Decls = decls

這里AST就被我們修改了,雖然我們是寫死了針對(duì)execute方法,但總歸是邁出了第一步。

再把AST轉(zhuǎn)換為源碼輸出,Go也提供了API:

var cfg printer.Config
var buf bytes.Buffer

cfg.Fprint(&buf, fset, file)

fmt.Printf(buf.String())

輸出效果如下:

看到這里,我猜你應(yīng)該有和我相同的想法,這玩意是不是可以用來格式化代碼?

沒錯(cuò),Go自帶的格式化代碼工具gofmt的原理就是如此。

當(dāng)我們寫完代碼時(shí),可以執(zhí)行g(shù)ofmt對(duì)代碼進(jìn)行格式化:

gofmt test.go

這相比于其他語言方便很多,終于有個(gè)官方的代碼格式了,甚至你可以在IDEA中安裝一個(gè)file watchers插件,監(jiān)聽文件變更,當(dāng)文件有變化時(shí)自動(dòng)執(zhí)行 gofmt 來格式化代碼。

看到這里你可能覺得太簡(jiǎn)單了,我查了下資料,AST中還能拿到注釋,這就厲害了,我們可以把注釋當(dāng)注解來玩,比如我加了 // before: 的注釋,自動(dòng)把這個(gè)注釋后的代碼添加到方法之前去。

// before:fmt.Println("before...")
func executeComment(name string) string {
	return name
}

修改AST代碼如下,為了篇幅,省略了打印代碼:

cmap := ast.NewCommentMap(fset, file, file.Comments)

for _, decl := range file.Decls {
	fd, ok := decl.(*ast.FuncDecl)
	if ok {
		if cs, ok := cmap[fd]; ok {
			for _, cg := range cs {
				for _, c := range cg.List {
					if strings.HasPrefix(c.Text, "http:// before:") {
						txt := strings.TrimPrefix(c.Text, "http:// before:")
						ei, err := parser.ParseExpr(txt)
						if err == nil {
							stats := make([]ast.Stmt, 0, len(fd.Body.List)+1)
							stats = append(stats, &ast.ExprStmt{
								X: ei,
							})
							stats = append(stats, fd.Body.List...)
							fd.Body.List = stats
							decls = append(decls, fd)
							continue
						}
					}
				}
			}
		} else {
			decls = append(decls, decl)
		}
	} else {
		decls = append(decls, decl)
	}
}

file.Decls = decls

跑一下看看:

雖然又是硬編碼,但這不重要,又不是不能用~

但你發(fā)現(xiàn),這樣實(shí)現(xiàn)AOP有個(gè)缺點(diǎn),必須在編譯期對(duì)代碼進(jìn)行一次重新生成,理論上來說,所有高級(jí)編程語言都可以這么操作。

但這不是說毫無用處,比如這篇文章《每個(gè) gopher 都需要了解的 Go AST》就給了我們一個(gè)實(shí)際的案例:

最后

寫到最后,我又在思考另一個(gè)問題,為什么Go的使用者沒有AOP的需求呢?反倒是寫Java的同學(xué)會(huì)想到AOP。

我覺得可能還是Go太年輕了,Java之所以要用AOP,很大的原因是代碼已經(jīng)堆積如山,沒法修改,歷史包袱沉重,最小代價(jià)實(shí)現(xiàn)需求是首選,所以會(huì)選擇AOP這種技術(shù)。

反觀Go還年輕,大多數(shù)項(xiàng)目屬于造輪子期間,需要AOP的地方早就在代碼中提前埋伏好了。我相信隨著發(fā)展,一定也會(huì)出現(xiàn)一個(gè)生產(chǎn)可用Go AOP框架。

至于現(xiàn)在問我,Go能否實(shí)現(xiàn)AOP,我還是回答:沒有、實(shí)現(xiàn)不了、再見。

對(duì)了,本文的完整測(cè)試代碼這里可以看到:

https://github.com/lkxiaolou/all-in-one/tree/master/go-in-one/samples/tree

感謝大家,如果有點(diǎn)收獲,點(diǎn)個(gè)在看、關(guān)注吧,我們下期再見。


搜索關(guān)注微信公眾號(hào)"捉蟲大師",后端技術(shù)分享,架構(gòu)設(shè)計(jì)、性能優(yōu)化、源碼閱讀、問題排查、踩坑實(shí)踐。

網(wǎng)站題目:Go能實(shí)現(xiàn)AOP嗎?
轉(zhuǎn)載來于:http://chinadenli.net/article24/dsoisce.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站制作、網(wǎng)站設(shè)計(jì)品牌網(wǎng)站建設(shè)、微信小程序、服務(wù)器托管、手機(jī)網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

小程序開發(fā)