绑定完请刷新页面
取消
刷新

分享好友

×
取消 复制
AOP框架Dora.Interception 3.0 「2」: 实现原理
2019-11-05 09:52:22

和所有的AOP框架一样,我们必须将正常的方法调用进行拦截,才能将应用到当前方法上的所有拦截器纳入当前调用链。Dora.Interception采用IL Eimit的方式实现对方法调用的拦截,接下来我们就来聊聊大致的实现原理。

一、与依赖注入框架的无缝集成

由于Dora.Interception是为.NET Core定制的AOP框架,而依赖注入是.NET Core基本的编程方式,所以Dora.Interception初就是作为一个依赖注入框架的扩展而涉及的。我们知道.NET Core的依赖注入框架支持三种服务实例提供方式。由于Dora.Interception终会利用IL Emit的方式动态生成目标实例的类型,所以它只适合种服务注册方式。

如果注册的是一个服务类型,终会选择一个匹配的构造函数来创建服务实例;

如果注册的是一个服务实例创建工厂,那么目标服务实例就由该工厂来创建;

如果注册的是一个服务实例,那么它会直接作为目标服务实例。

二、两种拦截方式

.NET Core的依赖注入框架采用ServiceDescriptor对象来描述服务注册。拦截器终会注册到ImplementationType 属性表示的实现类型上,所以Dora.Interception需要根据该类型生成一个可以被拦截的代理类型。针对ServiceType属性表示的服务类型的不同,我们会采用不同的代码生成方式。

针对接口

如果注册服务时提供的是一个接口和它的实现类型,我们会按照如下的方式来生成可被拦截的代理类型。假设接口和实现类型分别为IFoobar和Foobar,那么我们会生成一个同样实现IFoobar接口的FoobarProxy类型。FoobarProxy对象是对Foobar对象的封装,对于它实现的方法来说,如果没有拦截器应用到Foobar类型对应的方法上,它只需要调用封装的这个Foobar对象对应的方法就可以了。反之,针对拦截器的调用将会注入到FoobarProxy实现的方法中。

针对类型

如果注册是提供的服务类型并不是一个接口,而是一个类型,比如服务类型和实现类型都是Foobar,上述的代码生成机制就不适用了。此时我们要求Foobar必须是一个非封闭(Sealed)的类型,而且拦截器只能应用到它的虚方法上。基于这种假设,我们生成的代理类型FoobarProxy实际上市Foobar的子类,如果拦截器应用到Foobar的某个虚方法上,FoobarProxy只需要重写这个方法将应用的拦截器注入到方法调用管道中。

三、ICodeGenerator & ICodeGeneratorFactory

上述针对IL Emit的动态代理类型生成体现在如下这个ICodeGenerator接口上,该接口的方法GenerateInterceptableProxyClass会根据提供的上下文信息生成可被拦截的代理类型。作为代码生成上下文的的CodeGenerationContext对象来说,它除了提供服务注册的类型和实现类型之外,它还提供了IInterceptorRegistry对象。

public interface ICodeGenerator

{

Type GenerateInterceptableProxyClass(CodeGenerationContext context);

}

public class CodeGenerationContext

{

public Type InterfaceOrBaseType { get; }

public Type TargetType { get; }

public IInterceptorRegistry Interceptors { get; }

public CodeGenerationContext(Type baseType, IInterceptorRegistry interceptors );

public CodeGenerationContext(Type @interface, Type targetType, IInterceptorRegistry interceptors);

}

IInterceptorRegistry接口在Dora.Interception中表示某个类型针对拦截器的注册。它的IsEmpty表示拦截器是否应用到目标类型的任意成员中;IsInterceptable方法帮助我们确定指定的方法是否应用了拦截器;应用到某个方法的所有拦截器可以通过GetInterceptor方法提取出来。

public interface IInterceptorRegistry

{

bool IsEmpty { get; }

InterceptorDelegate GetInterceptor(MethodInfo methodInfo);

bool IsInterceptable(MethodInfo methodInfo);

MethodInfo GetTargetMethod(MethodInfo methodInfo);

}

如果我们需要得到针对某个类型的IInterceptorRegistry对象,可以调用IInterceptorResolver接口的如下两个GetInterceptors方法重载。

public interface IInterceptorResolver

{

IInterceptorRegistry GetInterceptors(Type initerfaceType, Type targetType);

IInterceptorRegistry GetInterceptors(Type targetType);

}

与代码生成相关的还具有如下这个ICodeGeneratorFactory接口,它是创建ICodeGenerator的工厂。

四、ServiceDescriptor的转换

由于服务实例终是通过依赖注入框架提供的,而终得到怎样的服务实例则由初的服务注册决定。为了让依赖注入框架能够提供一个可被拦截的代理对象,而不是原始的目标对象,我们必须改变初始的服务注册,为此我们定义了如下这个InterceptableServiceDescriptor。如下面的代码片段所示,InterceptableServiceDescriptor实际是一个基于工厂的ServiceDescriptor,创建代理对象的逻辑体现在GetImplementationFactory方法返回Func对象上。

public sealed class InterceptableServiceDescriptor : ServiceDescriptor, IInterceptableServiceDescriptor

{

private readonly Type _targetType;

: base(serviceType, GetImplementationFactory(serviceType, implementationType), lifetime)

{

if (serviceType.IsGenericTypeDefinition)

{

throw new ArgumentException("Open generic type (generic type definition) is not support", nameof(serviceType));

}

_targetType = implementationType;

}

Type IInterceptableServiceDescriptor.TargetType => _targetType;

private static Func GetImplementationFactory(Type serviceType, Type implementationType)

{

return serviceProvider =>

{

var interceptorResolver = serviceProvider.GetRequiredService();

var codeGeneratorFactory = serviceProvider.GetRequiredService();

var factoryCache = serviceProvider.GetRequiredService();

if (serviceType.IsInterface)

{

var interceptors = interceptorResolver.GetInterceptors(serviceType, implementationType);

if (interceptors.IsEmpty)

{

return ActivatorUtilities.CreateInstance(serviceProvider, implementationType);

}

else

{

var target = ActivatorUtilities.CreateInstance(serviceProvider, implementationType);

return factoryCache.GetInstanceFactory(serviceType, implementationType).Invoke(target);

}

}

else

{

var interceptors = interceptorResolver.GetInterceptors(implementationType);

if (interceptors.IsEmpty)

{

return ActivatorUtilities.CreateInstance(serviceProvider, implementationType);

}

else

{

return factoryCache.GetTypeFactory(implementationType).Invoke(serviceProvider);

}

}

};

}

}

我们可以利用提供的如下的扩展方法直接创建InterceptableServiceDescriptor 对象作为服务注册。

public static class AddInterceptionExtensions

{

public static IServiceCollection AddInterceptable(this IServiceCollection services, Type serviceType, Type implementationType, ServiceLifetime lifetime);

public static IServiceCollection AddTransientInterceptable(this IServiceCollection services, Type serviceType, Type implementationType);

public static IServiceCollection AddScopedInterceptable(this IServiceCollection services, Type serviceType, Type implementationType);

public static IServiceCollection AddSingletonInterceptable(this IServiceCollection services, Type serviceType, Type implementationType);

public static IServiceCollection AddInterceptable(this IServiceCollection services, ServiceLifetime lifetime);

public static IServiceCollection AddTransientInterceptable(this IServiceCollection services);

public static IServiceCollection AddScopedInterceptable(this IServiceCollection services);

public static IServiceCollection AddSingletonInterceptable(this IServiceCollection services);

public static IServiceCollection TryAddInterceptable(this IServiceCollection services, Type serviceType, Type implementationType, ServiceLifetime lifetime);

public static IServiceCollection TryAddTransientInterceptable(this IServiceCollection services, Type serviceType, Type implementationType);

public static IServiceCollection TryAddScopedInterceptable(this IServiceCollection services, Type serviceType, Type implementationType);

public static IServiceCollection TryAddSingletonInterceptable(this IServiceCollection services, Type serviceType, Type implementationType);

public static IServiceCollection TryAddInterceptable(this IServiceCollection services, ServiceLifetime lifetime);

public static IServiceCollection TryAddInterceptable(this IServiceCollection services);

public static IServiceCollection TryAddScopedInterceptable(this IServiceCollection services);

public static IServiceCollection TryAddSingletonInterceptable(this IServiceCollection services);

public static IServiceCollection TryAddEnumerableInterceptable(this IServiceCollection services, Type serviceType, Type implementationType, ServiceLifetime lifetime);

public static IServiceCollection TryAddEnumerableInterceptable(this IServiceCollection services, ServiceLifetime lifetime)

}

