README

AOP

Aspect Oriented Programming For Golang

current version is in alpha, welcome to submit your ideas (api is not stable current version)

Basic Usage
define struct
type Auth struct {
}

func (p *Auth) Login(userName, password string) bool {
	if userName == "zeal" && password == "gogap" {
		return true
	}
	return false
}

// use join point to get Args from real method
func (p *Auth) Before(jp aop.JoinPointer) {
	username := ""
	jp.Args().MapTo(func(u, p string) {
		username = u
	})

	fmt.Printf("Before Login: %s\n", username)
}

// the args is same as Login
func (p *Auth) After(username, password string) {
	fmt.Printf("After Login: %s %s\n", username, password)
}

// use join point to around the real func of login
func (p *Auth) Around(pjp aop.ProceedingJoinPointer) {
	fmt.Println("@Begin Around")

	ret := pjp.Proceed("fakeName", "fakePassword")
	ret.MapTo(func(loginResult bool) {
		fmt.Println("@Proceed Result is", loginResult)
	})

	fmt.Println("@End Around")
}

In this case, we want call Before() func before Login(), and After() func after Login()

In general, we will do it like as following

func (p *Auth) Login(userName string, password string) bool {
	p.Before(userName, password)
	defer p.After(userName, password)
	
	if userName == "zeal" && password == "gogap" {
		return true
	}
	return false
}

So, if we have more funcs to call before and after, it will pollution the real logic func Login(), we want a proxy help us to invoke Before() and After() automatic.

That was what AOP does.

Step 1: Define Beans factory
beanFactory := aop.NewClassicBeanFactory()
beanFactory.RegisterBean("auth", new(Auth))
Step 2: Define Aspect
aspect := aop.NewAspect("aspect_1", "auth")
aspect.SetBeanFactory(beanFactory)
Step 3: Define Pointcut
pointcut := aop.NewPointcut("pointcut_1").Execution(`Login()`)
aspect.AddPointcut(pointcut)
Step 4: Add Advice
aspect.AddAdvice(&aop.Advice{Ordering: aop.Before, Method: "Before", PointcutRefID: "pointcut_1"})
aspect.AddAdvice(&aop.Advice{Ordering: aop.After, Method: "After", PointcutRefID: "pointcut_1"})
aspect.AddAdvice(&aop.Advice{Ordering: aop.Around, Method: "Around", PointcutRefID: "pointcut_1"})
Step 5: Create AOP
gogapAop := aop.NewAOP()
gogapAop.SetBeanFactory(beanFactory)
gogapAop.AddAspect(aspect)
Setp 6: Get Proxy
proxy, err := gogapAop.GetProxy("auth")
Last Step: Enjoy
login := proxy.Method(new(Auth).Login).(func(string, string) bool)("zeal", "gogap")

fmt.Println("login result:", login)

output

$> go run main.go
Before Login: zeal
After Login: zeal gogap
Login result: true
Advance
Pointcut expression

every condition expression is regex expression

pointcut := aop.NewPointcut("pointcut_1")

// will trigger the advice while call login
pointcut.Execution(`Login()`)

// will trigger the advice will call any func
pointcut.Execution(`.*?`)

// will not trigger the advice will call any func
pointcut.NotExecution(`Login()`)
other conditions:
  • WithIn
  • NotWithIn
  • Bean
  • NotBean
// will trigger the advie while we call Login 
// and in bean named auth
pointcut.Execution(`Login()`).Bean(`auth`)

// will trigger the advie while we call Login 
// and in bean named auth and sysAuth
pointcut.Execution(`Login()`).Bean(`auth`).Bean(`sysAuth`)


// will trigger the advie while we call Login 
// and in bean named auth not sysAuth
pointcut.Execution(`Login()`).Bean(`auth`).NotBean(`sysAuth`)

// will trigger the advie while we call Login 
// and the call stacktrace should contain example/aop/main
pointcut.Execution(`Login()`).WithIn(`example/aop/main`)

Do not want to assertion func type
proxy.Invoke(new(Auth).Login, "zeal", "errorpassword").End(
		func(result bool) {
			login = result
		})
Weaving other beans into aspect
define a bean
type Foo struct {
}

