实现一个基于动态代理的 AOP
实现一个基于动态代理的 aop
intro
上次看基于动态代理的 aop 框架实现,立了一个 flag, 自己写一个简单的 aop 实现示例,今天过来填坑了
目前的实现是基于 emit 来做的,后面有时间再写一个基于 roslyn 来实现的示例
效果演示
演示代码:
切面逻辑定义:
public class tryinvokeaspect : abstractaspect { public override void invoke(methodinvocationcontext methodinvocationcontext, action next) { console.writeline($"begin invoke method {methodinvocationcontext.proxymethod.name} in {gettype().name}..."); try { next(); } catch (exception e) { console.writeline($"invoke {methodinvocationcontext.proxymethod.declaringtype?.fullname}.{methodinvocationcontext.proxymethod.name} exception"); console.writeline(e); } console.writeline($"end invoke method {methodinvocationcontext.proxymethod.name} in {gettype().name}..."); } } public class tryinvoke1aspect : abstractaspect { public override void invoke(methodinvocationcontext methodinvocationcontext, action next) { console.writeline($"begin invoke method {methodinvocationcontext.proxymethod.name} in {gettype().name}..."); try { next(); } catch (exception e) { console.writeline($"invoke {methodinvocationcontext.proxymethod.declaringtype?.fullname}.{methodinvocationcontext.proxymethod.name} exception"); console.writeline(e); } console.writeline($"end invoke method {methodinvocationcontext.proxymethod.name} in {gettype().name}..."); } } public class tryinvoke2aspect : abstractaspect { public override void invoke(methodinvocationcontext methodinvocationcontext, action next) { console.writeline($"begin invoke method {methodinvocationcontext.proxymethod.name} in {gettype().name}..."); try { next(); } catch (exception e) { console.writeline($"invoke {methodinvocationcontext.proxymethod.declaringtype?.fullname}.{methodinvocationcontext.proxymethod.name} exception"); console.writeline(e); } console.writeline($"end invoke method {methodinvocationcontext.proxymethod.name} in {gettype().name}..."); } }
测试服务定义
// 测试接口定义 public interface itestservice { [tryinvokeaspect] void test(); [tryinvokeaspect] [tryinvoke1aspect] [tryinvoke2aspect] void test1(int a, string b); [tryinvokeaspect] string test2(); [tryinvokeaspect] int test3(); } // 测试接口实例定义 public class testservice : itestservice { [tryinvokeaspect] public virtual string testprop { get; set; } public void test() { console.writeline("test invoked"); } public virtual void test1(int a, string b) { console.writeline($"a:{a}, b:{b}"); } [tryinvoke1aspect] public virtual string test2() { return "hello"; } [tryinvokeaspect] public virtual int test3() { return 1; } }
测试代码:
//var testservice = proxygenerator.instance.createinterfaceproxy<itestservice>(); var testservice = proxygenerator.instance.createinterfaceproxy<itestservice, testservice>(); // var testservice = proxygenerator.instance.createclassproxy<testservice>(); // testservice.testprop = "12133"; testservice.test(); console.writeline(); testservice.test1(1, "str"); var a = testservice.test2(); var b = testservice.test3(); console.writeline($"a:{a}, b:{b}"); console.readline();
输出效果:
整体结构
proxygenerator
proxygenerator
代理生成器,用来创建代理对象
public class proxygenerator { public static readonly proxygenerator instance = new proxygenerator(); public object createinterfaceproxy(type interfacetype) { var type = proxyutil.createinterfaceproxy(interfacetype); return activator.createinstance(type); } public object createinterfaceproxy(type interfacetype, type implementationtype) { var type = proxyutil.createinterfaceproxy(interfacetype, implementationtype); return activator.createinstance(type); } public object createclassproxy(type classtype, params type[] interfacetypes) { var type = proxyutil.createclassproxy(classtype, interfacetypes); return activator.createinstance(type); } public object createclassproxy(type classtype, type implementationtype, params type[] interfacetypes) { var type = proxyutil.createclassproxy(implementationtype, interfacetypes); return activator.createinstance(type); } }
为了更方便的使用泛型,定义了几个扩展方法:
public static class extensions { public static tinterface createinterfaceproxy<tinterface>(this proxygenerator proxygenerator) => (tinterface)proxygenerator.createinterfaceproxy(typeof(tinterface)); public static tinterface createinterfaceproxy<tinterface, timplement>(this proxygenerator proxygenerator) where timplement : tinterface => (tinterface)proxygenerator.createinterfaceproxy(typeof(tinterface), typeof(timplement)); public static tclass createclassproxy<tclass>(this proxygenerator proxygenerator) where tclass : class => (tclass)proxygenerator.createclassproxy(typeof(tclass)); public static tclass createclassproxy<tclass, timplement>(this proxygenerator proxygenerator) where timplement : tclass => (tclass)proxygenerator.createclassproxy(typeof(tclass), typeof(timplement)); }
abstractaspect
abstractaspect
切面抽象类,继承了 attribute
,可以继承它来实现自己的切面逻辑
public abstract class abstractaspect : attribute { public abstract void invoke(methodinvocationcontext methodinvocationcontext, action next); }
methodinvocationcontext
methodinvocationcontext
方法执行上下文,包含了执行方法时的原始方法信息以及代理方法信息,方法参数,方法返回值
public class methodinvocationcontext { public methodinfo proxymethod { get; } public methodinfo methodbase { get; } public object proxytarget { get; } public object target { get; } public object[] parameters { get; } public object returnvalue { get; set; } public methodinvocationcontext(methodinfo method, methodinfo methodbase, object proxytarget, object target, object[] parameters) { proxymethod = method; methodbase = methodbase; proxytarget = proxytarget; target = target; parameters = parameters; } }
代理方法逻辑
生成代理的方法在上一节已经介绍,主要就是通过 emit 生成代理类,要写一些 emit 代码, emit 不在今天的讨论范围内,这里不多介绍,生成代理方法的时候,会检查方法上的 attribute
,如果是切面逻辑就注册切面逻辑,最后像 asp.net core 中间件一样组装在一起拼成一个委托。
核心代码如下:
// var invocation = new methodinvocationcontext(method, methodbase, this, parameters); var localaspectinvocation = il.declarelocal(typeof(methodinvocationcontext)); il.emit(opcodes.ldloc, localcurrentmethod); il.emit(opcodes.ldloc, localmethodbase); il.emit(opcodes.ldarg_0); il.emit(opcodes.ldloc, localtarget); il.emit(opcodes.ldloc, localparameters); // 创建一个 methodinvocationcontext 实例 il.new(typeof(methodinvocationcontext).getconstructors()[0]); il.emit(opcodes.stloc, localaspectinvocation); // aspectdelegate.invokeaspectdelegate(invocation); il.emit(opcodes.ldloc, localaspectinvocation); var invokeaspectdelegatemethod = typeof(aspectdelegate).getmethod(nameof(aspectdelegate.invokeaspectdelegate)); // 执行方法以及注册的切面逻辑 il.call(invokeaspectdelegatemethod); il.emit(opcodes.nop); if (method.returntype != typeof(void)) { // 获取方法返回值 il.emit(opcodes.ldloc, localaspectinvocation); var getmethod = typeof(methodinvocationcontext).getproperty("returnvalue").getgetmethod(); il.emitcall(opcodes.callvirt, getmethod, type.emptytypes); if (method.returntype.isvaluetype) { // 如果是值类型,做一下类型转换 il.emitcasttotype(typeof(object), method.returntype); } il.emit(opcodes.stloc, localreturnvalue); il.emit(opcodes.ldloc, localreturnvalue); } il.emit(opcodes.ret);
注册并执行切面逻辑代码实现:
// 缓存方法体执行的委托,包含切面逻辑的执行和方法的调用 private static readonly concurrentdictionary<string, action<methodinvocationcontext>> _aspectdelegates = new concurrentdictionary<string, action<methodinvocationcontext>>(); public static void invokeaspectdelegate(methodinvocationcontext context) { var action = _aspectdelegates.getoradd($"{context.proxymethod.declaringtype}.{context.proxymethod}", m => { // 获取切面逻辑,这里根据切面类型做了一个去重 var aspects = new list<abstractaspect>(8); if (context.methodbase != null) { // 获取类方法上的切面逻辑 foreach (var aspect in context.methodbase.getcustomattributes<abstractaspect>()) { if (!aspects.exists(x => x.gettype() == aspect.gettype())) { aspects.add(aspect); } } } // 获取接口方法上的切面 var methodparametertypes = context.proxymethod.getparameters().select(p => p.gettype()).toarray(); foreach (var implementedinterface in context.proxytarget.gettype().getimplementedinterfaces()) { var method = implementedinterface.getmethod(context.proxymethod.name, methodparametertypes); if (null != method) { foreach (var aspect in method.getcustomattributes<abstractaspect>()) { if (!aspects.exists(x => x.gettype() == aspect.gettype())) { aspects.add(aspect); } } } } // 构建切面逻辑执行管道,类似于 asp.net core 里的请求管道, 以原始方法调用作为中间件的最后一步 var builder = pipelinebuilder.create<methodinvocationcontext>(x => x.invoke()); foreach (var aspect in aspects) { // 注册切面逻辑 builder.use(aspect.invoke); } // 构建方法执行委托 return builder.build(); }); // 执行委托 action.invoke(context); // 检查返回值,防止切面逻辑管道的中断执行导致值类型返回值没有赋值 if (context.proxymethod.returntype != typeof(void)) { if (context.returnvalue == null && context.proxymethod.returntype.isvaluetype) { // 为值类型返回值设置默认值作为返回值 context.returnvalue = activator.createinstance(context.proxymethod.returntype); } } }
more
以上基本可以实现一个 aop 功能,但是从扩展性以及功能上来说都还比较欠缺,基于 attribute
的方式固然可以实现功能,但是太不灵活,如果我要在一个无法修改的接口上的某一个方法做一个切面逻辑,显然只使用 attribute
是做不到的,还是 fluent-api 的方式比较灵活。
想做一层 aop 的抽象,切面逻辑通过 fluent-api 的方式来注册,大概的 api 可能是这样的:
var settings = fluentaspects.for<itestservice>(); setting.propertysetter(x=>x.testprop) .interceptwith<tryinterceptor>() .interceptwith<tryinterceptor1>(); setting.method(x=> x.test2()) .interceptwith<tryinterceptor>() .interceptwith<tryinterceptor1>();
然后基于 aspectcore
和 castle.core
来实现具体的 aop 功能,暂时先想一下,争取尽快的发布一个基本可用的版本,然后之前基于 ef core 的自动审计也可以基于 aop 来实现了,这样就不需要显示继承 auditdbcontext
了~
文章所有源码可以在 github 上获取到,github 地址: https://github.com/weihanli/samplesinpractice/tree/master/aopsample