五、另一种改变服务注册的方式

如果我们依然希望采用默认提供的服务注册API,那么我们可以将服务注册的转换实现在利用IServiceCollection集合创建IServiceProvider对象的时候,为此我们定义了如下这个BuildInterceptableServiceProvider扩展方法。顺便说一下,另一个AddInterception扩展方法用来注册Dora.Interception框架自身的一些核心服务。BuildInterceptableServiceProvider方法内部会调用这个方法,如果没有采用这种方式来创建IServiceProvider对象,AddInterception扩展方法必须显式调用。

public static class ServiceCollectionExtensions

{

public static IServiceProvider BuildInterceptableServiceProvider(this IServiceCollection services, Action configure = null);

public static IServiceCollection AddInterception(this IServiceCollection services, Action configure = null);

}

六、InterceptableServiceProviderFactory

.NET Core依赖注入框架利用自定义的IServiceProviderFactory<TContainerBuilder>实现与第三方依赖注入框架的整合。如下这个的InterceptableServiceProviderFactory是我们为Dora.Interception定义的实现类型。

public sealed class InterceptableServiceProviderFactory : IServiceProviderFactory

{

public InterceptableServiceProviderFactory(ServiceProviderOptions options, Action configure);

public IServiceCollection CreateBuilder(IServiceCollection services);

public IServiceProvider CreateServiceProvider(IServiceCollection containerBuilder);

}

为了在服务承载应用(含ASP.NET Core应用)更好地使用Dora.Interception,可以调用我们为IHostBuilder定义的UseInterceptableServiceProvider扩展方法,该方法会帮助我们完成针对InterceptableServiceProviderFactory的注册。

public static class HostBuilderExtensions

{

public static IHostBuilder UseInterceptableServiceProvider(this IHostBuilder builder,ServiceProviderOptions options = null,Action configure = null);

}

我们在《AOP框架Dora.Interception 3.0 [1]: 编程体验》提供的演示程序(如下所示)正是调用了这个UseInterceptableServiceProvider方法。

public class Program

{

public static void Main(string[] args)

{

Host.CreateDefaultBuilder()

.UseInterceptableServiceProvider(configure: Configure)

.ConfigureWebHostDefaults(buider => buider.UseStartup())

.Build()

.Run();

static void Configure(InterceptionBuilder interceptionBuilder)

{

interceptionBuilder.AddPolicy(policyBuilder => policyBuilder

.For(order: 1, cache => cache

.To(target => target

.IncludeMethod(clock => clock.GetCurrentTime(default)))));

}

}

}

作者:蒋金楠

出处:https://www.cnblogs.com/artech/p/dora-interception-3-2.html

分享好友

分享这个小栈给你的朋友们,一起进步吧。

应用开发
创建时间:2020-06-17 15:31:04
应用软件开发是指使用程序语言C#、java、 c++、vb等语言编写,主要是用于商业、生活应用的软件的开发。
展开
订阅须知

• 所有用户可根据关注领域订阅专区或所有专区

• 付费订阅:虚拟交易,一经交易不退款;若特殊情况,可3日内客服咨询

• 专区发布评论属默认订阅所评论专区(除付费小栈外)

技术专家

查看更多
  • 栈栈
    专家
戳我,来吐槽~