.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(); }
结果如下: