在java的spring中有自動註入功能,使得代碼變得更加簡潔靈活,所以想把這個功能移植到c#中,接下來逐步分析實現過程 1.使用自動註入場景分析 在asp.net mvc中,無論是什麼代碼邏輯分層,最終的表現層為Controller層,所以我們註入點就是在Controller中,這裡我們需要替換默 ...
在java的spring中有自動註入功能,使得代碼變得更加簡潔靈活,所以想把這個功能移植到c#中,接下來逐步分析實現過程
1.使用自動註入場景分析
在asp.net mvc中,無論是什麼代碼邏輯分層,最終的表現層為Controller層,所以我們註入點就是在Controller中,這裡我們需要替換預設的ControllerFactory,掃描代碼中標記需要註入的對象,進行實例化註入
public class FastControllerFactory : DefaultControllerFactory { public override IController CreateController(RequestContext requestContext, string controllerName) { Type type = this.GetControllerType(requestContext, controllerName); Object obj = GetControllerInstance(requestContext, type); //Controller中標記AutoWired屬性的自動註入 List<FieldInfo> AutoWiredFieldList = type.GetRuntimeFields().Where(f => f.GetCustomAttribute(typeof(AutoWired)) != null).ToList(); foreach (FieldInfo field in AutoWiredFieldList) { field.SetValue(obj, InjectUtil.Container.Resolve(field.FieldType)); } return obj as IController; } }
FastControllerFactory就是我們自定義的一個Controller工廠,重寫CreateController方法,對標記了AutoWired這個自定義註解的變數,從Bean容器中取出實例進行賦值,同時我們還需要在Global文件中的Start方法中,進行預設工廠進行替換
ControllerBuilder.Current.SetControllerFactory(new FastControllerFactory());
2.IOC容器的實現
c#中的自定義容器有很多開源成熟的框架,例如AutoFac等,這裡我們是自己實現一個輕量級的版本
源碼地址:https://gitee.com/grassprogramming/FastIOC
這裡就重點說一下如何在asp.net mvc中的使用,首先我們需要對需要註入的Bean對象進行標記,這個標記就叫做Component,
在asp.net mvc Global文件中的Start方法中,我們需要將整個項目中需要自動註入的Bean加入到容器中
public class InjectUtil { public static ContainerBuilder Container; public static void Init() { Container = new ContainerBuilder(); //獲取所有程式集 var assemblies = System.Web.Compilation.BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToArray(); //註入所有Component組件 Container.RegisterAssemblyTypes(assemblies, typeof(Component),true); Container.Build(); } }
到這裡Controller層面的事項就已經完成了,接下來就需要在IOC容器中初始化Bean實例方法中進一步處理
private Object GetInstance(RegisterEntity Entity) { Object obj = null; if (Entity.IsEnableIntercept) { bool IsExtend = Entity.RealType == Entity.RegistType; obj = DynamictProxy.CreateProxyObject(Entity.RealType, Entity.RegistType, Entity.InterceptType, IsExtend, Entity.IsInterceptAllMethod); } else { var constructors = Entity.RegistType.GetConstructors(); obj = constructors[0].Invoke(new Object[] { }); } //這裡使用單例模式將實例化Instance存儲,提前暴露未進行後續設置的對象實例 if (!SingleInstanceDic.ContainsKey(Entity.RealType)) { SingleInstanceDic.Add(Entity.RealType, obj); } //如果這個class標記了Component,且有標記了AutoWired的Field,進行自動註入 if (Entity.RealType.GetCustomAttribute(typeof(Component), true) != null) { //這裡要使用GetRuntimeFields,此方法返回在指定類型上定義的所有欄位,包括繼承,非公共,實例和靜態欄位。 foreach (FieldInfo Field in Entity.RealType.GetRuntimeFields()) { if (Field.GetCustomAttribute(typeof(AutoWired), true) != null) { Type FieldType = Field.FieldType; if (Contains(FieldType)) { //判斷單例存儲中是否包含,如果有,取出賦值,這裡可以防止迴圈依賴導致的死迴圈 if (SingleInstanceDic.ContainsKey(FieldType)) { Field.SetValue(obj, SingleInstanceDic[FieldType]); } else { Field.SetValue(obj, Resolve(FieldType)); } } } } } return obj; }
GetInstance方法就是實例化Bean對象的核心方法,其實很簡單,就是通過反射創建對象,其中需要註意的有兩點
1)對於一個Bean初始化時需要掃描Bean中的所有變數,如果內部還有依賴註入的嵌套對象,需要使用遞歸,直到沒有需要註入的Field
2)我這裡使用的是單例模式,因為在測試過程中可能存在在A類中對B進行依賴註入,在B類中對A進行依賴註入,常規創建過程,如果使用遞歸進行掃描,就會進入死迴圈,記憶體溢出,所以使用對象的單例,一旦創建就放入字典中,如果再次掃描到該對象需要註入,則直接取出使用,就避免了迴圈引用
3.其他
對其他不在Controller中使用的類需要依賴註入,則需要直接從IOC的Bean容器取出使用
private AuthUtil @AuthUtil = InjectUtil.Container.Resolve<AuthUtil>();
功能到這裡就全部分析完畢了,最後打個廣告,自己寫的ASP.NET MVC快速開發框架,希望支持一波
地址:https://gitee.com/grassprogramming/FastExecutor