2、Ioc容器的使用
(现在最最流行的编码方式,通过DDD编码思想 + Ioc容器完成对象的生命周期管理)
通过自己先来造一个容器,来了解一下所谓的容器是怎么回事,它是如何完成对象的注册和取出的骚操作。
定义自制容器需要用到的接口类型:
/// <summary> /// 定义Ioc容器的规则 /// </summary> public interface IContainer : IServiceProvider, IServiceRegister { } /// <summary> /// 服务取出接口 /// </summary> public interface IServiceProvider { T Resolve<T>() where T : class; } /// <summary> /// 服务注册接口 /// </summary> public interface IServiceRegister { IServiceRegister Register<T>(Func<IServiceProvider, T> serviceCreator) where T : class; IServiceRegister Register<T>() where T : class; }
定义一个自制容器的类型
/// <summary> /// 迷你Ioc容器。自制低配版本的autofac框架。 /// </summary> public class DefaultServiceProvider : IContainer { private readonly IDictionary<string, object> _factories = new Dictionary<string, object>(); private readonly IDictionary<string, Type> _registrations = new Dictionary<string, Type>(); private readonly IDictionary<string, object> _instances = new Dictionary<string, object>(); private static DefaultServiceProvider _Instance = null; public static DefaultServiceProvider Instance { get { if (_Instance == null) { _Instance = new DefaultServiceProvider(); } return _Instance; } } private bool ServiceIsRegistered(string typeName) { return this._factories.ContainsKey(typeName) || this._registrations.ContainsKey(typeName); } public IServiceRegister Register<T>(Func<IServiceProvider, T> ctor) where T : class { Preconditions.CheckNotNull(ctor, "serviceCreator"); Type type = typeof(T); string typeName = type.FullName; if (this.ServiceIsRegistered(typeName)) { return this; } this._factories.Add(typeName, ctor); return this; } public T Resolve<T>() where T : class { Type serivceType = typeof(T); string typeName = serivceType.FullName; if (!this.ServiceIsRegistered(typeName)) { throw new Exception(string.Format("类型名称 {0} 已经被注册过!", typeName)); } if (!this._instances.ContainsKey(typeName)) { if (this._registrations.ContainsKey(typeName)) { Type type = this._registrations[typeName]; object @object = this.CreateServiceInstance(type); this._instances.Add(typeName, @object); } if (this._factories.ContainsKey(typeName)) { object @object = ((Func<IServiceProvider, T>)this._factories[typeName])(this); this._instances.Add(typeName, @object); } } return (T)this._instances[typeName]; } private object Resolve(Type serviceType) { return typeof(DefaultServiceProvider).GetMethod("Resolve", new Type[0]).MakeGenericMethod(serviceType).Invoke(this, new object[0]); } private object CreateServiceInstance(Type type) { var constructors = type.GetConstructors(); ParameterInfo[] parameterInfos = constructors[0].GetParameters(); var parameters = parameterInfos.Select(parameterInfo => this.Resolve(parameterInfo.ParameterType)).ToArray(); return constructors[0].Invoke(parameters); } public IServiceRegister Register<T>() where T : class { Type type = typeof(T); string typeName = type.FullName; if (this.ServiceIsRegistered(typeName)) return this; var constructors = type.GetConstructors(); if (constructors.Length != 1) { throw new Exception(string.Format("注册类型必须至少要有一个构造函数. 目前这个 {0} 类型里面有 {1} 个构造函数", type.Name, constructors.Length.ToString())); } this._registrations.Add(typeName, type); return this; } }
如果你用过其他容器,你肯定知道该怎么使用了。
介绍一下微软的:程序集 Microsoft.Extensions.DependencyInjection.Abstractions
微软这个容器里管理对象的生命周期有三种方式: AddTransient、AddSingleton、AddScoped。
1、AddSingleton的生命周期:项目启动-项目关闭,相当于静态类,只会有一个。
2、AddScoped的生命周期:请求开始-请求结束,在这次请求中获取的对象都是同一个,B/S结构里最常使用的一种注册对象的方式。
3、AddTransient的生命周期:请求获取-(GC回收-主动释放)每一次获取的对象都不是同一个,相当于每次取出来都是新New出来的对象。
注意:
由于AddScoped对象是在请求的时候创建的,所以不能在AddSingleton对象中使用,甚至也不能在AddTransient对象中使用。
所以权重的依赖顺序为:AddSingleton→AddTransient→AddScoped。不然则会抛异常。
我们项目使用了Autofac这个容器,这个容器太灵活了,我就不在这里废话了,我只是介绍一下和上面的DependencyInjection相同之处:
Autofac这个容器里管理对象的生命周期有很多种方式,我只介绍和上面一样的三种方式:
1、InstancePerLifetimeScope的生命周期:和AddScoped一样,请求开始-请求结束,在这次请求中获取的对象都是同一个,B/S结构里最常使用的一种注册对象的方式。
2、SingleInstance的生命周期:和AddSingleton一样,项目启动-项目关闭,相当于静态类,只会有一个。
3、InstancePerDependency的生命周期:和AddTransient一样,请求获取-(GC回收-主动释放)每一次获取的对象都不是同一个,相当于每次取出来都是新New出来的对象。
同理权重的依赖顺序也和DependencyInjection是一样的。SingleInstance→InstancePerDependency→InstancePerLifetimeScope。不然则会抛异常。
不管容器是Autofac还是系统自带的ServiceCollection,我们见过最多的都是通过构造函数的方式得到注入到容器里面的对象,但是在java里是通过注解的方式得到容器里面的对象,.net如何才能做到类似java那种通过注解的方式得到呢?看代码
/// <summary> /// 从FromService中得到属性注入 /// </summary> [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] public class PropertyFromServiceAttribute : Attribute, IBindingSourceMetadata { public BindingSource BindingSource => BindingSource.Services; }
使用的时候通过Attribute就可以了,和java的@Resource或@Autowired是不是一样了呢?
[PropertyFromService] public SystemClass SystemClass { get; set; }
先休息一下
未完待续