/// <summary> /// 从容器装配service /// </summary> [AppService] public class AutowiredService { IServiceProvider serviceProvider; public AutowiredService(IServiceProvider serviceProvider) { this.serviceProvider = serviceProvider; } public void Autowired(object service) { var serviceType = service.GetType(); //字段赋值 foreach (FieldInfo field in serviceType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { var autowiredAttr = field.GetCustomAttribute<AutowiredAttribute>(); if (autowiredAttr != null) { field.SetValue(service, serviceProvider.GetService(field.FieldType)); } } //属性赋值 foreach (PropertyInfo property in serviceType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { var autowiredAttr = property.GetCustomAttribute<AutowiredAttribute>(); if (autowiredAttr != null) { property.SetValue(service, serviceProvider.GetService(property.PropertyType)); } } } }
我们刚刚写的[AppService]特性在这里已经用上了,并且这个类使用构造器注入了IServiceProvider。Autowired(object service)方法的参数是要装配的服务实例,首先获取服务类型,再使用反射查询有AutowiredAttribute特性的字段和属性,我们在构造器注入了serviceProvider,这里便可以使用serviceProvider的GetService方法从容器中获取对应类型的实例来给字段和属性赋值。 整个过程就是这样,简单明了。开始的时候我想使用静态类来编写AutowiredService,但是静态类没法注入IServiceProvider,解决方法也有,可以使用定位器模式全局保存IServiceProvider:
/// <summary> /// 服务提供者定位器 /// </summary> public static class ServiceLocator { public static IServiceProvider Instance { get; set; } }
在Setup的Configure方法中赋值:
ServiceLocator.Instance = app.ApplicationServices;
这样在静态的AutowiredService中也就可以访问IServiceProvider了,但是使其自己也注册成服务能更好的和其他组件交互,java有了spring框架,大家都认可spring,一切都在容器中,一切都可注入,spring提供了统一的对象管理,非常好,我感觉netcore的将来也将会是这样。
Autowired(object service)方法的实现虽然简单,但是使用了效率底下的反射,这个美中不足需要改进,以前可以使用晦涩难懂的EMIT来编写,现在有Expression,编写和阅读都简单了好多,并且效率也不比EMIT差,所以我们使用表达式+缓存来改进。Autowired方法要做的就是从容器中取出合适的对象,然后赋值给service要自动装配的字段和属性,据此我们先编写出委托的伪代码:
(obj,serviceProvider)=>{ ((TService)obj).aa=(TAAType)serviceProvider.GetService(aaFieldType); ((TService)obj).bb=(TBBType)serviceProvider.GetService(aaFieldType); ... }
注意伪代码中的类型转换,Expression表达式在编译成委托时是非常严格的,所有转换都不能省。写表达式的时候我习惯先写伪代码,我希望大家也能养成这个习惯!有了伪代码我们可以开始改造AutowiredService类了:
/// <summary> /// 从容器装配service /// </summary> [AppService] public class AutowiredService { IServiceProvider serviceProvider; public AutowiredService(IServiceProvider serviceProvider) { this.serviceProvider = serviceProvider; } Dictionary<Type, Action<object, IServiceProvider>> autowiredActions = new Dictionary<Type, Action<object, IServiceProvider>>(); public void Autowired(object service) { Autowired(service, serviceProvider); } /// <summary> /// 装配属性和字段 /// </summary> /// <param></param> /// <param></param> public void Autowired(object service, IServiceProvider serviceProvider) { var serviceType = service.GetType(); if (autowiredActions.TryGetValue(serviceType, out Action<object, IServiceProvider> act)) { act(service, serviceProvider); } else { //参数 var objParam = Expression.Parameter(typeof(object), "obj"); var spParam = Expression.Parameter(typeof(IServiceProvider), "sp"); var obj = Expression.Convert(objParam, serviceType); var GetService = typeof(IServiceProvider).GetMethod("GetService"); List<Expression> setList = new List<Expression>(); //字段赋值 foreach (FieldInfo field in serviceType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { var autowiredAttr = field.GetCustomAttribute<AutowiredAttribute>(); if (autowiredAttr != null) { var fieldExp = Expression.Field(obj, field); var createService = Expression.Call(spParam, GetService, Expression.Constant(field.FieldType)); var setExp = Expression.Assign(fieldExp, Expression.Convert(createService, field.FieldType)); setList.Add(setExp); } } //属性赋值 foreach (PropertyInfo property in serviceType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { var autowiredAttr = property.GetCustomAttribute<AutowiredAttribute>(); if (autowiredAttr != null) { var propExp = Expression.Property(obj, property); var createService = Expression.Call(spParam, GetService, Expression.Constant(property.PropertyType)); var setExp = Expression.Assign(propExp, Expression.Convert(createService, property.PropertyType)); setList.Add(setExp); } } var bodyExp = Expression.Block(setList); var setAction = Expression.Lambda<Action<object, IServiceProvider>>(bodyExp, objParam, spParam).Compile(); autowiredActions[serviceType] = setAction; setAction(service, serviceProvider); } } }
代码一下子多了不少,不过由于我们前面的铺垫,理解起来也不难,至此自动装配字段和属性的服务已经写好了,下面看看如何使用:
编写服务类,并添加[AppService]特性
[AppService] public class MyService { //functions }
在Setup的ConfigureServices方法中注册应用服务
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); //注册应用服务 services.AddAppServices(); }
在其他类中注入使用,比如Controller中