Loading

动手造轮子:实现一个简单的依赖注入(三) --- 支持属性注入

动手造轮子:实现一个简单的依赖注入(三) --- 支持属性注入

Intro

前面写了几篇依赖注入的文章,有兴趣的小伙伴可以参考文末 Reference 部分中的链接,一直有小伙伴希望增加属性注入的支持,昨天试着加了一下,思路很简单,在获取到服务实例之后检查实例中有没有需要注入的属性,如果有并且不为 null 就从服务容器中获取一个对应属性类型的实例

代码修改

FromServiceAttribute

完整的代码修改可以参考这个 commit https://github.com/WeihanLi/WeihanLi.Common/commit/91dc0b515d12e7c036771fba9419824cd0219544

首先我们需要增加一个 FromServiceAttribute 用来标识哪些属性需要注入,代码如下:

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
public sealed class FromServiceAttribute : Attribute
{
}

这里 AttributeTargets 除了属性之外增加了字段和参数,是想可能以后会用到,参数典型的应用场景就是类似于 asp.net core 里的 [FromServices] 用来实现方法注入参数

EnrichObject

增加了一个 EnrichObject 方法,用来在获取到服务实例之后,对服务实例做一些补充的配置,如我们要加的属性注入,如果我们要加字段注入等也可以在这个方法内完成,来看实现:

private object EnrichObject(object obj)
{
    if (null != obj)
    {
        // PropertyInjection
        var type = obj.GetType();
        foreach (var property in CacheUtil.TypePropertyCache.GetOrAdd(type, t => t.GetProperties())
            .Where(x => x.IsDefined(typeof(FromServiceAttribute))))
        {
            if (property.GetValueGetter()?.Invoke(obj) == null)
            {
                property.GetValueSetter()?.Invoke(
                    obj,
                    GetService(property.PropertyType)
                    );
            }
        }
    }

    return obj;
}

上面的逻辑就是获取这个 object 定义的所有需要注入的属性,如果属性的值不为 null 则,从服务容器中获取对应的服务实例,之所以要检查是不是null

上面的 CacheUtil.TypePropertyCache 是一个 Type 为 key,PropertyInfo 数组为 Value 的并发字典,用来缓存类型的属性

GetValueGetter/GetValueSetter 是 PropertyInfo 的扩展方法,利用表达式树和缓存提高属性 Get/Set 的效率

GetSertviceInstance

修改原来的 GetServiceInstance 方法为 GetServiceInstanceInternal,增加一个一样的方法,实现逻辑是在 GetServiceInstanceInternal 的基础上调用上面的 Enrich 方法来实现属性注入

More

虽然增加了属性注入的支持,但是还是不太推荐使用,从上面属性注入的代码中可以看得到,如果用不好很容易出现循环依赖的问题,而且用构造器注入的话依赖关系很清晰,分析方法的构造方法即可,如果要使用属性注入请谨慎使用

Reference

posted @ 2020-06-10 08:06  WeihanLi  阅读(596)  评论(0编辑  收藏