现在我们回到最开始报错的地方,将其 Install() 方法改为调用我们新的扩展方法。
public void RegisterAssemblyByConvention(Assembly assembly, ConventionalRegistrationConfig config) { var context = new ConventionalRegistrationContext(assembly, this, config); foreach (var registerer in _conventionalRegistrars) { registerer.RegisterAssembly(context); } if (config.InstallInstallers) { // 调用之前编写的扩展方法 this.Install(assembly); } } 4.1.2 依赖注入辅助接口改造Abp 库本身提供了两个接口 (ITransientDependency、ISingletonDependency ) 来帮助用户快速地注入某个对象,然后通过注册规约结合 IocManager 提供的 AddConventionalRegistrar() 方法和 RegisterAssemblyByConvention() 方法能够快速地将某个程序集内部符合规则的类型进行注入。(PS: 这里其实流程很像之前 Installer 的做法)
在使用 Castle Windsor 的时候,Abp 本身并不需要做太多的工作,就可以实现上述的功能。而 DryIoc 本身是没有提供这些比较高级的特性的,但原理其实并不复杂, 就是扫描整个程序集的所有类型,然后挨个进行判断即可。
在原来的 BasicConventionalRegistrar 类型内部,对实现了 ITransientDependency 、 ISingletonDependency 、 IInterceptor 接口的类型进行了自动注册。所以我们就有了以下的实现代码:
using System; using System.Linq; using System.Reflection; using Abp.Extensions; using Castle.DynamicProxy; namespace Abp.Dependency { public class AssemblyType { public Type ServiceType { get; set; } public Type ImplType { get; set; } } /// <summary> /// 本类用于注册实现了 <see cref="ITransientDependency"/> 和 <see cref="ISingletonDependency"/> 接口的类型。 /// </summary> public class BasicConventionalRegistrar : IConventionalDependencyRegistrar { public void RegisterAssembly(IConventionalRegistrationContext context) { // 瞬时对象注册 var waitRegisterTransient = GetTypes<ITransientDependency>(context.Assembly).ToList(); foreach (var transientType in waitRegisterTransient) { context.IocManager.RegisterIfNot(transientType.ServiceType,transientType.ImplType,DependencyLifeStyle.Transient); } // 单例对象注册 var waitRegisterSingleton = GetTypes<ISingletonDependency>(context.Assembly).ToList(); foreach (var singletonType in waitRegisterSingleton) { context.IocManager.RegisterIfNot(singletonType.ServiceType,singletonType.ImplType,DependencyLifeStyle.Singleton); } // Castle.DynamicProxy 拦截器注册 var waitRegisterInterceptor = GetTypes<IInterceptor>(context.Assembly).ToList(); foreach (var interceptorType in waitRegisterInterceptor) { context.IocManager.RegisterIfNot(interceptorType.ServiceType,interceptorType.ImplType,DependencyLifeStyle.Transient); } } private ParallelQuery<AssemblyType> GetTypes<TInterface>(Assembly assembly) { Type GetServiceType(Type type) { var interfaces = type.GetInterfaces().Where(i => i != typeof(TInterface)); // 优先匹配去除 I 之后的接口 var defaultInterface = interfaces.FirstOrDefault(i => type.Name.Equals(i.Name.RemovePreFix("I"))); if (defaultInterface != null) return defaultInterface; if (interfaces.FirstOrDefault() != null) return interfaces.FirstOrDefault(); return type; } return assembly.GetTypes() .AsParallel() .Where(type => typeof(TInterface).IsAssignableFrom(type)) .Where(type => type.GetInterfaces().Any() && !type.IsInterface) .Where(type => !type.IsGenericTypeDefinition) .Where(type => !type.IsAbstract) .Select(type => new AssemblyType { ServiceType = GetServiceType(type), ImplType = type }); } } }在我们实现的新的注册规约当中可以看到,其实最核心的代码在于 GetTypes() 方法内部,在其内部进行了比较复杂的判断逻辑,其余的瞬时对象与单例对象的注入,都是直接调用的 IIocManager 接口所提供的注册方法。
4.1.3 拦截器绑定因为没有使用 Castle Windsor ,那么我们拦截器如何使用?又如何与类型进行绑定的呢?
在 DryIoc 官方文档已经说明,DryIoc 本身的拦截功能也是通过 Castle Dynamic Proxy 来实现的,所以我们只需要编写一个辅助的静态扩展类即可。
using System; using System.Linq; using Castle.DynamicProxy; using DryIoc; using ImTools; public static class DryIocInterception { static readonly DefaultProxyBuilder ProxyBuilder = new DefaultProxyBuilder(); public static void Intercept(this IRegistrator registrator,Type serviceType,Type interceptorType,Type implType, object serviceKey = null) { // 判断传入的类型是接口还是类型,以便建立代理类 Type proxyType; if (serviceType.IsInterface()) proxyType = ProxyBuilder.CreateInterfaceProxyTypeWithTargetInterface( serviceType, ArrayTools.Empty<Type>(), ProxyGenerationOptions.Default); else if (serviceType.IsClass()) proxyType = ProxyBuilder.CreateClassProxyTypeWithTarget( serviceType, ArrayTools.Empty<Type>(), ProxyGenerationOptions.Default); else throw new ArgumentException( $"Intercepted service type {serviceType} is not a supported, cause it is nor a class nor an interface"); // 创建 DryIoc 装饰器 var decoratorSetup = serviceKey == null ? Setup.DecoratorWith(useDecorateeReuse: true) : Setup.DecoratorWith(r => serviceKey.Equals(r.ServiceKey), useDecorateeReuse: true); // 替换注册原来接口的解析,解析到新的代理类 registrator.Register(serviceType, proxyType, made: Made.Of((Type type) => type.GetConstructors().SingleOrDefault(c => c.GetParameters().Length != 0), Parameters.Of.Type<IInterceptor[]>(interceptorType.MakeArrayType()), // 一定要加上这个,不然属性注入无法使用 PropertiesAndFields.Auto), setup: decoratorSetup); } public static void Intercept<TService,TImplType, TInterceptor>(this IRegistrator registrator, object serviceKey = null) where TInterceptor : class, IInterceptor { Intercept(registrator,typeof(TService),typeof(TInterceptor),typeof(TImplType),serviceKey); } }这个扩展类的用法,在后面就有体现。
4.1.4 拦截器注册器绑定事件