基于Autofac实现完全特性方式依赖注入实例以及结合Castle完成AOP横切关注的类库OpenDeepSpace.Autofacastle

4 篇文章 0 订阅

在之前OpenDeepSpace.Autofac.Autowired[目前在NUGET上的下载量接近4000次,虽然可能还有点少,但是还行吧!]的基础之上,进行了进一步的思考和改变并且做了一个优化这是一个全新的版本,使其使用更加方便并且支持现在你正在使用Autofac作为Ioc容器的项目或AspNetCore项目中自带的容器通过Autofac来替换也可以轻松完成集成,当然一个全新的项目你可以考虑直接使用它作为你项目的IOC和AOP的类库。可以说能支持使用Autofac和Castle的项目中,你就可以尝试集成它使用它。NUGET上搜索OpenDeepSpace.Autofacastle
OpenDeepSpace.Autofacastle:
故名思意我取这个名称的原因是把Autofac和Castle进行集合实现的一个简单通过特性就能实现IOC和AOP操作。
注意对于批量注入和Aop切面拦截 如果你没有指定需要加载和拦截哪些程序集 Autofacastle会自动读取当前项目所有程序集并排除以Microsoft和System开头的程序集

github源码地址, OpenDeepSpace.Autofacastle
gitee源码地址,OpenDeepSpace.Autofacastle

集成Autofacastle

仅仅只需要这样的简单的一句话即可

//containerBuilder是ContainerBuilder的实例
containerBuilder.UseAutofacastle(automaticInjectionSelectors:null);

UseAutofacastle存在四个重载方法,可以供你根据情况来使用。

/// <summary>
        /// 
        /// 该方法与
        /// <see cref="ContainerBuilderExtensions.BatchInjection(ContainerBuilder)"/>
        /// 结合使用完成批量注入 特性依赖注入
        /// 
        /// 或单独使用该方法来完成特性依赖注入
        /// 
        /// </summary>
        /// <param name="containerBuilder"></param>
        /// <param name="automaticInjectionSelectors">自动注入筛选器,满足自动注入筛选器条件的类可以不使用AutomaticInjection特性就可以完成实例的注入</param>
        /// <returns></returns>
        public static ContainerBuilder UseAutofacastle(this ContainerBuilder containerBuilder, List<AutomaticInjectionSelector> automaticInjectionSelectors = null)


/// <summary>
        /// 该方法单独使用完成批量注入 特性依赖注入 切面拦截
        /// 
        /// </summary>
        /// <param name="containerBuilder"></param>
        /// <param name="automaticInjectionSelectors"></param>
        /// <param name="nonInterceptSelectors">不拦截筛选器 符合条件的类将不会被拦截器所拦截</param>
        /// <param name="IsConfigureIntercept">是否配置拦截器 如果开启表示使用拦截器,注意如果你目前项目中已经使用了基于Autofac和Castle的拦截器 建议不开启以免出现冲突异常</param>
        /// <returns></returns>
        public static ContainerBuilder UseAutofacastle(this ContainerBuilder containerBuilder, List<AutomaticInjectionSelector> automaticInjectionSelectors = null, List<NonInterceptSelector> nonInterceptSelectors = null, bool IsConfigureIntercept=false)


/// <summary>
        /// 该方法单独使用完成批量注入 特性依赖注入 切面拦截
        /// 可指定程序集 
        /// </summary>
        /// <param name="containerBuilder"></param>
        /// <param name="assemblies">程序集</param>
        /// <param name="automaticInjectionSelectors"></param>
        /// <param name="nonInterceptSelectors"></param>
        /// <param name="IsConfigureIntercept"></param>
        /// <returns></returns>
        public static ContainerBuilder UseAutofacastle(this ContainerBuilder containerBuilder,List<Assembly> assemblies,List<AutomaticInjectionSelector> automaticInjectionSelectors = null, List<NonInterceptSelector> nonInterceptSelectors = null, bool IsConfigureIntercept = false)

/// <summary>
        /// 该方法单独使用完成批量注入 特性依赖注入 切面拦截
        /// 指定类型集
        /// </summary>
        /// <param name="containerBuilder"></param>
        /// <param name="types">类型集</param>
        /// <param name="automaticInjectionSelectors"></param>
        /// <param name="nonInterceptSelectors"></param>
        /// <param name="IsConfigureIntercept"></param>
        /// <returns></returns>
        public static ContainerBuilder UseAutofacastle(this ContainerBuilder containerBuilder,List<Type> types,List<AutomaticInjectionSelector> automaticInjectionSelectors = null, List<NonInterceptSelector> nonInterceptSelectors = null, bool IsConfigureIntercept = false)

在AspNetCore项目中

如果你使用的系统自带的容器即通过IServiceCollection注入了一些实例,如果你想集成Autofacastle,这里你需要实现Autofac中的IServiceProviderFactory<>泛型工厂,通过这样来集成Autofacastle。然后重写ContainerBuilder的Populate方法即在这里面来完成对IServiceCollection中注入的类然后注入到Autofac容器中,并且通过UseServiceProviderFactory中使用该类。

Controller里面需要使用自动注入特性需要把Controller作为Service:

builder.Services.AddControllers().AddControllersAsServices();//controller中要使用自动注入特性注入实例

实现IServiceProviderFactory<>工厂

/// <summary>
    /// 重写服务工厂来实现通过IServiceCollection从外部自己注入的service实现拦截
    /// </summary>
    public class AutofacastleServiceProviderFactory : IServiceProviderFactory<ContainerBuilder>
    {
        private readonly Action<ContainerBuilder>? _configurationAction;
        private readonly List<AutomaticInjectionSelector> _automaticInjectionSelectors;
        private readonly List<NonInterceptSelector> _nonInterceptSelectors;

        //如果需要自己指定程序集 和 类型也可以自己传入 最后自己传入程序集 如果不用传入程序集 系统将默认加载当前项目所有程序集并排除某些开头的程序集
        private readonly List<Assembly> _assemblies;

        public AutofacastleServiceProviderFactory(Action<ContainerBuilder>? configurationAction = null, List<AutomaticInjectionSelector>? automaticInjectionSelectors = null, List<NonInterceptSelector>? nonInterceptSelectors = null,List<Assembly>? assemblies=null)
        {
            _configurationAction = configurationAction ?? (builder => { });
            _assemblies = assemblies ?? AssemblyFinder.GetAllAssemblies();
            _automaticInjectionSelectors = automaticInjectionSelectors ?? new List<AutomaticInjectionSelector>();
            _nonInterceptSelectors = nonInterceptSelectors ?? new List<NonInterceptSelector>();
        }

        public ContainerBuilder CreateBuilder(IServiceCollection services)
        {
            var builder = new ContainerBuilder();

            builder.UseAutofacastle(_assemblies,_automaticInjectionSelectors,_nonInterceptSelectors,IsConfigureIntercept:true);

            builder.Populate(services);

            if (_configurationAction == null)
                throw new InvalidOperationException($"{nameof(_configurationAction)}为空");

            _configurationAction(builder);

            return builder;
        }

        public IServiceProvider CreateServiceProvider(ContainerBuilder containerBuilder)
        {
            if (containerBuilder == null)
                throw new ArgumentNullException(nameof(containerBuilder));

            var container = containerBuilder.Build();

            //在不能使用特性 集比如静态方法中获取注入到容器中的值
            IocManager.InitContainer(container);

            return new AutofacServiceProvider(container);
        }
    }

完成IServiceCollection中注入的类注入到Autofac容器中

/// <summary>
    /// 
    /// </summary>
    public static class AutofacastleRegistrationExtensions
    {
        /// <summary>
        /// Populates the Autofac container builder with the set of registered service descriptors
        /// and makes <see cref="IServiceProvider"/> and <see cref="IServiceScopeFactory"/>
        /// available in the container.
        /// </summary>
        /// <param name="builder">
        /// The <see cref="ContainerBuilder"/> into which the registrations should be made.
        /// </param>
        /// <param name="services">
        /// A container builder that can be used to create an <see cref="IServiceProvider" />.
        /// </param>
        public static void Populate(
            this ContainerBuilder builder,
            IServiceCollection services)
        {
            Populate(builder, services, null);
        }

        /// <summary>
        /// Populates the Autofac container builder with the set of registered service descriptors
        /// and makes <see cref="IServiceProvider"/> and <see cref="IServiceScopeFactory"/>
        /// available in the container. Using this overload is incompatible with the ASP.NET Core
        /// support for <see cref="IServiceProviderFactory{TContainerBuilder}"/>.
        /// </summary>
        /// <param name="builder">
        /// The <see cref="ContainerBuilder"/> into which the registrations should be made.
        /// </param>
        /// <param name="services">
        /// A container builder that can be used to create an <see cref="IServiceProvider" />.
        /// </param>
        /// <param name="lifetimeScopeTagForSingletons">
        /// If provided and not <see langword="null"/> then all registrations with lifetime <see cref="ServiceLifetime.Singleton" /> are registered
        /// using <see cref="IRegistrationBuilder{TLimit,TActivatorData,TRegistrationStyle}.InstancePerMatchingLifetimeScope" />
        /// with provided <paramref name="lifetimeScopeTagForSingletons"/>
        /// instead of using <see cref="IRegistrationBuilder{TLimit,TActivatorData,TRegistrationStyle}.SingleInstance"/>.
        /// </param>
        /// <remarks>
        /// <para>
        /// Specifying a <paramref name="lifetimeScopeTagForSingletons"/> addresses a specific case where you have
        /// an application that uses Autofac but where you need to isolate a set of services in a child scope. For example,
        /// if you have a large application that self-hosts ASP.NET Core items, you may want to isolate the ASP.NET
        /// Core registrations in a child lifetime scope so they don't show up for the rest of the application.
        /// This overload allows that. Note it is the developer's responsibility to execute this and create an
        /// <see cref="AutofacServiceProvider"/> using the child lifetime scope.
        /// </para>
        /// </remarks>
        public static void Populate(
            this ContainerBuilder builder,
            IServiceCollection services,
            object? lifetimeScopeTagForSingletons)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

            builder.RegisterType<AutofacServiceProvider>().As<IServiceProvider>().ExternallyOwned();
            var autofacServiceScopeFactory = typeof(AutofacServiceProvider).Assembly.GetType("Autofac.Extensions.DependencyInjection.AutofacServiceScopeFactory");
            if (autofacServiceScopeFactory == null)
            {
                throw new Exception("Unable get type of Autofac.Extensions.DependencyInjection.AutofacServiceScopeFactory!");
            }
            builder.RegisterType(autofacServiceScopeFactory).As<IServiceScopeFactory>();

            Register(builder, services, lifetimeScopeTagForSingletons);
        }

        /// <summary>
        /// Configures the lifecycle on a service registration.
        /// </summary>
        /// <typeparam name="TActivatorData">The activator data type.</typeparam>
        /// <typeparam name="TRegistrationStyle">The object registration style.</typeparam>
        /// <param name="registrationBuilder">The registration being built.</param>
        /// <param name="lifecycleKind">The lifecycle specified on the service registration.</param>
        /// <param name="lifetimeScopeTagForSingleton">
        /// If not <see langword="null"/> then all registrations with lifetime <see cref="ServiceLifetime.Singleton" /> are registered
        /// using <see cref="IRegistrationBuilder{TLimit,TActivatorData,TRegistrationStyle}.InstancePerMatchingLifetimeScope" />
        /// with provided <paramref name="lifetimeScopeTagForSingleton"/>
        /// instead of using <see cref="IRegistrationBuilder{TLimit,TActivatorData,TRegistrationStyle}.SingleInstance"/>.
        /// </param>
        /// <returns>
        /// The <paramref name="registrationBuilder" />, configured with the proper lifetime scope,
        /// and available for additional configuration.
        /// </returns>
        private static IRegistrationBuilder<object, TActivatorData, TRegistrationStyle> ConfigureLifecycle<TActivatorData, TRegistrationStyle>(
            this IRegistrationBuilder<object, TActivatorData, TRegistrationStyle> registrationBuilder,
            ServiceLifetime lifecycleKind,
            object? lifetimeScopeTagForSingleton)
        {
            switch (lifecycleKind)
            {
                case ServiceLifetime.Singleton:
                    if (lifetimeScopeTagForSingleton == null)
                    {
                        registrationBuilder.SingleInstance();
                    }
                    else
                    {
                        registrationBuilder.InstancePerMatchingLifetimeScope(lifetimeScopeTagForSingleton);
                    }

                    break;
                case ServiceLifetime.Scoped:
                    registrationBuilder.InstancePerLifetimeScope();
                    break;
                case ServiceLifetime.Transient:
                    registrationBuilder.InstancePerDependency();
                    break;
            }

            return registrationBuilder;
        }

        /// <summary>
        /// 遍历IServiceCollection中的service并注册到Autofac的容器中
        /// Populates the Autofac container builder with the set of registered service descriptors.
        /// </summary>
        /// <param name="builder">
        /// The <see cref="ContainerBuilder"/> into which the registrations should be made.
        /// </param>
        /// <param name="services">
        /// A container builder that can be used to create an <see cref="IServiceProvider" />.
        /// </param>
        /// <param name="lifetimeScopeTagForSingletons">
        /// If not <see langword="null"/> then all registrations with lifetime <see cref="ServiceLifetime.Singleton" /> are registered
        /// using <see cref="IRegistrationBuilder{TLimit,TActivatorData,TRegistrationStyle}.InstancePerMatchingLifetimeScope" />
        /// with provided <paramref name="lifetimeScopeTagForSingletons"/>
        /// instead of using <see cref="IRegistrationBuilder{TLimit,TActivatorData,TRegistrationStyle}.SingleInstance"/>.
        /// </param>
        private static void Register(
            ContainerBuilder builder,
            IServiceCollection services,
            object? lifetimeScopeTagForSingletons)
        {
            //key value对调
            var keyedDicKvReverse = AutofacastleCollection.KeyedImplementationTypes.ToDictionary(t => t.Value, t => t.Key);
            var namedDicKvReverse = AutofacastleCollection.NamedImplementationTypes.ToDictionary(t => t.Value, t => t.Key);

            foreach (var descriptor in services)
            {
                if (descriptor.ImplementationType != null)//存在实现类
                {


                    // Test if the an open generic type is being registered
                    var serviceTypeInfo = descriptor.ServiceType.GetTypeInfo();


                    if (descriptor.ImplementationType.IsGenericTypeDefinition)//动态泛型
                    {

                        if (serviceTypeInfo.ContainsGenericParameters)//包含泛型参数 实现的为开放泛型才注入:xxx<> 实现的非开放泛型不能注入:xxx
                        {

                            var registrationBuilder = builder
                                .RegisterGeneric(descriptor.ImplementationType)
                                .As(descriptor.ServiceType).AsSelf().Keyed(descriptor.ImplementationType, descriptor.ServiceType);
                            if (descriptor.ImplementationType.FullName != null)
                                registrationBuilder.Named(descriptor.ImplementationType.FullName, descriptor.ServiceType);

                            if (keyedDicKvReverse.ContainsKey(descriptor.ImplementationType))
                            {
                                registrationBuilder.Keyed(keyedDicKvReverse[descriptor.ImplementationType], descriptor.ServiceType);
                            }

                            if (namedDicKvReverse.ContainsKey(descriptor.ImplementationType))
                            {
                                registrationBuilder.Named(namedDicKvReverse[descriptor.ImplementationType], descriptor.ServiceType);
                            }

                            registrationBuilder.ConfigureLifecycle(descriptor.Lifetime, lifetimeScopeTagForSingletons)
                                .AddIntercept(descriptor.ImplementationType);
                        }

                    }
                    else
                    {

                        var registrationBuilder = builder
                            .RegisterType(descriptor.ImplementationType).AsSelf()
                            .As(descriptor.ServiceType).Keyed(descriptor.ImplementationType, descriptor.ServiceType);
                        if (descriptor.ImplementationType.FullName != null)
                            registrationBuilder.Named(descriptor.ImplementationType.FullName, descriptor.ServiceType);

                        if (keyedDicKvReverse.ContainsKey(descriptor.ImplementationType))
                        {
                            registrationBuilder.Keyed(keyedDicKvReverse[descriptor.ImplementationType], descriptor.ServiceType);
                        }

                        if (namedDicKvReverse.ContainsKey(descriptor.ImplementationType))
                        {
                            registrationBuilder.Named(namedDicKvReverse[descriptor.ImplementationType], descriptor.ServiceType);
                        }

                        if (AutofacastleCollection.AutoActives.Any(t => t == descriptor.ImplementationType))
                            registrationBuilder.AutoActivate();//预加载

                        registrationBuilder.ConfigureLifecycle(descriptor.Lifetime, lifetimeScopeTagForSingletons)
                            .AddIntercept(descriptor.ImplementationType);

                    }
                }
                else if (descriptor.ImplementationFactory != null)
                {

                    var registration = RegistrationBuilder.ForDelegate(descriptor.ServiceType, (context, parameters) =>
                    {
                        var serviceProvider = context.Resolve<IServiceProvider>();
                        return descriptor.ImplementationFactory(serviceProvider);
                    })
                        .ConfigureLifecycle(descriptor.Lifetime, lifetimeScopeTagForSingletons)
                        .CreateRegistration();

                    builder.RegisterComponent(registration);
                }
                else
                {
                    
                    if (descriptor.ImplementationInstance == null)
                        return;

                    var registrationBuilder = builder
                        .RegisterInstance(descriptor.ImplementationInstance)
                        .As(descriptor.ServiceType).Keyed(descriptor.ImplementationInstance.GetType(), descriptor.ServiceType);
                    var implementationInstanceFullName = descriptor.ImplementationInstance.GetType().FullName;
                    if (implementationInstanceFullName != null)
                        registrationBuilder.Named(implementationInstanceFullName, descriptor.ServiceType);

                    if (keyedDicKvReverse.ContainsKey(descriptor.ImplementationInstance.GetType()))
                    {
                        registrationBuilder.Keyed(keyedDicKvReverse[descriptor.ImplementationInstance.GetType()], descriptor.ServiceType);
                    }

                    if (namedDicKvReverse.ContainsKey(descriptor.ImplementationInstance.GetType()))
                    {
                        registrationBuilder.Named(namedDicKvReverse[descriptor.ImplementationInstance.GetType()], descriptor.ServiceType);
                    }

                    registrationBuilder.ConfigureLifecycle(descriptor.Lifetime, null);
                }
            }
        }
    }

使用Autofacastle

//外部兼容
builder.Host.ConfigureContainer<ContainerBuilder>(builder =>
{



    //外部注入单独的添加拦截器需要先调用UseAutofacastle
    builder.RegisterType<ExternalService>().AsSelf().AddIntercept(typeof(ExternalService));



}).UseServiceProviderFactory(new AutofacastleServiceProviderFactory(automaticInjectionSelectors:new List<AutomaticInjectionSelector>()
{ 
    
      //实现了ITransientService类中的 所有字段属性自动注入
    new OpenDeepSpace.Autofacastle.DependencyInjection.AutomaticInjectionSelector(t=>typeof(ITransientService).IsAssignableFrom(t))
    

}, nonInterceptSelectors:new List<OpenDeepSpace.Autofacastle.AspectAttention.NonInterceptSelector>() { 

    //实现了ITransientService的类 都不被AOP所拦截
    new OpenDeepSpace.Autofacastle.AspectAttention.NonInterceptSelector(t=>typeof(ITransientService).IsAssignableFrom(t) && t!=typeof(TransientUpgradeService))
}));

以上代码只是一个举例,表示你可以在适当的位置使用Autofacastle,当然你也可以根据自己的情况在需要使用Autofacastle的地方使用它。

自动注入实例

使用特性自动注入实例

