.net core系列之《从源码对Configuration的底层运行机制进行分析》

通过对Configuration源代码的分析从而来自定义一个配置数据源

1、用反编译工具来看看AddJsonFile()这个方法究竟干了什么,源代码如下:

        public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, string path)
        {
            return builder.AddJsonFile(null, path, false, false);
        }
        
        public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, string path, bool optional)
        {
            return builder.AddJsonFile(null, path, optional, false);
        }
        
        public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange)
        {
            return builder.AddJsonFile(null, path, optional, reloadOnChange);
        }

首先我们可以看到这3个重载方法,它们都是调用了下面这个方法:

        public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, IFileProvider provider, string path, bool optional, bool reloadOnChange)
        {
            if (builder == null)
            {
                throw new ArgumentNullException("builder");
            }
            if (string.IsNullOrEmpty(path))
            {
                throw new ArgumentException(Microsoft.Extensions.Configuration.Json.Resources.Error_InvalidFilePath, "path");
            }
            return builder.AddJsonFile(delegate (JsonConfigurationSource s) {
                s.FileProvider = provider;
                s.Path = path;
                s.Optional = optional;
                s.ReloadOnChange = reloadOnChange;
                s.ResolveFileProvider();
            });
        }

而这个方法呢,又是调用了下面这个方法:

        public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, Action<JsonConfigurationSource> configureSource)
        {
            return builder.Add<JsonConfigurationSource>(configureSource);
        }

从上面的方法可以看出,类型参数为 JsonConfigurationSource 然后返回IConfigurationBuilder的扩展方法 Add<TSource>(this IConfigurationBuilder builder, Action<TSource> configureSource)  

代码如下:

        public static IConfigurationBuilder Add<TSource>(this IConfigurationBuilder builder, Action<TSource> configureSource) where TSource: IConfigurationSource, new()
        {
            TSource source = Activator.CreateInstance<TSource>();
            if (configureSource != null)
            {
                configureSource(source);
            }
            return builder.Add(source);
        }

能过这个方法,创建 JsonConfigurationSource 的一个实例,最后返回 IConfigurationBuilder  中的 Add(IConfigurationSource source) 方法,接下来我们来看看在这个方法里都干了什么吧

        public IConfigurationBuilder Add(IConfigurationSource source)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            this.Sources.Add(source);
            return this;
        }

        public IList<IConfigurationSource> Sources
        {
            [CompilerGenerated]
            get
            {
                return this.<Sources>k__BackingField;
            }
        }

通过以上代码我知道了,AddJsonFile()这个方法,创建了一个 JsonConfigurationSource 的一个实例,并将AddJsonFile()中的参数给了这个对象,然后将这个对象存到一个集合中。

2、接下来我们来看看Build()这个方法干了什么:

        public IConfigurationRoot Build()
        {
            List<IConfigurationProvider> list = new List<IConfigurationProvider>();
            using (IEnumerator<IConfigurationSource> enumerator = this.Sources.GetEnumerator())
            {
                while (enumerator.MoveNext())
                {
                    IConfigurationProvider provider = enumerator.Current.Build(this);
                    list.Add(provider);
                }
            }
            return new ConfigurationRoot((IList<IConfigurationProvider>) list);
        }

这个方法通过遍历你通过AddJsonFile()方法存放进去的 IConfigurationSource 的集合,然后通过调用 IConfigurationSource 的实现类的 Build方法 ,我们来看看这个方法是怎样的:

        public override IConfigurationProvider Build(IConfigurationBuilder builder)
        {
            base.EnsureDefaults(builder);
            return new JsonConfigurationProvider(this);
        }

这个方法返回一个 IConfigurationProvider  对象,被赋给了 List<IConfigurationProvider>  集合,接下来我们来看看 JsonConfigurationProvider 这个类里面有什么

        public override void Load(Stream stream)
        {
            try
            {
                base.Data = JsonConfigurationFileParser.Parse(stream);
            }
            catch (JsonReaderException exception)
            {
                string str = string.Empty;
                if (stream.get_CanSeek())
                {
                    stream.Seek((long) 0L, (SeekOrigin) SeekOrigin.Begin);
                    using (StreamReader reader = new StreamReader(stream))
                    {
                        IEnumerable<string> fileContent = ReadLines(reader);
                        str = RetrieveErrorContext(exception, fileContent);
                    }
                }
                throw new FormatException(Microsoft.Extensions.Configuration.Json.Resources.FormatError_JSONParseError((int) exception.LineNumber, str), exception);
            }
        }

我们看到了这里面有一个Load()方法,这个方法就是用来加载文件中的数据的。

然后我们接着分析上面还没分析的  return new ConfigurationRoot((IList<IConfigurationProvider>) list); 这段代码

这段代码返回了一个 ConfigurationRoot 对象,还将我们所有的 IConfigurationProvider 给传了进行,它想干嘛呢?

没错,上面虽然有Load()方法,但是没人调用啊。 ConfigurationRoot  这个类就是干这个的。

        public ConfigurationRoot(IList<IConfigurationProvider> providers)
        {
            if (providers == null)
            {
                throw new ArgumentNullException("providers");
            }
            this._providers = providers;
            foreach (IConfigurationProvider p in providers)
            {
                p.Load();
                ChangeToken.OnChange(delegate {
                    return p.GetReloadToken();
                }, delegate {
                    this.RaiseChanged();
                });
            }
        }

通过以上代码的分析我们知道了Configuration的底层运行机制

3、那么接下来我们来自定义一个获取自定义的文件的Configuration,能过以上代码我们可以分析出有几个重要的对象

  1、JsonConfigurationSource:最后从这个对象的基类的Data属性中取值

  2、JsonConfigurationProvider:这个里面的Load()方法用来加载文件数据

  3、JsonConfigurationExtensions:这个是用来定义AddJsonFile()方法的

我们规定我们的文件后缀为.custom

CustomConfigurationSource类代码

    public class CustomConfigurationSource : FileConfigurationSource
    {
        private string path = string.Empty;
        public CustomConfigurationSource(string path)
        {
            this.path = path;
        }
        public override IConfigurationProvider Build(IConfigurationBuilder builder)
        {
            return new CustomConfigurationProvider(this.path);
        }
    }

CustomConfigurationProvider类代码

    public class CustomConfigurationProvider : ConfigurationProvider
    {
        private string path = string.Empty;
        public CustomConfigurationProvider(string path)
        {
            this.path = path;
        }
        public override void Load()
        {
            var lines = System.IO.File.ReadAllLines(this.path);
            var dict = new Dictionary<string, string>();
            foreach (var item in lines)
            {
                var items = item.Split("=");
                dict.Add(items[0], items[1]);
            }
            base.Data = dict;
        }
    }

CustomConfigurationExtensions类代码 :

    public static class CustomConfigurationExtensions
    {
        public static IConfigurationBuilder AddCustomFile(this IConfigurationBuilder builder, string path)
        {
            return builder.Add(new CustomConfigurationSource(path));
        }
    }

最终调用代码:

        static void Main(string[] args)
        {
            IConfiguration configuration = new ConfigurationBuilder()
                .SetBasePath(Environment.CurrentDirectory)
                .AddCustomFile("1.custom")
                .Build();;
            var info = configuration["host"];
            Console.WriteLine(info);
            Console.ReadKey();
        }

结果如下:

posted @ 2018-09-25 21:48  黄厚镇  阅读(780)  评论(2编辑  收藏  举报