// @AfterReturning, the method could have args of aop.Result,
// it will get the result from real func return values
func (p *Foo) Bar(result aop.Result) {
	result.MapTo(func(v bool) {
		fmt.Println("Bar Bar Bar .... Result is:", v)
	})
}
register bean
beanFactory.RegisterBean("foo", new(Foo))
create aspect
aspectFoo := aop.NewAspect("aspect_2", "foo")
aspectFoo.SetBeanFactory(beanFactory)
add advice
aspectFoo.AddAdvice(&aop.Advice{Ordering: aop.AfterReturning, Method: "Bar", PointcutRefID: "pointcut_1"})
add aspect into aop
gogapAop.AddAspect(aspectFoo)

result

Before Login: zeal
Bar Bar Bar .... Result is: true
After Login: zeal gogap
Login result: true
Turn on trace for debug
err := aop.StartTrace()

....
// use proxy to call your funcs

t, err := aop.StopTrace()

for _, item := range t.Items() {
		fmt.Println(item.ID, item.InvokeID, item.BeanRefID, item.Pointcut, item.Method)
}
$> go run main.go
go run main.go
==========Func Type Assertion==========
Before Login: zeal
@Begin Around
@Login fakeName fakePassword
@Proceed Result is false
@End Around
After Login: zeal gogap
Login result: false
================Invoke=================
Before Login: zeal
@Begin Around
@Login fakeName fakePassword
@Proceed Result is false
@End Around
After Login: zeal errorpassword
Login result: false
1 aqpk3jjhssa5ul6pt0h0 auth main.(Auth).Login Before
2 aqpk3jjhssa5ul6pt0h0 auth main.(Auth).Login Around
3 aqpk3jjhssa5ul6pt0h0 auth main.(Auth).Login *Login
4 aqpk3jjhssa5ul6pt0h0 foo main.(Auth).Login Bar
5 aqpk3jjhssa5ul6pt0h0 auth main.(Auth).Login After
6 aqpk3jjhssa5ul6pt0hg auth main.(Auth).Login Before
7 aqpk3jjhssa5ul6pt0hg auth main.(Auth).Login Around
8 aqpk3jjhssa5ul6pt0hg auth main.(Auth).Login *Login
9 aqpk3jjhssa5ul6pt0hg foo main.(Auth).Login Bar
10 aqpk3jjhssa5ul6pt0hg auth main.(Auth).Login After

the * means the real func in this call

Expand ▾ Collapse ▴

Documentation

Index

Constants

View Source
const (
	AOPErrorNamespace = "AOP"
)

Variables

View Source
var (
	ErrBeanInstanceIsNil         = errors.TN(AOPErrorNamespace, 1, "aop error namespace is nil, id: {{.id}}")
	ErrBeanIsNotAnPtr            = errors.TN(AOPErrorNamespace, 2, "bean should be an ptr, id: {{.id}}")
	ErrBeanAlreadyRegistered     = errors.TN(AOPErrorNamespace, 3, "bean already regitered, id: {{.id}}")
	ErrBeanIDShouldNotBeEmpty    = errors.TN(AOPErrorNamespace, 4, "bean id should not be empty")
	ErrBeanNotExist              = errors.TN(AOPErrorNamespace, 5, "bean not exist, id: {{.id}}")
	ErrAspectIDShouldNotBeEmpty  = errors.TN(AOPErrorNamespace, 6, "aspect id should not be empty")
	ErrBeanMethodNotExit         = errors.TN(AOPErrorNamespace, 7, "bean method not exist, id: {{.id}}, class: {{.class}}, method: {{.method}}")
	ErrWrongAdviceFuncArgsNum    = errors.TN(AOPErrorNamespace, 8, "wrong advice func args number, id: {{.id}}, class: {{.class}}, method: {{.method}}")
	ErrEndInvokeParamsIsNotFunc  = errors.TN(AOPErrorNamespace, 9, "en invoke params is not func, bean id: {{.id}}, method: {{.method}}")
	ErrWrongEndInvokeFuncArgsNum = errors.TN(AOPErrorNamespace, 10, "wrong end invoke func args number, bean id: {{.id}}, method: {{.method}}")
	ErrInvokeParamsIsNotFunc     = errors.TN(AOPErrorNamespace, 11, "invoke params is not func, bean id: {{.id}}, method: {{.method}}")
	ErrWrongInvokeFuncArgsNum    = errors.TN(AOPErrorNamespace, 12, "wrong invoke func args number, bean id: {{.id}}, method: {{.method}}")
	ErrInvokeFuncNotExist        = errors.TN(AOPErrorNamespace, 13, "invoke func not exist, bean id: {{.id}}, method: {{.method}}")
	ErrInvokeFuncTypeError       = errors.TN(AOPErrorNamespace, 14, "invoke func is not func type, bean id: {{.id}}, method: {{.method}}")
	ErrEndInvokeTwice            = errors.TN(AOPErrorNamespace, 15, "end invoke twice, bean id: {{.id}}, method: {{.method}}")
	ErrBadInvokeMethodType       = errors.TN(AOPErrorNamespace, 16, "invoke method params should be func name or func type")
	ErrPointcutNotExist          = errors.TN(AOPErrorNamespace, 17, "pointcut no exist, pointcut id: {{.id}}")
	ErrMethodIsNil               = errors.TN(AOPErrorNamespace, 18, "method is nil")
	ErrBadMethodType             = errors.TN(AOPErrorNamespace, 19, "method type error")
	ErrBadPointcutExpr           = errors.TN(AOPErrorNamespace, 20, "bad pointcut expression, expression: {{.expr}}")
	ErrEmptyPointcutRefID        = errors.TN(AOPErrorNamespace, 21, "pointcut ref id is empty")
	ErrExecExpressionAlreadySet  = errors.TN(AOPErrorNamespace, 22, "execution expression already set at pointcut {{.id}}, old expr is: {{.expr}}")
	ErrTracAlreadyStarted        = errors.TN(AOPErrorNamespace, 23, "trace already started")
	ErrTracNotStarted            = errors.TN(AOPErrorNamespace, 24, "trace not started")
	ErrUnknownJoinPointArgType   = errors.TN(AOPErrorNamespace, 25, "unknown advice func arg type, bean id: {{.id}}, method: {{.method}}, pointcut {{.refID}}")
	ErrJoinPointArgsUsage        = errors.TN(AOPErrorNamespace, 26, "only AfterReturning could have aop.Result argument, bean id: {{.id}}, method: {{.method}}, pointcut {{.refID}}")
	ErrMapperArgShouldBeFunc     = errors.TN(AOPErrorNamespace, 27, "result map to arg should be a func")
	ErrWrongMapFuncArgsNum       = errors.TN(AOPErrorNamespace, 28, "wrong end invoke func args number")
)

Functions

func IsTracing

func IsTracing() bool

func StartTrace

func StartTrace() (err error)

Types

type AOP

type AOP struct {
	// contains filtered or unexported fields
}

func NewAOP

func NewAOP() *AOP

func (*AOP) AddAspect

func (p *AOP) AddAspect(aspect *Aspect) *AOP

func (*AOP) GetProxy

func (p *AOP) GetProxy(beanID string) (proxy *Proxy, err error)

func (*AOP) SetBeanFactory

func (p *AOP) SetBeanFactory(factory BeanFactory)

type Advice

type Advice struct {
	Ordering AdviceOrdering
	Method   string

	Pointcut      string
	PointcutRefID string
	// contains filtered or unexported fields
}

type AdviceOrdering

type AdviceOrdering int
const (
	Before         AdviceOrdering = 1
	After          AdviceOrdering = 2
	AfterReturning AdviceOrdering = 3
	AfterError     AdviceOrdering = 4
	AfterPanic     AdviceOrdering = 5
	Around         AdviceOrdering = 6
)

type Args

type Args []interface{}

func (Args) MapTo

func (p Args) MapTo(fn interface{})

type Aspect

type Aspect struct {
	// contains filtered or unexported fields
}

func NewAspect

func NewAspect(id, beanRefID string) *Aspect

func (*Aspect) AddAdvice

func (p *Aspect) AddAdvice(advice *Advice) *Aspect

func (*Aspect) AddPointcut

func (p *Aspect) AddPointcut(pointcut *Pointcut) *Aspect

func (*Aspect) BeanRefID

func (p *Aspect) BeanRefID() string

func (*Aspect) GetMatchedAdvices

func (p *Aspect) GetMatchedAdvices(bean *Bean, methodName string, args Args) (advices map[AdviceOrdering][]*Advice, err error)

func (*Aspect) ID

func (p *Aspect) ID() string

func (*Aspect) SetBeanFactory

func (p *Aspect) SetBeanFactory(factory BeanFactory)

type Bean

type Bean struct {
	// contains filtered or unexported fields
}

func NewBean

func NewBean(id string, instance interface{}) (bean *Bean, err error)

func (*Bean) Call

func (p *Bean) Call(methodName string, args Args) []reflect.Value

func (*Bean) Class

func (p *Bean) Class() string

func (*Bean) ID

func (p *Bean) ID() string

func (*Bean) Invoke

