golang中的aop方案一:proxy模式

276 阅读2分钟

1.目前go里面实现aop的手段

1.代理模式
2.代码生成器模式
3.利用汇编运行时调整

以后的系列文章我们将陆续介绍后面两种手段,今天我们以代理模式开讲。

2.大白话AOP

AOP简单来说就是在目标方法执行前后,或执行期间进行无入侵性,加入逻辑。举例来说: 例如Java的拦截器,.NET的过滤器等都有此功效,但这两类语言都可以基于字节码或EMIT等事先运行时动态代理,JAVA还可以利用gclib实现更厉害的功能。但对于golang来说就有点尴尬了,因为目前语言层面没有给我们提供这样的能力。

3.功能样例

我们简单模拟一下功能代码: 有一个IAccount接口,声明了方法Query和Update,分别用于查询帐户和更新帐户 类AccountImpl实现了IAccount接口,有一个简单工厂New,用于创建一个IAccount对象 ,代码结构如下

image.png

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的实现即可。

注意:严格要求编译流程。