當前分單例,做用域(範圍),短暫。單例是整個服務中只有一個實例,短暫則是每一次獲得的都是新的實例,做用域就是在該一套行動中內獲得的是同一個實例,該行動中指的是什麼?咱們看看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對象就能夠了
文章導航: