Skip to content

Commit 76ec8ad

Browse files
qile222ximin.cxm
andauthored
feat: implement advice params
--------- Co-authored-by: ximin.cxm <[email protected]>
1 parent 2c683c3 commit 76ec8ad

File tree

11 files changed

+146
-48
lines changed

11 files changed

+146
-48
lines changed

core/aop-decorator/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export * from './src/decorator/Advice';
22
export * from './src/decorator/Pointcut';
3-
export * from './src/decorator/Cosscut';
3+
export * from './src/decorator/Crosscut';
44
export * from './src/model/Aspect';
55
export * from './src/model/PointcutInfo';
66
export * from './src/util/AdviceInfoUtil';

core/aop-decorator/src/decorator/Advice.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ import {
88
import { AdviceInfoUtil } from '../util/AdviceInfoUtil';
99
import { StackUtil } from '@eggjs/tegg-common-util';
1010

11-
export interface AdviceContext<T = object> {
11+
export interface AdviceContext<T = object, K = any> {
1212
that: T;
1313
method: PropertyKey;
1414
args: any[];
15+
adviceParams?: K;
1516
}
1617

1718
/**
@@ -21,33 +22,33 @@ export interface AdviceContext<T = object> {
2122
* 1. afterReturn/afterThrow
2223
* 1. afterFinally
2324
*/
24-
export interface IAdvice<T = object> {
25+
export interface IAdvice<T = object, K = any> {
2526
/**
2627
* call before function
2728
* @param ctx
2829
*/
29-
beforeCall?(ctx: AdviceContext<T>): Promise<void>;
30+
beforeCall?(ctx: AdviceContext<T, K>): Promise<void>;
3031

3132
/**
3233
* call after function succeed
3334
*/
34-
afterReturn?(ctx: AdviceContext<T>, result: any): Promise<void>;
35+
afterReturn?(ctx: AdviceContext<T, K>, result: any): Promise<void>;
3536

3637
/**
3738
* call after function throw error
3839
*/
39-
afterThrow?(ctx: AdviceContext<T>, error: Error): Promise<void>;
40+
afterThrow?(ctx: AdviceContext<T, K>, error: Error): Promise<void>;
4041

4142
/**
4243
* always call after function done
4344
*/
44-
afterFinally?(ctx: AdviceContext<T>): Promise<void>;
45+
afterFinally?(ctx: AdviceContext<T, K>): Promise<void>;
4546

4647
/**
4748
* execute the function
4849
* the only one can modify the function return value
4950
*/
50-
around?(ctx: AdviceContext<T>, next: () => Promise<any>): Promise<any>;
51+
around?(ctx: AdviceContext<T, K>, next: () => Promise<any>): Promise<any>;
5152
}
5253

5354
const defaultAdviceParam = {

core/aop-decorator/src/decorator/Cosscut.ts renamed to core/aop-decorator/src/decorator/Crosscut.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
export interface CrosscutOptions {
1313
// 默认值 100
1414
order?: number;
15+
adviceParams?: any;
1516
}
1617

1718
const defaultCrossOptions = {
@@ -48,6 +49,7 @@ export function Crosscut(param: CrosscutParam, options?: CrosscutOptions) {
4849
adviceInfo: {
4950
clazz: constructor,
5051
order: options?.order ?? defaultCrossOptions.order,
52+
adviceParams: options?.adviceParams,
5153
},
5254
};
5355
} else if (param.type === PointcutType.NAME) {
@@ -56,6 +58,7 @@ export function Crosscut(param: CrosscutParam, options?: CrosscutOptions) {
5658
adviceInfo: {
5759
clazz: constructor,
5860
order: options?.order ?? defaultCrossOptions.order,
61+
adviceParams: options?.adviceParams,
5962
},
6063
};
6164
} else {
@@ -64,6 +67,7 @@ export function Crosscut(param: CrosscutParam, options?: CrosscutOptions) {
6467
adviceInfo: {
6568
clazz: constructor,
6669
order: options?.order ?? defaultCrossOptions.order,
70+
adviceParams: options?.adviceParams,
6771
},
6872
};
6973
}

core/aop-decorator/src/decorator/Pointcut.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { AdviceInfoUtil } from '../util/AdviceInfoUtil';
77
export interface PointcutOptions {
88
// default is 1000
99
order?: number;
10+
adviceParams?: any;
1011
}
1112

1213
const defaultPointcutOptions = {
@@ -21,6 +22,7 @@ export function Pointcut(adviceClazz: EggProtoImplClass<IAdvice>, options?: Poin
2122
PointcutAdviceInfoUtil.addPointcutAdviceInfo({
2223
clazz: adviceClazz,
2324
order: options?.order ?? defaultPointcutOptions.order,
25+
adviceParams: options?.adviceParams,
2426
}, targetClazz, methodName);
2527
};
2628
}

core/aop-decorator/src/model/Aspect.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import { IAdvice } from '../decorator/Advice';
44
export interface AdviceInfo {
55
clazz: EggProtoImplClass<IAdvice>;
66
order: number;
7+
adviceParams: any;
78
}
89

910
export interface AspectAdvice {
1011
name: string;
1112
clazz: EggProtoImplClass<IAdvice>;
13+
adviceParams: any;
1214
}
1315

1416
export class Aspect {
@@ -44,6 +46,7 @@ export class AspectBuilder {
4446
return {
4547
clazz: t.clazz,
4648
name: this.adviceName(t.clazz, i),
49+
adviceParams: t.adviceParams,
4750
};
4851
});
4952
return new Aspect(this.clazz, this.method, aspectAdviceList);

core/aop-decorator/test/AspectMetaBuilder.test.ts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ describe('test/AspectMetaBuild.test.ts', () => {
4545
const advices = aspect.adviceList;
4646

4747
assert.deepStrictEqual(advices, [
48-
{ name: 'PointcutExample#hello#PointcutAdviceBeforeCallExample#0', clazz: PointcutAdviceBeforeCallExample },
49-
{ name: 'PointcutExample#hello#PointcutAdviceAfterReturnExample#1', clazz: PointcutAdviceAfterReturnExample },
48+
{ name: 'PointcutExample#hello#PointcutAdviceBeforeCallExample#0', clazz: PointcutAdviceBeforeCallExample, adviceParams: undefined },
49+
{ name: 'PointcutExample#hello#PointcutAdviceAfterReturnExample#1', clazz: PointcutAdviceAfterReturnExample, adviceParams: undefined },
5050
]);
5151
});
5252
});
@@ -63,9 +63,9 @@ describe('test/AspectMetaBuild.test.ts', () => {
6363
assert(aspect.method === 'hello');
6464
const advices = aspect.adviceList;
6565
assert.deepStrictEqual(advices, [
66-
{ name: 'CrosscutExample#hello#CrosscutClassAdviceExample#0', clazz: CrosscutClassAdviceExample },
67-
{ name: 'CrosscutExample#hello#CrosscutNameAdviceExample#1', clazz: CrosscutNameAdviceExample },
68-
{ name: 'CrosscutExample#hello#CrosscutCustomAdviceExample#2', clazz: CrosscutCustomAdviceExample },
66+
{ name: 'CrosscutExample#hello#CrosscutClassAdviceExample#0', clazz: CrosscutClassAdviceExample, adviceParams: undefined },
67+
{ name: 'CrosscutExample#hello#CrosscutNameAdviceExample#1', clazz: CrosscutNameAdviceExample, adviceParams: undefined },
68+
{ name: 'CrosscutExample#hello#CrosscutCustomAdviceExample#2', clazz: CrosscutCustomAdviceExample, adviceParams: undefined },
6969
]);
7070
});
7171
});
@@ -83,20 +83,20 @@ describe('test/AspectMetaBuild.test.ts', () => {
8383
const overwriteAdvices = overwriteAspect.adviceList;
8484

8585
assert.deepStrictEqual(overwriteAdvices, [
86-
{ name: 'ChildExample#overwriteMethod#CrosscutOverwriteParentExample#0', clazz: CrosscutOverwriteParentExample },
87-
{ name: 'ChildExample#overwriteMethod#CrosscutOverwriteChildExample#1', clazz: CrosscutOverwriteChildExample },
86+
{ name: 'ChildExample#overwriteMethod#CrosscutOverwriteParentExample#0', clazz: CrosscutOverwriteParentExample, adviceParams: undefined },
87+
{ name: 'ChildExample#overwriteMethod#CrosscutOverwriteChildExample#1', clazz: CrosscutOverwriteChildExample, adviceParams: undefined },
8888
// FIXME: parent/child should has correct order
89-
{ name: 'ChildExample#overwriteMethod#PointcutAdviceOverwriteChildExample#2', clazz: PointcutAdviceOverwriteChildExample },
90-
{ name: 'ChildExample#overwriteMethod#PointcutAdviceOverwriteParentExample#3', clazz: PointcutAdviceOverwriteParentExample },
89+
{ name: 'ChildExample#overwriteMethod#PointcutAdviceOverwriteChildExample#2', clazz: PointcutAdviceOverwriteChildExample, adviceParams: undefined },
90+
{ name: 'ChildExample#overwriteMethod#PointcutAdviceOverwriteParentExample#3', clazz: PointcutAdviceOverwriteParentExample, adviceParams: undefined },
9191
]);
9292

9393
const noOverwriteAspect = aspects.find(t => t.method === 'noOverwriteMethod');
9494
assert(noOverwriteAspect);
9595
assert(noOverwriteAspect.clazz === ChildExample);
9696
const noOverwriteAdvices = noOverwriteAspect.adviceList;
9797
assert.deepStrictEqual(noOverwriteAdvices, [
98-
{ name: 'ChildExample#noOverwriteMethod#CrosscutNoOverwriteParentExample#0', clazz: CrosscutNoOverwriteParentExample },
99-
{ name: 'ChildExample#noOverwriteMethod#PointcutAdviceNoOverwriteParentExample#1', clazz: PointcutAdviceNoOverwriteParentExample },
98+
{ name: 'ChildExample#noOverwriteMethod#CrosscutNoOverwriteParentExample#0', clazz: CrosscutNoOverwriteParentExample, adviceParams: undefined },
99+
{ name: 'ChildExample#noOverwriteMethod#PointcutAdviceNoOverwriteParentExample#1', clazz: PointcutAdviceNoOverwriteParentExample, adviceParams: undefined },
100100
]);
101101
});
102102

@@ -112,17 +112,17 @@ describe('test/AspectMetaBuild.test.ts', () => {
112112
assert(overwriteAspect.clazz === ParentExample);
113113
const overwriteAdvices = overwriteAspect.adviceList;
114114
assert.deepStrictEqual(overwriteAdvices, [
115-
{ name: 'ParentExample#overwriteMethod#CrosscutOverwriteParentExample#0', clazz: CrosscutOverwriteParentExample },
116-
{ name: 'ParentExample#overwriteMethod#PointcutAdviceOverwriteParentExample#1', clazz: PointcutAdviceOverwriteParentExample },
115+
{ name: 'ParentExample#overwriteMethod#CrosscutOverwriteParentExample#0', clazz: CrosscutOverwriteParentExample, adviceParams: undefined },
116+
{ name: 'ParentExample#overwriteMethod#PointcutAdviceOverwriteParentExample#1', clazz: PointcutAdviceOverwriteParentExample, adviceParams: undefined },
117117
]);
118118

119119
const noOverwriteAspect = aspects.find(t => t.method === 'noOverwriteMethod');
120120
assert(noOverwriteAspect);
121121
assert(noOverwriteAspect.clazz === ParentExample);
122122
const noOverwriteAdvices = noOverwriteAspect.adviceList;
123123
assert.deepStrictEqual(noOverwriteAdvices, [
124-
{ name: 'ParentExample#noOverwriteMethod#CrosscutNoOverwriteParentExample#0', clazz: CrosscutNoOverwriteParentExample },
125-
{ name: 'ParentExample#noOverwriteMethod#PointcutAdviceNoOverwriteParentExample#1', clazz: PointcutAdviceNoOverwriteParentExample },
124+
{ name: 'ParentExample#noOverwriteMethod#CrosscutNoOverwriteParentExample#0', clazz: CrosscutNoOverwriteParentExample, adviceParams: undefined },
125+
{ name: 'ParentExample#noOverwriteMethod#PointcutAdviceNoOverwriteParentExample#1', clazz: PointcutAdviceNoOverwriteParentExample, adviceParams: undefined },
126126
]);
127127
});
128128
});

core/aop-decorator/test/fixtures/InheritExample.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ContextProto } from '@eggjs/core-decorator';
22
import { Advice, AdviceContext, IAdvice } from '../../src/decorator/Advice';
33
import { Pointcut } from '../../src/decorator/Pointcut';
4-
import { Crosscut } from '../../src/decorator/Cosscut';
4+
import { Crosscut } from '../../src/decorator/Crosscut';
55
import { PointcutType } from '../../src/model/PointcutInfo';
66

77
@Advice()

core/aop-runtime/src/AspectExecutor.ts

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ import { AdviceContext, AspectAdvice, IAdvice } from '@eggjs/aop-decorator';
22
import compose from 'koa-compose';
33
import type { Middleware } from 'koa-compose';
44

5+
interface InternalAdviceContext<T = object> {
6+
that: T;
7+
method: PropertyKey;
8+
args: any[];
9+
}
10+
511
export class AspectExecutor {
612
obj: Object;
713
method: PropertyKey;
@@ -14,7 +20,7 @@ export class AspectExecutor {
1420
}
1521

1622
async execute(...args: any[]) {
17-
const ctx: AdviceContext = {
23+
const ctx: InternalAdviceContext = {
1824
that: this.obj,
1925
method: this.method,
2026
args,
@@ -32,53 +38,59 @@ export class AspectExecutor {
3238
}
3339
}
3440

35-
async beforeCall(ctx: AdviceContext) {
41+
async beforeCall(ctx: InternalAdviceContext) {
3642
for (const aspectAdvice of this.aspectAdviceList) {
3743
const advice: IAdvice = ctx.that[aspectAdvice.name];
3844
if (advice.beforeCall) {
39-
await advice.beforeCall(ctx);
45+
await advice.beforeCall({ ...ctx, adviceParams: aspectAdvice.adviceParams });
4046
}
4147
}
4248
}
4349

44-
async afterReturn(ctx: AdviceContext, result: any) {
50+
async afterReturn(ctx: InternalAdviceContext, result: any) {
4551
for (const aspectAdvice of this.aspectAdviceList) {
4652
const advice: IAdvice = ctx.that[aspectAdvice.name];
4753
if (advice.afterReturn) {
48-
await advice.afterReturn(ctx, result);
54+
await advice.afterReturn({ ...ctx, adviceParams: aspectAdvice.adviceParams }, result);
4955
}
5056
}
5157
}
5258

53-
async afterThrow(ctx: AdviceContext, error: Error) {
59+
async afterThrow(ctx: InternalAdviceContext, error: Error) {
5460
for (const aspectAdvice of this.aspectAdviceList) {
5561
const advice: IAdvice = ctx.that[aspectAdvice.name];
5662
if (advice.afterThrow) {
57-
await advice.afterThrow(ctx, error);
63+
await advice.afterThrow({ ...ctx, adviceParams: aspectAdvice.adviceParams }, error);
5864
}
5965
}
6066
}
6167

62-
async afterFinally(ctx: AdviceContext) {
68+
async afterFinally(ctx: InternalAdviceContext) {
6369
for (const aspectAdvice of this.aspectAdviceList) {
6470
const advice: IAdvice = ctx.that[aspectAdvice.name];
6571
if (advice.afterFinally) {
66-
await advice.afterFinally(ctx);
72+
await advice.afterFinally({ ...ctx, adviceParams: aspectAdvice.adviceParams });
6773
}
6874
}
6975
}
7076

71-
async doExecute(ctx: AdviceContext) {
77+
async doExecute(ctx: InternalAdviceContext) {
7278
const lastCall = () => {
7379
const originMethod = Object.getPrototypeOf(this.obj)[this.method];
7480
return Reflect.apply(originMethod, ctx.that, ctx.args);
7581
};
76-
const functions: Array<Middleware<AdviceContext>> = [];
82+
const functions: Array<Middleware<InternalAdviceContext>> = [];
7783
for (const aspectAdvice of this.aspectAdviceList) {
7884
const advice: IAdvice = ctx.that[aspectAdvice.name];
7985
const fn = advice.around;
8086
if (fn) {
81-
functions.push(fn.bind(advice));
87+
functions.push(async (ctx: InternalAdviceContext<object>, next: () => Promise<any>) => {
88+
const fnCtx: AdviceContext = {
89+
...ctx,
90+
adviceParams: aspectAdvice.adviceParams,
91+
};
92+
return await fn.call(advice, fnCtx, next);
93+
});
8294
}
8395
}
8496
functions.push(lastCall);

core/aop-runtime/test/aop-runtime.test.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { EggObjectLifecycleUtil, LoadUnitInstance, LoadUnitInstanceFactory } fro
55
import { EggPrototypeLifecycleUtil, LoadUnitFactory, LoadUnitLifecycleUtil } from '@eggjs/tegg-metadata';
66
import { CrosscutAdviceFactory } from '@eggjs/aop-decorator';
77
import { EggTestContext } from '../../test-util';
8-
import { CallTrace, Hello } from './fixtures/modules/hello_succeed/Hello';
8+
import { CallTrace, Hello, crosscutAdviceParams, pointcutAdviceParams } from './fixtures/modules/hello_succeed/Hello';
99
import { CoreTestHelper } from '../../test-util/CoreTestHelper';
1010
import { EggObjectAopHook } from '../src/EggObjectAopHook';
1111
import { LoadUnitAopHook } from '../src/LoadUnitAopHook';
@@ -50,47 +50,65 @@ describe('test/aop-runtime.test.ts', () => {
5050
const callTrace = await CoreTestHelper.getObject(CallTrace);
5151
const msg = await hello.hello('aop');
5252
const traceMsg = callTrace.msgs;
53-
assert(msg === 'withCrossAroundResult(withPointAroundResult(hello withPointAroundParam(withCrosscutAroundParam(aop))))');
53+
assert.deepStrictEqual(msg, `withCrossAroundResult(withPointAroundResult(hello withPointAroundParam(withCrosscutAroundParam(aop))${JSON.stringify(pointcutAdviceParams)})${JSON.stringify(crosscutAdviceParams)})`);
5454
assert.deepStrictEqual(traceMsg, [
5555
{
5656
className: 'CrosscutAdvice',
5757
methodName: 'beforeCall',
5858
id: 233,
5959
name: 'aop',
60+
adviceParams: crosscutAdviceParams,
6061
},
6162
{
6263
className: 'PointcutAdvice',
6364
methodName: 'beforeCall',
6465
id: 233,
6566
name: 'aop',
67+
adviceParams: pointcutAdviceParams,
6668
},
6769
{
6870
className: 'CrosscutAdvice',
6971
methodName: 'afterReturn',
7072
id: 233,
7173
name: 'withPointAroundParam(withCrosscutAroundParam(aop))',
72-
result: 'withCrossAroundResult(withPointAroundResult(hello withPointAroundParam(withCrosscutAroundParam(aop))))',
74+
result: `withCrossAroundResult(withPointAroundResult(hello withPointAroundParam(withCrosscutAroundParam(aop))${JSON.stringify(pointcutAdviceParams)})${JSON.stringify(crosscutAdviceParams)})`,
75+
adviceParams: crosscutAdviceParams,
7376
},
7477
{
7578
className: 'PointcutAdvice',
7679
methodName: 'afterReturn',
7780
id: 233,
7881
name: 'withPointAroundParam(withCrosscutAroundParam(aop))',
79-
result: 'withCrossAroundResult(withPointAroundResult(hello withPointAroundParam(withCrosscutAroundParam(aop))))',
82+
result: `withCrossAroundResult(withPointAroundResult(hello withPointAroundParam(withCrosscutAroundParam(aop))${JSON.stringify(pointcutAdviceParams)})${JSON.stringify(crosscutAdviceParams)})`,
83+
adviceParams: pointcutAdviceParams,
8084
},
8185
{
8286
className: 'CrosscutAdvice',
8387
methodName: 'afterFinally',
8488
id: 233,
8589
name: 'withPointAroundParam(withCrosscutAroundParam(aop))',
90+
adviceParams: crosscutAdviceParams,
8691
},
8792
{
8893
className: 'PointcutAdvice',
8994
methodName: 'afterFinally',
9095
id: 233,
9196
name: 'withPointAroundParam(withCrosscutAroundParam(aop))',
97+
adviceParams: pointcutAdviceParams,
9298
},
9399
]);
100+
101+
await assert.rejects(async () => {
102+
await hello.helloWithException('foo');
103+
}, new Error('ops, exception for withPointAroundParam(foo)'));
104+
assert.deepStrictEqual(callTrace.msgs[callTrace.msgs.length - 2], {
105+
className: 'PointcutAdvice',
106+
methodName: 'afterThrow',
107+
id: 233,
108+
name: 'withPointAroundParam(foo)',
109+
result: 'ops, exception for withPointAroundParam(foo)',
110+
adviceParams: pointcutAdviceParams,
111+
});
94112
});
95113
});
96114

0 commit comments

Comments
 (0)