func (p *Bean) Invoke(methodName string, args Args, callback ...interface{}) (returnFunc func(), err error)

type BeanFactory

type BeanFactory interface {
	RegisterBean(id string, value interface{}) BeanFactory
	GetBean(id string) (bean *Bean, err error)
}

func NewClassicBeanFactory

func NewClassicBeanFactory() BeanFactory

type BeanFactoryAware

type BeanFactoryAware interface {
	SetBeanFactory(factory BeanFactory)
}

type ClassicBeanFactory

type ClassicBeanFactory struct {
	// contains filtered or unexported fields
}

func (*ClassicBeanFactory) GetBean

func (p *ClassicBeanFactory) GetBean(id string) (bean *Bean, err error)

func (*ClassicBeanFactory) RegisterBean

func (p *ClassicBeanFactory) RegisterBean(id string, beanInstance interface{}) (factory BeanFactory)

type InvokeResult

type InvokeResult struct {
	// contains filtered or unexported fields
}

func (*InvokeResult) BeanID

func (p *InvokeResult) BeanID() string

func (*InvokeResult) End

func (p *InvokeResult) End(callback ...interface{}) (err error)

func (*InvokeResult) Error

func (p *InvokeResult) Error() error

func (*InvokeResult) MethodName

func (p *InvokeResult) MethodName() string

type JoinPoint

type JoinPoint struct {
	// contains filtered or unexported fields
}

func (*JoinPoint) Args

func (p *JoinPoint) Args() Args

func (*JoinPoint) CallID

func (p *JoinPoint) CallID() string

func (*JoinPoint) Target

func (p *JoinPoint) Target() *Bean

type JoinPointer

type JoinPointer interface {
	Args() Args
	Target() *Bean
	CallID() string
}

type MethodMetadata

type MethodMetadata struct {
	Method reflect.Method
	File   string
	Line   int
}

func (*MethodMetadata) IsEqual

func (p *MethodMetadata) IsEqual(t reflect.Type) bool

type Pointcut

type Pointcut struct {
	// contains filtered or unexported fields
}

func NewPointcut

func NewPointcut(id string) *Pointcut

func (*Pointcut) Bean

func (p *Pointcut) Bean(expr string) *Pointcut

func (*Pointcut) Execution

func (p *Pointcut) Execution(expr string) *Pointcut

func (*Pointcut) ID

func (p *Pointcut) ID() string

func (*Pointcut) IsMatch

func (p *Pointcut) IsMatch(bean *Bean, methodName string, args Args) bool

func (*Pointcut) NotBean

func (p *Pointcut) NotBean(expr string) *Pointcut

func (*Pointcut) NotExecution

func (p *Pointcut) NotExecution(expr string) *Pointcut

func (*Pointcut) NotWithin

func (p *Pointcut) NotWithin(expr string) *Pointcut

func (*Pointcut) Within

func (p *Pointcut) Within(expr string) *Pointcut

type ProceedingJoinPoint

type ProceedingJoinPoint struct {
	JoinPointer
	// contains filtered or unexported fields
}

func (*ProceedingJoinPoint) Args

func (p *ProceedingJoinPoint) Args() Args

func (*ProceedingJoinPoint) Proceed

func (p *ProceedingJoinPoint) Proceed(args ...interface{}) (result Result)

func (*ProceedingJoinPoint) Target

func (p *ProceedingJoinPoint) Target() *Bean

type ProceedingJoinPointer

type ProceedingJoinPointer interface {
	JoinPointer

	Proceed(args ...interface{}) Result
}

type Proxy

type Proxy struct {
	// contains filtered or unexported fields
}

func NewProxy

func NewProxy(beanID string) *Proxy

func (*Proxy) BeanID

func (p *Proxy) BeanID() string

func (*Proxy) Invoke

func (p *Proxy) Invoke(method interface{}, args ...interface{}) (result *InvokeResult)

func (*Proxy) Method

func (p *Proxy) Method(fn interface{}) (method interface{})

type Result

type Result []reflect.Value

func (Result) MapTo

func (p Result) MapTo(fn interface{})

type Trace

type Trace struct {
	// contains filtered or unexported fields
}

func StopTrace

func StopTrace() (t *Trace, err error)

func (*Trace) Items

func (p *Trace) Items() []TraceItem

type TraceItem

type TraceItem struct {
	ID        int
	InvokeID  string
	File      string
	Line      int
	Method    string
	Pointcut  string
	BeanRefID string
	Timestamp string
}

Directories

Path Synopsis