在之前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 自动注入特性 可使用在类/属性/字段上 使用在类上该类下面所有属性字段都会自动注入相应的实例
属性 | 类型 | 说明 |
---|---|---|
ImplementationType | Type | 指定实现类 用在单接口多实现的情况 比如一个接口有多个实现类 你需要指定某个实现类作为注入实例 |
Keyed | object | 指定Keyed |
Named | string | 指定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 | 单例 每次都是相同的对象 |
以上的自动注入特性存在一下属性可设置
属性名称 | 类型 | 说明 |
---|---|---|
AsServices | Type[] | 作为服务,如果当前实现类实现了多个接口或继承了某一个基类 你希望这个实现类只作为其中几个接口或基类的实现注入到容器中 就可以通过他指定 |
Keyed | Object | 指定以某个Keyed注入 |
Named | string | 指定以某个Named注入 |
AutoActivate | bool | 预加载(自动激活加载),Singleton特性特有,即程序启动的时候完成对类的初始化和加载 |
AOP横切关注
需要被拦截的类需要注入到容器中
Autofacastle中拦截分类两大类:一种是针对接口代理的拦截,一种是针对类代理的拦截而对类代理的拦截只能拦截虚方法
类拦截特性
ClassInterceptAttribute特性,由于Autofacastle会自动判断如果实现了接口的类都会使用接口代理,但是实际上比如一个类实现了IDispose接口,显然它不能以IDispose生成代理,这个时候需要你在该类上使用类拦截特性,表示这个类使用类拦截。
不拦截特性
NonInterceptAttribute特性,主要结合通过使用拦截点拦截时,某些类并不需要拦截,那么你可以使用该特性表示不需要拦截
不拦截筛选器
不拦截筛选器在UseAutofacastle时进行配置,NonInterceptSelector,符合该筛选器的类将不会被拦截。
通过特性来完成拦截
特性名称 | 说明 |
---|---|
MethodBeforeAbstractInterceptAttribute | 方法前拦截 |
MethodAroundAbstractInterceptAttribute | 环绕方法拦截 或 方法前后拦截 |
MethodAfterAbstractInterceptAttribute | 方法执行完成之后执行 无论是否存在异常都会执行 |
MethodAfterReturnAbstractInterceptAttribute | 方法正常执行无异常后 执行 |
MethodAfterThrowingAbstractInterceptAttribute | 在方法执行完后抛出异常时拦截执行 |
特性中可以指定的属性值
属性值 | 类型 | 说明 |
---|---|---|
Order | int | 拦截优先级序号 order越小优先级越高 同分组的order应相同 |
GroupName | string | 分组组名 |
以上这些特性,共同组成一个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);
}