AutomaticInjectionAttribute 自动注入特性 可使用在类/属性/字段上 使用在类上该类下面所有属性字段都会自动注入相应的实例

属性类型说明
ImplementationTypeType指定实现类 用在单接口多实现的情况 比如一个接口有多个实现类 你需要指定某个实现类作为注入实例
Keyedobject指定Keyed
Namedstring指定Named

使用自动注入筛选器注入实例

自动注入筛选器在UseAutofacastle的时候可以进行配置,即AutomaticInjectionSelector。如果配置了那么符合条件类下面所依赖的类,在属性和字段可以不使AutomaticInjection特性即可完成注入。

使用不自动注入特性

NonAutomaticInjectionAttribute 不自动注入特性 可用在类上/字段上/属性上
该特性主要当设置了自动注入筛选器后,比如该范围内某些类/属性/字段不需要自动注入,使用了该特性就不会被注入。

在不能使用特性注入实例的类中通过IocManager获取容器中注入的实例

需要使用IocManager获取容器中的实例。你就需要首先对IocManager进行一个容器初始化操作。这里提供以下几个方法。

//传入IContainer
public static void InitContainer(IContainer container)
//传入ContainerBuilder
public static void InitContainer(ContainerBuilder container)
//传入ILifetimeScope
public static void InitContainer(ILifetimeScope lifetimeScope)

在不能使用特性注入的类里面获取实例

private IAutomaticInjectionServiceA automaticInjectionServiceA => IocManager.TryResolve<IAutomaticInjectionServiceA>(typeof(AutomaticInjectionServiceA));
        private IAutomaticInjectionServiceA automaticInjectionServiceATwo => IocManager.Resolve<IAutomaticInjectionServiceA>(typeof(AutomaticInjectionServiceA),Autofacastle.DependencyInjection.Enums.ResolveMode.Self);
        private IAutomaticInjectionServiceA automaticInjectionServiceATrip => IocManager.Resolve<IAutomaticInjectionServiceA>(typeof(AutomaticInjectionServiceA).FullName);
        private IAutomaticInjectionServiceA automaticInjectionServiceAFour => IocManager.Resolve<IAutomaticInjectionServiceA>(Keyed:typeof(AutomaticInjectionServiceA));

批量注入

OpenDeepSpace.Autofacastle自带通过特性或实现接口自动批量注入类。
如果你要单独只使用这个批量注入,可以使用如下代码来完成:

/// <summary>
        /// 批量注入
        /// </summary>
        /// <param name="containerBuilder"></param>
        /// <param name="assemblies">程序集</param>
        /// <returns></returns>
        public static ContainerBuilder BatchInjection(this ContainerBuilder containerBuilder, List<Assembly> assemblies)

/// <summary>
        /// 批量注入
        /// </summary>
        /// <param name="containerBuilder"></param>
        /// <param name="types">类型集合</param>
        /// <returns></returns>
        public static ContainerBuilder BatchInjection(this ContainerBuilder containerBuilder, List<Type> types)

/// <summary>
        /// ContainerBuilder批量注入
        /// </summary>
        /// <param name="containerBuilder"></param>
        /// <returns></returns>
        public static ContainerBuilder BatchInjection(this ContainerBuilder containerBuilder)

自动注入接口

接口名称说明
ITransient对应瞬时每次都获取一个新的对象
IScoped每次请求获取一个对象或在同一个Scope范围内是一个对象
ISingleton单例 每次都是相同的对象

自动注入特性

特性名称说明
TransientAttribute对应瞬时每次都获取一个新的对象
ScopedAttribute每次请求获取一个对象或在同一个Scope范围内是一个对象
SingletonAttribute单例 每次都是相同的对象

以上的自动注入特性存在一下属性可设置

属性名称类型说明
AsServicesType[]作为服务,如果当前实现类实现了多个接口或继承了某一个基类 你希望这个实现类只作为其中几个接口或基类的实现注入到容器中 就可以通过他指定
KeyedObject指定以某个Keyed注入
Namedstring指定以某个Named注入
AutoActivatebool预加载(自动激活加载),Singleton特性特有,即程序启动的时候完成对类的初始化和加载

AOP横切关注

需要被拦截的类需要注入到容器中
Autofacastle中拦截分类两大类:一种是针对接口代理的拦截,一种是针对类代理的拦截而对类代理的拦截只能拦截虚方法

类拦截特性

ClassInterceptAttribute特性,由于Autofacastle会自动判断如果实现了接口的类都会使用接口代理,但是实际上比如一个类实现了IDispose接口,显然它不能以IDispose生成代理,这个时候需要你在该类上使用类拦截特性,表示这个类使用类拦截。

不拦截特性

NonInterceptAttribute特性,主要结合通过使用拦截点拦截时,某些类并不需要拦截,那么你可以使用该特性表示不需要拦截

不拦截筛选器

不拦截筛选器在UseAutofacastle时进行配置,NonInterceptSelector,符合该筛选器的类将不会被拦截。

通过特性来完成拦截

特性名称说明
MethodBeforeAbstractInterceptAttribute方法前拦截
MethodAroundAbstractInterceptAttribute环绕方法拦截 或 方法前后拦截
MethodAfterAbstractInterceptAttribute方法执行完成之后执行 无论是否存在异常都会执行
MethodAfterReturnAbstractInterceptAttribute方法正常执行无异常后 执行
MethodAfterThrowingAbstractInterceptAttribute在方法执行完后抛出异常时拦截执行

特性中可以指定的属性值

属性值类型说明
Orderint拦截优先级序号 order越小优先级越高 同分组的order应相同
GroupNamestring分组组名

以上这些特性,共同组成一个Autofacastle中的一个拦截面,你可以通过Order和GroupName指定多个分组 并根据Order优先级来依次执行,Order越小优先级越高

