1.目前go里面实现aop的手段
1.代理模式
2.代码生成器模式
3.利用汇编运行时调整
以后的系列文章我们将陆续介绍后面两种手段,今天我们以代理模式开讲。
2.大白话AOP
AOP简单来说就是在目标方法执行前后,或执行期间进行无入侵性,加入逻辑。举例来说: 例如Java的拦截器,.NET的过滤器等都有此功效,但这两类语言都可以基于字节码或EMIT等事先运行时动态代理,JAVA还可以利用gclib实现更厉害的功能。但对于golang来说就有点尴尬了,因为目前语言层面没有给我们提供这样的能力。
3.功能样例
我们简单模拟一下功能代码: 有一个IAccount接口,声明了方法Query和Update,分别用于查询帐户和更新帐户 类AccountImpl实现了IAccount接口,有一个简单工厂New,用于创建一个IAccount对象 ,代码结构如下
1.main.go
package main
import (
"study2/irepository"
"study2/proxy"
"study2/repository"
)
func main() {
id := "123"
a := repository.NewAccount(id, "ABC", 100)
a.Query(id)
a.Update(id, 500)
}
func init() {
repository.NewAccount = func(id, name string, value int) irepository.IAccount {
a := &repository.Account{id, name, value}
p := &proxy.AccountProxy{a}
return p
}
}
2.业务接口
package irepository
type IAccount interface {
Query(id string) int
Update(id string, value int)
}
3.业务代码
package repository
import (
"fmt"
"study2/irepository"
)
type Account struct {
Id string
Name string
Value int
}
func (a *Account) Query(_ string) int {
fmt.Println("Account.Query")
return 100
}
func (a *Account) Update(_ string, _ int) {
fmt.Println("Account.Update")
}
var NewAccount = func(id, name string, value int) irepository.IAccount {
return &Account{id, name, value}
}
4.代理类
package proxy
import (
"fmt"
"study2/irepository"
)
type AccountProxy struct {
Account irepository.IAccount
}
func (p *AccountProxy) Query(id string) int {
fmt.Println("Proxy.Query begin")
value := p.Account.Query(id)
fmt.Println("Proxy.Query end")
return value
}
func (p *AccountProxy) Update(id string, value int) {
fmt.Println("Proxy.Update begin")
p.Account.Update(id, value)
fmt.Println("Proxy.Update end")
}
5.运行效果
Proxy.Query begin
Account.Query
Proxy.Query end
Proxy.Update begin
Account.Update
Proxy.Update end
4.思考
完全符合期望,而且拦截器对产品代码基本无侵入,仅需在main包的init函数中重定义简单工厂函数变量。本文结合代码给出了Golang拦截器的一种实现,即“静态代理模式 + 简单工厂函数变量”。读者掌握拦截器的实现技巧后,可以基本无侵入的实现微服务的trace功能。
问题:怎么让Proxy的编写工作不要这么重复呢?还有如何让NewAccount的重定义工作自动化?
利用ast(抽象语法树)做一个自定义的代码生成器,在编译前自动调用代码生成器,参照wire的实现即可。
注意:严格要求编译流程。