当前分单例,做用域(范围),短暂。单例是整个服务中只有一个实例,短暂则是每一次获得的都是新的实例,做用域就是在该一套行动中内获得的是同一个实例,该行动中指的是什么?咱们看看demo下的startup里面一个方法html
using (var sc = x.ServiceLocator.BeginLifetimeScope()) { var serv = sc.Resolve<IUserService>(); sc.Resolve<IVCodeService>(); sc.Resolve<IUserService>(); sc.Resolve<IUserProxyService>(); sc.Resolve<Controllers.LoginController>(); var logger = sc.Resolve<ILoggerBuilder>().Build(typeof(Startup)); logger.Info("startup at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); }
这里using块代码就是咱们使用了一个做用域的例子,因此做用域应该是指一件事的整个过程(这件事里面拆分了几个子事件,每一个子事件又能够是一个做用域)。git
在web模式中,从beginreqeust到endrequest,咱们均可以认为从开始到结束的一种做用域,这就是web的周期。autofac对周期的描述:IoC之AutoFac(三)——生命周期github
easyioc中用了ILifetimeScopeTracker接口让使用者去管理做用域周期,好比想在web实现的begin+end周期,ILifetimeScope StartScope(ILifetimeScope parent)方法中返回的对象使用HttpContext.Item去管理就能够了。web
每一次使用都要开启一个做用域,将要释放资源的对象(实现了IDisposable 接口)放到ILifetimeScope的上下文的释放队列中,用于等下被调用方法释放。单例不会进入ILifetimeScope的释放队列中,而短暂 + 做用域的就有可能被加入到队列中(有可能对象没有实现IDisposable接口)。sql
/// <summary> /// 组件生命范围定义跟踪者 /// </summary> public interface ILifetimeScopeTracker { /// <summary> /// 开始一个范围 /// </summary> /// <param name="parent"></param> /// <returns></returns> ILifetimeScope StartScope(ILifetimeScope parent); /// <summary> /// 清空全部范围 /// </summary> void CleanScope(); }
当前easyioc中有 DefaultLifetimeScopeTracker,ThreadLifetimeScopeTracker,WebLifetimeScopeTracker三个做用域跟踪者。shell
#region ILifetimeScopeTracker /// <summary> /// 开始一个范围 /// </summary> /// <param name="parent"></param> /// <returns></returns> public virtual ILifetimeScope StartScope(ILifetimeScope parent) { return parent == null ? parent : parent.BeginLifetimeScope(); } /// <summary> /// 结束全部范围 /// </summary> public virtual void CleanScope() { } #endregion ILifetimeScopeTracker
能够看到,该对象始终都会开启范围,因为参数ILifetimeScope parent始终是系统ILifetimeScope第一个实例,每一次BeginLifetimeScope获得的对象都是新的一个ILifetimeScope实例。设计模式
private readonly System.Threading.ThreadLocal<ILifetimeScope> threadLocal = null; public override ILifetimeScope StartScope(ILifetimeScope parent) { if (this.threadLocal.IsValueCreated) return this.threadLocal.Value; return this.threadLocal.Value = base.StartScope(parent); } public override void CleanScope() { if (this.threadLocal.IsValueCreated && this.threadLocal.Value != null) { this.threadLocal.Value.Dispose(); this.threadLocal.Value = null; } base.CleanScope(); }
使用了System.Threading.ThreadLocal<T>去管理当前ILifetimeScope,跟名字同样,用在线程管理的场景,可是异步线程会有切换问题,能够看看AsyncLocal<T>的来源。api
public override ILifetimeScope StartScope(ILifetimeScope parent) { return new HttpThreadCache().Get("BeginLifetimeScope", () => base.StartScope(parent)); } public override void CleanScope() { var cache = new HttpThreadCache(); var scope = cache.Get<ILifetimeScope>("BeginLifetimeScope"); if (scope != null) scope.Dispose(); cache.Remove("BeginLifetimeScope"); base.CleanScope(); }
static HttpThreadCache() { asyncLocak = new AsyncLocal<IDictionary>(); init = new Func<IDictionary>(() => { #if NET461 if (HttpContext.Current == null) goto _do; if (HttpContext.Current.Items.Contains(key)) return System.Web.HttpContext.Current.Items[key] as Hashtable; var result = new Hashtable(); HttpContext.Current.Items[key] = result; return result; #else goto _do; #endif _do: { if (asyncLocak.Value == null) asyncLocak.Value = new Hashtable(); return asyncLocak.Value; } }); }
web周期的跟踪者,HttpThreadCached对象就是在framework中使用了上面说到的HttpContent.Item去管理,非framework则使用了System.Thread.AsyncLocal<T>去管理。framework下相对ThreadLifetimeScopeTracker无非就是将周期拉长而已数组
ioc.RegisterType<T,IT>(string key,lifestyle style) 像这样的方法注入了IT接口T实现的一个规则,key能够为空。在easyioc中还能够注入回调方法去构造对象缓存
/// <summary> /// 注册对象实例映射关系 /// </summary> /// <typeparam name="TService">服务类型</typeparam> /// <param name="mission">回调生成</param> /// <param name="key">key</param> /// <param name="lifeStyle">生命周期</param> /// <returns></returns> public void RegisterCallBack<TService>(string key, ComponentLifeStyle lifeStyle, Func<ILifetimeScope, TService> mission) { if (this.option.Value.Unabled) throw new InvalidException("the builder is builded,can not update rules"); var rule = new RegisterRuleCollector(1); rule.RegisterCallBack(key, lifeStyle, mission); register.Update(rule); }
全部的注册规则要遵照:
该对象的定义比较复杂,实际上你能够理解这里是保存了4个核心对象:T,IT,key,lifestyle。咱们上面ioc.RegisterType<T,IT>(string key,lifestyle style)方法用到的对象就是这个RegisterRule对象了。
/// <summary> /// 注册规则 /// </summary> public class RegisterRule : IEquatable<RegisterRule>, ICloneable, IDisposable, IRegisterRule, IParameterRegisterRule, IProxyRegisterRule, IRegisterRuleDescriptor { ..... }
private string ConcatCachedKey() { switch (this.lifeStyle) { case ComponentLifeStyle.Singleton: { return string.Concat("s", this.key, "_", increment); } case ComponentLifeStyle.Transient: { return string.Concat("t", this.key, "_", increment); } case ComponentLifeStyle.Scoped: { return string.Concat("l", this.key, "_", increment); } } return this.serviceType.FullName; }
在这里咱们加上key和style表示一些额外的信息,实际彻底能够用该Id去对比。
/// <summary> /// 参数注册规则 /// </summary> public interface IObviousProxyRegisterRule { /// <summary> /// 构造函数参数 /// </summary> /// <typeparam name="TService">服务类型</typeparam> /// <param name="key">注册key</param> /// <returns></returns> IObviousProxyRegisterRule WithParameter<TService>(string key); }
IProxyRegisterRule WithInterceptor<TInterceptor>(string key) where TInterceptor : Never.Aop.IInterceptor;
拦截器定义以下:
/// <summary> /// 拦截接口 /// </summary> public interface IInterceptor { /// <summary> /// 在对方法进行调用前 /// </summary> /// <param name="invocation">调用信息</param> void PreProceed(IInvocation invocation); /// <summary> /// 对方法进行调用后 /// </summary> /// <param name="invocation">调用信息</param> void PostProceed(IInvocation invocation); }
能够扩展一下:在webapi请求过程当中,对每一个方法调用进行监督其性能,可使用该特性注入性能监督拦截器
对每一条使用到的规则,去进行实例化的构建;RegisterRuleBuilder该对象分析规则的构造函数,找到适当的构造方法(含参数),使用emit去调用该构造而去实例目标对象(指的是规则里面的T目标),将构造好的方法缓存起来放到RegisterRule的Builder与OptionalBuilder这2个属性
/// <summary> /// 是否相容的周期 /// </summary> /// <param name="current">当前周期</param> /// <param name="target">目标周期</param> /// <returns></returns> public static string Compatible(this RegisterRule target, RegisterRule current) { switch (current.LifeStyle) { /*单例能够注入到任何实例中,其构造只能是单例对象*/ case ComponentLifeStyle.Singleton: { return string.Empty; } /*短暂只能注入到短暂,其构造可接受任何实例对象*/ case ComponentLifeStyle.Transient: { if (target.LifeStyle != ComponentLifeStyle.Transient) return string.Format("构建当前对象{0}为{1},指望对象{2}为短暂,不能相容", target.ServiceType.FullName, target.LifeStyle == ComponentLifeStyle.Scoped ? "做用域" : "单例", current.ServiceType.FullName); return string.Empty; } /*做用域其构造不能接受短暂,可接受有做用域和单例*/ case ComponentLifeStyle.Scoped: { if (target.LifeStyle == ComponentLifeStyle.Singleton) return string.Format("构建当前对象{0}为单例,指望对象{1}为做用域,不能相容", target.ServiceType.FullName, current.ServiceType.FullName); return string.Empty; } } return string.Empty; }
/*选择策略*/ if (level > 0) { /*递归检查*/ foreach (var re in recursion) { if (re.ImplementationType == rule.ImplementationType) { throw new ArgumentOutOfRangeException(string.Format("{0}和{1}类型造成递归调用", re.ImplementationType.FullName, rule.ImplementationType.FullName)); } } if (recursion[recursion.Count - 1] != null) { RuleMatchUsingNegativeSort(rule, recursion[recursion.Count - 1]); } }
代码中RuleMatchUsingNegativeSort是检查规则的相容性。
实际上叫容器是要在不一样场景的叫法,好比咱们的注册规则也要有个集合,保存着全部的规则,咱们也叫容器,而相对于整个系统来讲,注入Register,构建Resolve等全部组件组合起来,这也是容器(easyContainer的面貌)
/// <summary> /// IoC容器接口 /// </summary> public interface IContainer { /// <summary> /// 服务注册器 /// </summary> IServiceRegister ServiceRegister { get; } /// <summary> /// 服务定位器 /// </summary> IServiceLocator ServiceLocator { get; } /// <summary> /// 服务建立器 /// </summary> IServiceActivator ServiceActivator { get; } /// <summary> /// 类型发现者 /// </summary> ITypeFinder TypeFinder { get; } }
/// <summary> /// IoC容器 /// </summary> public class EasyContainer : Never.IoC.IContainer, Never.IoC.IContainerStartup, IValuableOption<UnableRegisterRule>
/// <summary> /// 更新容器规则 /// </summary> /// <param name="collector"></param> public void Update(RegisterRuleCollector collector) { if (option != null && option.Value.Unabled) return; .... }
并且相对接近使用者层的ServiceRegister对象,则是直接抛异常
public void RegisterType(Type implementationType, Type serviceType, string key, ComponentLifeStyle lifeStyle) { if (this.option.Value.Unabled) throw new InvalidException("the builder is builded,can not update rules"); var rule = new RegisterRuleCollector(1); rule.RegisterType(implementationType, serviceType, key, lifeStyle); register.Update(rule); }
还记得Autofac里面ContainerBuilder的Update方法?官方目前已经被标识为废弃方法,大伙能够讨论一下为何会这样。
可能懒惰的缘由,咱们不用每一次都手动注入<AA,IA>,<BA,IA>这种规则,因此咱们能够定义扫描程序集去找到AA,BA后注入。举个栗子,程序C我不想BA注入,程序D又想只用BA,程序E二者均可以,所以不一样环境下扫描程序集后想要注入AA和BA也要有策略。
假设<AA,IA>规则是单例 + 运行环境是"programc",<BA,IA>规则是做用域 + 运行环境是"programd",程序C的运行环境是“programc",规则扫描者是扫描单例的,而程序D运行环境是”programd",规则扫描者是扫描线程的,程序E的运行环境是""(可认为*,能够匹配全部环境),规则扫描者是扫描线程的+扫描单例的。在这三种环境中,能够得出,程序C环境匹配 + 单例扫描者扫描到AA,能够注入<AA,IA>,单例扫描者扫描不到BA这个类型(为何描述不到?一个是环境,一个是单例只匹配单例,不匹配做用域+短暂),因此不会注入<BA,IA>,程序E则能够注入<BA,IA>,<AA,IA>,而程序D自己环境不是”grogramc",直接环境不匹配<BA,IA>。系统默认实现了3个扫描者:
在代码中咱们发现IAutoInjectingEnvironmentRuleCollector定义,这个接口的做用是什么?里面方法Register参数中的collector是什么对象?
当前咱们有好几个IoC工具,第一种工具都有本身的实现方法,特别是其Container的核心设计,这个核心有些的方法咱们想用的话,构架就要将其暴露出去,只不过构架要抽象出来方便作适配,所以IAutoInjectingEnvironmentRuleCollector接口可让不一样的工具作适配工具而已。
何时会调用这个自动注入的接口方法?
void Call(AutoInjectingGroupInfo[] groups, IContainerStartupEventArgs eventArgs)
咱们先去看看扩展方法Never.StartupExtension.UseAutoInjectingAttributeUsingIoC方法,第二个参数接受的是IAutoInjectingEnvironmentProvider[] providers,一个数组,说明咱们环境能够有多个扫描规则者。
/// <summary> /// 在sampleioc中自动使用属性发现注入 /// </summary> /// <param name="startup"></param> /// <param name="providers"></param> /// <returns></returns> public static ApplicationStartup UseAutoInjectingAttributeUsingIoC(this ApplicationStartup startup, IAutoInjectingEnvironmentProvider[] providers) { startup.RegisterStartService(new AutoInjectingStartupService(providers)); return startup; }
跟踪到里面的AutoInjectingStartupService类型,咱们发现环境自动注入是使用了IContainerStartup接口的OnStarting事件,IContainerStartup接口则是定义了IContainer的启动过程,OnStarting事件一定是Container里面调用的,咱们也发现IContainerStartupEventArgs对象的属性Collector被设定为object类型,跟咱们上面说的IAutoInjectingEnvironmentRuleCollector接口方法Register参数的collector同样的设计。
/// <summary> /// 容器初始化过程事件 /// </summary> public class IContainerStartupEventArgs : EventArgs { /// <summary> /// 类型发现者 /// </summary> public ITypeFinder TypeFinder { get; } /// <summary> /// 程序集 /// </summary> public IEnumerable<Assembly> Assemblies { get; } /// <summary> /// app /// </summary> public object Collector { get; } }
实际上不管是OnIniting事件仍是OnStarting事件,咱们会将Collector对象设计为每种IoC技术方案的规则容器,好比Autofac的是Autofac.ContainerBuilder类型,StructureMap的是StructureMap.Container类型,都只是让使用者能够直接使用Autofac.ContainerBuilder或StructureMap.Container的友好特性而已,固然前提你要知道你当前使用的是Autofac,仍是StructureMap或者是EasyIoC。
若是我先使用autofac来替换easyioc怎么办?先去github下载never的扩展信息
咱们能够打开Never.IoC.Autofac项目代码发现,实际上也是实现了上面说到的IContainer,IServiceLocator,IServiceActivator,IServiceRegister,ILifetimeScope5个核心接口,而后在Startup对象中ApplicationStartup实例使用.UseAutofac()方法就能够了。
而环境的自动注入解决方案:实现IAutoInjectingEnvironmentRuleCollector接口,传入到TransientAutoInjectingEnvironmentProvider构造就能够了,当前组件要本身实现哦,看着Never下面的AutoInjectingEnvironmentCollector对象就能够了
文章导航: