目前这样子,对于我来说,基本已经够用了,因为在应用层,我都是依赖实现编程的😀(哈哈,会不会很多人说咦......呢?)。设计模式说:“要依赖于抽象,不要依赖于具体”,这点我还没做到,我抽空检讨(呵呵,谁信呢!)。所以呢,我们的批量注入要支持那些优秀的同学。
从上面的代码不难发现,如果定义接口IA和其实现A:IA,并在A上添加AppService特性是不行的:
public interface IA { } [AppService] public class A : IA { }
这个时候我们并不能依赖IA编程,因为我们注册的服务类是A,实现类是A,我们需要注册成服务类是IA,实现类是A才可:
public class HomeController : Controller { private IA a; public HomeController(IA a) { this.a = a; //这里a是null,不能使用 } }
让我继续改进,在AppServiceAttribute中,我们加入服务类型的信息:
[AttributeUsage(AttributeTargets.Class, Inherited = false)] public class AppServiceAttribute : Attribute { /// <summary> /// 生命周期 /// </summary> public ServiceLifetime Lifetime { get; set; } = ServiceLifetime.Singleton; /// <summary> /// 指定服务类型 /// </summary> public Type ServiceType { get; set; } /// <summary> /// 是否可以从第一个接口获取服务类型 /// </summary> public bool InterfaceServiceType { get; set; } = true; }
我们从两个方面入手来解决服务类型的问题,一个是指定ServiceType,这个就毫无疑问了,在A的AppService中可以明确指定IA为其服务类:
[AppService(ServiceType = typeof(IA))] public class A : IA { }
另一个是从服务类自身所继承的接口中获取服务类形,这一点要在AddAppServices方法中体现了,再次改进AddAppServices方法,还是替换最开始services.AddSingleton(type)的位置:
var serviceType = serviceAttribute.ServiceType; if (serviceType == null && serviceAttribute.InterfaceServiceType) { serviceType = type.GetInterfaces().FirstOrDefault(); } if (serviceType == null) { serviceType = type; } switch (serviceAttribute.Lifetime) { case ServiceLifetime.Singleton: services.AddSingleton(serviceType, type); break; case ServiceLifetime.Scoped: services.AddScoped(serviceType, type); break; case ServiceLifetime.Transient: services.AddTransient(serviceType, type); break; default: break; }
我们首先检查serviceAttribute.ServiceType,如果有值的话,它就是注册服务的类型,如果没有的话,看是否允许从接口中获取服务类型,如果允许,便尝试获取第一个作为服务类型,如果还没获取到,就把自身的类型作为服务类型。
第一种情况不常见,特殊情况才会指定ServiceType,因为写起来麻烦;
第二种情况适用于依赖抽象编程的同学,注意这里只取第一个接口的类型;
第三种情况就是适用于像我这种有不良习惯的患者(依赖实现编程)!
到此为止我们的服务注册已经讨论完了,下面看看如何获取。
字段和属性注入
这里我们说的获取,不是框架默认容器提供的构造器注入,而是要实现字段和属性注入,先看看构造器注入是什么样的:
public class HomeController : Controller { UserService userService; OrderService orderService; MsgService msgService; OtherService otherService; OtherService2 otherService2; public HomeController(UserService userService, OrderService orderService, MsgService msgService, OtherService otherService, OtherService2 otherService2) { this.userService = userService; this.orderService = orderService; this.msgService = msgService; this.otherService = otherService; this.otherService2 = otherService2; } }
如果引用的服务不再添加还好,如果编写边添加就太要命了,每次都要定义字段、在构造器方法签名中些添加参数、在构造器中赋值,便捷性和Spring的@autowired注解没法比,所以我们要虚心学习,创作更便捷的操作。
首先我们再定义个特性,叫AutowiredAttribute,虽然也是个标识,但是由于这个特性是用在字段或者属性上,所以只能用特性Attribute,而不能使用接口Interface,到这里我们又发现一点,使用接口作为标识的话,只能用在类、接口和结构中,而不能用在他们的成员上,毕竟接口的主要作用是定义一组方法契约(即抽象)!
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public class AutowiredAttribute : Attribute { }
这个特性里面什么也没有,主要是下面这个类,装配操作都在这里: