Dora.Interception提供了兩種攔截器注冊方式,一種是利用標注在目標類型、屬性和方法上的InterceptorAttribute特性,另一種采用基于目標方法或者屬性的調用表達式。通過提供的擴展點,我們可以任何我們希望的攔截器注冊方式。
目錄
一、IInterceptorProvider
二、InterceptorProviderBase
三、實現一種“萬能”的攔截器注冊方式
四、ConditionalInterceptorProvider
一、IInterceptorProvider
攔截器最終需要應用到某個具體的目標方法上,所以攔截器的注冊就是如何建立攔截器與目標方法之間的映射關系,Dora.Interception將這一功能體現在如下所示的IInterceptorProvider接口上。顧名思義,IInterceptorProvider旨在解決為某個類型的某個方法提供攔截器列表的問題,這一個功能體現在GetInterceptors方法上。如下面的代碼片段所示,該方法返回一組Sortable<InvokeDelegate>對象,InvokeDelegate代表攔截器本身,Sortable<InvokeDelegate>對象在此基礎上添加了必要排序元素。
public?interface?IInterceptorProvider
{bool?CanIntercept(Type?targetType,?MethodInfo?method,?out?bool?suppressed);IEnumerable<Sortable<InvokeDelegate>>?GetInterceptors(Type?targetType,?MethodInfo?method);void?Validate(Type?targetType,?Action<MethodInfo>?methodValidator,?Action<PropertyInfo>?propertyValidator)?{}
}public?sealed?class?Sortable<T>
{public?int?Order?{?get;?}public?T?Value?{?get;?set;?}public?Sortable(int?order,?T?value){Order?=?order;Value?=?value;}
}
除了GetInterceptors方法,IInterceptorProvider接口還定義了額外兩個方法,CanIntercept方法用來判斷指定的方式是否需要被攔截,代碼生成器會利用這個方法決定如果生成最終可供攔截的代理類。另一個Validate方法用來驗證針對指定類型的攔截器注冊方式是否合法,即攔截器是否應用到一些根本無法被攔截的方法或者屬性上,具體的檢驗邏輯由方法提供的兩個委托來完成。
二、InterceptorProviderBase
我們自定義的IInterceptorProvider實現類型一般派生于如下這個抽象基類InterceptorProviderBase,后者在接口的基礎上提供了一個IConventionalInterceptorFactory接口類型的InterceptorFactory屬性。顧名思義,IConventionalInterceptorFactory對象幫助我們按照約定定義的攔截器類型或者其實例轉換成標準的攔截器表現形式,即InvokeDelegate委托。
public?abstract?class?InterceptorProviderBase?:?IInterceptorProvider
{public?IConventionalInterceptorFactory?InterceptorFactory?{?get;?}??protected?InterceptorProviderBase(IConventionalInterceptorFactory?interceptorFactory)?;public?abstract?bool?CanIntercept(Type?targetType,?MethodInfo?method,?out?bool?suppressed);public?abstract?IEnumerable<Sortable<InvokeDelegate>>?GetInterceptors(Type?targetType,?MethodInfo?method);
}public?interface?IConventionalInterceptorFactory
{InvokeDelegate?CreateInterceptor(Type?interceptorType,?params?object[]?arguments);InvokeDelegate?CreateInterceptor(object?interceptor);
}
三、實現一種“萬能”的攔截器注冊方式
接下來我們通過自定義的IInterceptorProvider類型實現一種“萬能”的攔截器注冊方式——根據指定的條件表達式將指定的攔截器關聯到目標方法上。在提供具體實現之前,我們先來體驗一下由它達成的編程模型。
public?class?FoobarInterceptor
{public?ValueTask?InvokeAsync(InvocationContext?invocationContext){var?method?=?invocationContext.MethodInfo;Console.WriteLine($"{method.DeclaringType!.Name}.{method.Name}?is?intercepted.");return?invocationContext.ProceedAsync();}
}public?class?Foobar
{public?virtual?void?M()?{?}public?virtual?object??P?{?get;?set;?}}
我們依然以上面這個簡單的攔截器類型FoobarInterceptor為例,現在我們需要將它應用到Foobar類型的M和P屬性的Set方法上,針對FoobarInterceptor的注冊就可以按照如下方式來完成。如代碼片段所示,我們在調用InterceptionBuilder的RegisterInterceptors擴展方法中提供了一個Action<ConditionalInterceptorProviderOptions>委托,并利用它添加了針對FoobarInterceptor與兩個Func<Type, MethodInfo, bool>委托之間的關系,后者用來匹配目標方法(含屬性方法)。
var?foobar=?new?ServiceCollection().AddSingleton<Foobar>().BuildInterceptableServiceProvider(interception?=>?interception.RegisterInterceptors(RegisterInterceptors)).GetRequiredService<Foobar>();foobar.M();
_?=?foobar.P;
foobar.P?=?null;
Console.ReadLine();static?void?RegisterInterceptors(ConditionalInterceptorProviderOptions?options)
{options.For<FoobarInterceptor>().To(1,?(type,?method)?=>?type?==?typeof(Foobar)?&&?method.Name?==?"M").To(1,?(type,?method)??=>?type?==?typeof(Foobar)?&&?method.IsSpecialName?&&?method.Name?==?"set_P");
}
程序運行后會在控制臺輸出如下的結果,可以看出FoobarInterceptor攔截確實只應用到M和P屬性的Set方法上,屬性的Get方法并未被攔截。
四、ConditionalInterceptorProvider
上述這種針對匹配條件的“萬能”注冊方式是通過如下這個ConditionalInterceptorProvider類型實現的。ConditionalInterceptorProviderOptions類型定義了對應的配置選項,其核心就是一組ConditionalInterceptorRegistration對象的集合,而每一個ConditionalInterceptorRegistration對象是一個表示匹配條件的Func<Type, MethodInfo, bool>委托與攔截器工廠的Func<IConventionalInterceptorFactory, Sortable<InvokeDelegate>>委托之間的映射關系,后者利用指定的IConventionalInterceptorFactory來創建一個對應的Sortable<InvokeDelegate>對象。
public?class?ConditionalInterceptorProvider?:?InterceptorProviderBase
{private?readonly?ConditionalInterceptorProviderOptions?_options;public?ConditionalInterceptorProvider(IConventionalInterceptorFactory?interceptorFactory,?IOptions<ConditionalInterceptorProviderOptions>?optionsAccessor)?:?base(interceptorFactory)=>?_options?=?optionsAccessor.Value;public?override?bool?CanIntercept(Type?targetType,?MethodInfo?method,?out?bool?suppressed){suppressed?=?false;return?_options.Registrations.Any(it?=>?it.Condition(targetType,?method));}public?override?IEnumerable<Sortable<InvokeDelegate>>?GetInterceptors(Type?targetType,?MethodInfo?method)=>?_options.Registrations.Where(it?=>?it.Condition(targetType,?method)).Select(it?=>?it.Factory(InterceptorFactory)).ToList();
}public?class?ConditionalInterceptorProviderOptions
{public?IList<ConditionalInterceptorRegistration>?Registrations?{?get;?}?=?new?List<ConditionalInterceptorRegistration>();public?Registry<TInterceptor>?For<TInterceptor>(params?object[]?arguments)=>?new(factory?=>?factory.CreateInterceptor(typeof(TInterceptor),?arguments),?this);
}public?class?Registry<TInterceptor>
{private?readonly?Func<IConventionalInterceptorFactory,?InvokeDelegate>?_factory;private?readonly?ConditionalInterceptorProviderOptions?_options;public?Registry(Func<IConventionalInterceptorFactory,?InvokeDelegate>?factory,?ConditionalInterceptorProviderOptions?options){_factory?=?factory;_options?=?options;}public?Registry<TInterceptor>?To(int?order,?Func<Type,?MethodInfo,?bool>?condition){var?entry?=?new?ConditionalInterceptorRegistration(condition,?factory=>new?Sortable<InvokeDelegate>(order,?_factory(factory)));_options.Registrations.Add(entry);return?this;}
}public?class?ConditionalInterceptorRegistration
{public?Func<Type,?MethodInfo,?bool>?Condition?{?get;?}public?Func<IConventionalInterceptorFactory,?Sortable<InvokeDelegate>>?Factory?{?get;?}public?ConditionalInterceptorRegistration(Func<Type,?MethodInfo,?bool>?condition,?Func<IConventionalInterceptorFactory,?Sortable<InvokeDelegate>>?factory){Condition?=?condition;Factory?=?factory;}
}
這一組映射關系利用ConditionalInterceptorProviderOptions的For<TInterceptor>方法進行添加,該方法返回一個Registry<TInterceptor>對象,后者提供的To方法指定了作為匹配條件的Func<Type, MethodInfo, bool>委托和決定攔截器執行順序的Order值。ConditionalInterceptorProvider利用構造函數注入的IOptions<ConditionalInterceptorProviderOptions>得到這組映射關系,CanIntercept方法利用這組關系的匹配條件確定指定的方法是否應該被攔截,另一個GetInterceptors方法則利用匹配的工廠來創建返回的這組Sortable<InvokeDelegate>對象。