一个拦截面执行顺序
在这里插入图片描述

多个拦截面的执行顺序
在这里插入图片描述
更多个拦截面以此类推,根据中间件原理先进后出的执行,如果其中在执行其中一个拦截面的时候方法出现异常,将不会在执行后续的拦截面,被当前执行的拦截面所处理。

通过拦截点来拦截

一个拦截点构成一个拦截面。
InterceptPointAttribute 拦截点特性 使用在某个拦截点类上

拦截表达式

拦截点特性上有个非常重要的参数,InterceptExpression拦截表达式并且一个拦截表达式需要遵守一定的规则。

拦截点表达式模式如下:
intercept(返回参数类型 命名空间 类名 方法名 (方法参数类型))
intercept()里面包含具体的拦截体
拦截体部分:返回参数类型 命名空间 类名 方法名 (方法参数) ,每一个之间都有一个空格注意仅一个严格控制

返回参数
示例说明
*任意返回值类型
system.void指定返回值类型(一定要完全名,不区分大小写)
命名空间/类名/方法
示例说明
*任意命名空间
test.*以test开头的命名空间/类
*.test以test结尾的命名空间/类
*.test.*包含test的命名空间/类
方法参数类型
示例说明
()空参数
(…)任意参数
(*)任意一个参数
(*,*)任意两个参数,逗号分隔,多个参数一次类推
system.int32具体参数,完全名不区分大小写
(*,system.int32)第一个参数任意,第二个参数具体,一次类推其他
几个拦截表达式的示例
示例说明
intercept(* * * * (…))任意返回值任意命名空间任意类任意方法任意参数
intercept(* * * * ())任意返回值任意命名空间任意类任意方法空参数
intercept(system.void * * * (…))void返回值的任意命名空间任意方法任意参数

实现一个拦截点,需要首先使用一个AbstractInterceptPoint,并在这个拦截点上打上拦截点特性

/// <summary>
    /// 日志拦截点
    /// </summary>
    [InterceptPoint("intercept(* * * * (..))")]
    public class LogInterceptPoint:AbstractInterceptPoint
    {

        public override async Task AroundBefore(InterceptContext interceptContext)
        {
            Console.WriteLine("InterceptPoint环绕前");
            await Task.CompletedTask;
        }

        public override async Task AroundAfter(InterceptContext interceptContext)
        {
            Console.WriteLine("InterceptPoint环绕后");
            await Task.CompletedTask;
        }

        public override void Before()
        {
            Console.WriteLine("InterceptPoint方法前");

        }

        public override void After()
        {
            Console.WriteLine("InterceptPoint方法后无论是否正常返回");

        }

        public override void AfterReturn(object value)
        {
            Console.WriteLine($"InterceptPoint方法执行正常返回{value}");
        }

        public override void AfterThrowing(Exception exception)
        {
            Console.WriteLine("InterceptPoint方法异常执行");
        }
    }

被拦截的类将会被代理,如果你想获取其实际非代理的类可以使用一下代码:

var instanceType = instance.GetType();
                if (ProxyUtil.IsProxy(instance))//如果是代理类 获取实际类以及其类型
                {
                    instance = ProxyUtil.GetUnproxiedInstance(instance);
                    instanceType = ProxyUtil.GetUnproxiedType(instance);
                }

欢迎大家在评论区指出使用中出现的错误和异常,以及在使用中有什么问题,可以提出来。谢谢!!!

最新版本1.1.0中的变化:
1.环绕拦截Around放开,不再有AroundBefore AroundAfter,需要自己决定什么时候执行真正的方法await next(interceptContext);
2.增加INonIntercept接口,实现该接口即不需要拦截
3.增加IClassIntercept接口,实现该接口即为类拦截
4.增加ClassInterceptSelector筛选器,满足筛选器条件的将为类拦截

如果是在NetCore中使用ServiceCollection实现批量注入,对于泛型类型出现异常,需要修复类型

/// <summary>
        /// 修复类型 获取到的泛型接口FullName为null
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static Type FixTypeReference(this Type type)
        {
            if (type.FullName != null)
                return type;

            string typeQualifiedName = type.DeclaringType != null
                ? type.DeclaringType.FullName + "+" + type.Name + "," + type.Assembly.FullName
                : type.Namespace + "." + type.Name + "," + type.Assembly.FullName;

            return Type.GetType(typeQualifiedName, true);
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值