前言

本篇文章着重讲一下在.Net中Host主机的构建过程,依旧延续之前文章的思路,着重讲解其源码,如果有不知道有哪些用法的同学可以点击这里,废话不多说,咱们直接进入正题

Host构建过程

下图是我自己整理的Host构建过程以及里面包含的知识点我都以链接的形式放上来,大家可以看下图,大概了解下过程(由于知识点过多,所以只能分上下两张图了):

图中标识的相关知识点连接如下(ps:与编号对应):

以上就是笔者在源码阅读阶段,其总结的自我感觉重要的知识点在微软文档中的对应位置。

源码解析

这部分笔者根据上图中的四大块分别进行源码解析,可能篇幅比较长,其主要是对源代码增加了自己理解的注释,所以读者在阅读的过程中,要多注意源码中的注释(ps:展示出的代码不是全部代码,而只是重要代码哦,每个小节总结的点都是按照代码顺序解释)

初始化默认配置ConfigDefaultBuilder

public static IHostBuilder ConfigureDefaults(this IHostBuilder builder, string[] args)
{
//设置程序运行路径
builder.UseContentRoot(Directory.GetCurrentDirectory());
builder.ConfigureHostConfiguration(config =>
{
//添加获取环境变量的前缀
config.AddEnvironmentVariables(prefix: "DOTNET_");
//添加命令行参数
if (args is { Length: > 0 })
{
config.AddCommandLine(args);
}
}); builder.ConfigureAppConfiguration((hostingContext, config) =>
{
//宿主机环境信息
IHostEnvironment env = hostingContext.HostingEnvironment;
//是否在文件改变时重新加载,默认是True
bool reloadOnChange = GetReloadConfigOnChangeValue(hostingContext); //默认添加的配置文件(格外添加以环境变量为名称的文件)
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: reloadOnChange)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: reloadOnChange);
//如果是开发环境,并且应用程序的应用名称不是空字符串,则加载用户机密,默认true(主要是为了不同开发人员的配置不同)
if (env.IsDevelopment() && env.ApplicationName is { Length: > 0 })
{
var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
if (appAssembly is not null)
{
config.AddUserSecrets(appAssembly, optional: true, reloadOnChange: reloadOnChange);
}
} //这里再次执行是为了让环境变量和命令行参数的配置优先级提高(后加载的key/value获取时优先级最高)
//添加其他环境变量
config.AddEnvironmentVariables(); //添加命令行参数
if (args is { Length: > 0 })
{
config.AddCommandLine(args);
}
})
.ConfigureLogging((hostingContext, logging) =>
{
//判断操作系统
bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
if (isWindows)
{
//添加过滤规则,捕获warning日志
logging.AddFilter<EventLogLoggerProvider>(level => level >= LogLevel.Warning);
}
//获取Logging配置
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
//添加输出到控制台
logging.AddConsole();
//添加将debug日志输出到控制台
logging.AddDebug();
//添加写入的事件源
logging.AddEventSourceLogger(); if (isWindows)
{
//添加事件日志
logging.AddEventLog();
}
//添加链路追踪选项
logging.Configure(options =>
{
options.ActivityTrackingOptions =
ActivityTrackingOptions.SpanId |
ActivityTrackingOptions.TraceId |
ActivityTrackingOptions.ParentId;
});
})
.UseDefaultServiceProvider((context, options) =>
{
bool isDevelopment = context.HostingEnvironment.IsDevelopment();
//依赖注入相关校验
options.ValidateScopes = isDevelopment;
options.ValidateOnBuild = isDevelopment;
});
return builder;
}

源码总结:

  • 设置程序执行路径以及获取环境变量和加载命令行参数。
  • 根据环境变量加载appsettings.json,加载用户机密数据(仅开发环境)。
  • 接着又加载环境变量和命令行参数(这里为什么又加载了一次呢?是因为这它们执行的顺序是不一样的,而后加载的会覆盖前面加载的Key/Value,前面加载主要是确定当前运行的环境变量以及用户自定义的命令行参数,后面是为确保通过key获取value的时候能够获取到准确的值)。
  • 接下来就主要是配置默认Log,如果是开发环境,依赖注入相关的配置默认开启(验证scope是否被用于singleton,验证是否在调用期间就创建所有服务至缓存)。

初始化主机启动配置ConfigureWebHostDefaults

public GenericWebHostBuilder(IHostBuilder builder, WebHostBuilderOptions options)
{
_builder = builder;
var configBuilder = new ConfigurationBuilder()
.AddInMemoryCollection(); //添加以ASPNETCORE_开头的环境变量(ps:判断当前环境是那个环境)
if (!options.SuppressEnvironmentConfiguration)
{
configBuilder.AddEnvironmentVariables(prefix: "ASPNETCORE_");
}
//这里主要加载环境变量
_config = configBuilder.Build(); _builder.ConfigureHostConfiguration(config =>
{
//将上面的配置加载进来
config.AddConfiguration(_config); //通过配置和特性加载额外的Config(或者不加载配置),通过继承IHostingStartup无侵入性加载。
ExecuteHostingStartups();
});
//将上面Startup中Config的配置放到Build阶段加载
_builder.ConfigureAppConfiguration((context, configurationBuilder) =>
{
if (_hostingStartupWebHostBuilder != null)
{
var webhostContext = GetWebHostBuilderContext(context);
_hostingStartupWebHostBuilder.ConfigureAppConfiguration(webhostContext, configurationBuilder);
}
}); //增加注入的服务
_builder.ConfigureServices((context, services) =>
{
var webhostContext = GetWebHostBuilderContext(context);
var webHostOptions = (WebHostOptions)context.Properties[typeof(WebHostOptions)]; //注入一些其他服务
services.AddSingleton(webhostContext.HostingEnvironment);
services.AddSingleton((AspNetCore.Hosting.IHostingEnvironment)webhostContext.HostingEnvironment);
services.AddSingleton<IApplicationLifetime, GenericWebHostApplicationLifetime>(); services.Configure<GenericWebHostServiceOptions>(options =>
{
options.WebHostOptions = webHostOptions;
options.HostingStartupExceptions = _hostingStartupErrors;
}); services.TryAddSingleton(sp => new DiagnosticListener("Microsoft.AspNetCore"));
services.TryAddSingleton<DiagnosticSource>(sp => sp.GetRequiredService<DiagnosticListener>());
services.TryAddSingleton(sp => new ActivitySource("Microsoft.AspNetCore")); services.TryAddSingleton<IHttpContextFactory, DefaultHttpContextFactory>();
services.TryAddScoped<IMiddlewareFactory, MiddlewareFactory>();
services.TryAddSingleton<IApplicationBuilderFactory, ApplicationBuilderFactory>(); _hostingStartupWebHostBuilder?.ConfigureServices(webhostContext, services); //可以通过配置的方式查找程序集加载StartUp,但是默认只会加载最后一个StartUp
if (!string.IsNullOrEmpty(webHostOptions.StartupAssembly))
{
try
{
var startupType = StartupLoader.FindStartupType(webHostOptions.StartupAssembly, webhostContext.HostingEnvironment.EnvironmentName);
UseStartup(startupType, context, services);
}
catch (Exception ex) when (webHostOptions.CaptureStartupErrors)
{
var capture = ExceptionDispatchInfo.Capture(ex); services.Configure<GenericWebHostServiceOptions>(options =>
{
options.ConfigureApplication = app =>
{
capture.Throw();
};
});
}
}
});
}
internal static void ConfigureWebDefaults(IWebHostBuilder builder)
{
//提供.netCore 静态Web资产(ps:说实话这里不知道有什么用)
builder.ConfigureAppConfiguration((ctx, cb) =>
{
if (ctx.HostingEnvironment.IsDevelopment())
{
StaticWebAssetsLoader.UseStaticWebAssets(ctx.HostingEnvironment, ctx.Configuration);
}
});
//使用 Kestrel 配置反向代理
builder.UseKestrel((builderContext, options) =>
{
options.Configure(builderContext.Configuration.GetSection("Kestrel"), reloadOnChange: true);
})
.ConfigureServices((hostingContext, services) =>
{
//配置启动的Url
services.PostConfigure<HostFilteringOptions>(options =>
{
if (options.AllowedHosts == null || options.AllowedHosts.Count == 0)
{
var hosts = hostingContext.Configuration["AllowedHosts"]?.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
options.AllowedHosts = (hosts?.Length > 0 ? hosts : new[] { "*" });
}
});
services.AddSingleton<IOptionsChangeTokenSource<HostFilteringOptions>>(
new ConfigurationChangeTokenSource<HostFilteringOptions>(hostingContext.Configuration));
services.AddTransient<IStartupFilter, HostFilteringStartupFilter>();
//用来获取客户端的IP地址
if (string.Equals("true", hostingContext.Configuration["ForwardedHeaders_Enabled"], StringComparison.OrdinalIgnoreCase))
{
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
services.AddTransient<IStartupFilter, ForwardedHeadersStartupFilter>();
}
//添加路由配置
services.AddRouting();
})
//默认使用IIS
.UseIIS()
//使用进程内的托管模式
.UseIISIntegration();
}

这部分内容可能会多点,源码总结:

  • 添加Memory缓存,添加ASPNETCORE_开头的环境变量。
  • 根据用户的配置,来加载额外的StartUp中Config配置,但是它的参数是IWebHostBuilder,这部分可以参考微软文档StartUp的部分。
  • 如果有存在这些配置的话,则统一放到Build阶段加载。
  • 加载web主机需要的注入的服务,以及判断是否需要通过程序集来加载StartUp,并且添加一个程序启动时调用的服务(这里主要是构建HttpContext执行管道)。
  • 引用Kestrel,继承路由和IIS,并且默认使用进程内托管。
  • 加载用户自定义的其他配置,例如默认的调用UseStartup方法。

根据指定配置开始初始化主机Build

public class HostBuilder : IHostBuilder
{
private List<Action<IConfigurationBuilder>> _configureHostConfigActions = new List<Action<IConfigurationBuilder>>();
private List<Action<HostBuilderContext, IConfigurationBuilder>> _configureAppConfigActions = new List<Action<HostBuilderContext, IConfigurationBuilder>>();
private List<Action<HostBuilderContext, IServiceCollection>> _configureServicesActions = new List<Action<HostBuilderContext, IServiceCollection>>();
private List<IConfigureContainerAdapter> _configureContainerActions = new List<IConfigureContainerAdapter>();
private IServiceFactoryAdapter _serviceProviderFactory = new ServiceFactoryAdapter<IServiceCollection>(new DefaultServiceProviderFactory());
private bool _hostBuilt;
private IConfiguration _hostConfiguration;
private IConfiguration _appConfiguration;
private HostBuilderContext _hostBuilderContext;
private HostingEnvironment _hostingEnvironment;
private IServiceProvider _appServices;
private PhysicalFileProvider _defaultProvider; public IDictionary<object, object> Properties { get; } = new Dictionary<object, object>(); public IHostBuilder ConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate)
{
_configureHostConfigActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));
return this;
} public IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate)
{
_configureAppConfigActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));
return this;
} public IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate)
{
_configureServicesActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));
return this;
} public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory)
{
_serviceProviderFactory = new ServiceFactoryAdapter<TContainerBuilder>(factory ?? throw new ArgumentNullException(nameof(factory)));
return this;
} public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factory)
{
_serviceProviderFactory = new ServiceFactoryAdapter<TContainerBuilder>(() => _hostBuilderContext, factory ?? throw new ArgumentNullException(nameof(factory)));
return this;
} public IHostBuilder ConfigureContainer<TContainerBuilder>(Action<HostBuilderContext, TContainerBuilder> configureDelegate)
{
_configureContainerActions.Add(new ConfigureContainerAdapter<TContainerBuilder>(configureDelegate
?? throw new ArgumentNullException(nameof(configureDelegate))));
return this;
} public IHost Build()
{
//只能执行一次这个方法
if (_hostBuilt)
{
throw new InvalidOperationException(SR.BuildCalled);
}
_hostBuilt = true; using var diagnosticListener = new DiagnosticListener("Microsoft.Extensions.Hosting");
const string hostBuildingEventName = "HostBuilding";
const string hostBuiltEventName = "HostBuilt"; if (diagnosticListener.IsEnabled() && diagnosticListener.IsEnabled(hostBuildingEventName))
{
Write(diagnosticListener, hostBuildingEventName, this);
} //执行Host配置(应用程序执行路径,加载_dotnet环境变量,获取命令行参数,加载预配置)
BuildHostConfiguration();
//设置主机环境变量
CreateHostingEnvironment();
//构建HostBuilderContext实例
CreateHostBuilderContext();
//构建程序配置(加载appsetting.json,环境变量,命令行参数等)
BuildAppConfiguration();
//构造容器,注入服务
CreateServiceProvider(); var host = _appServices.GetRequiredService<IHost>();
if (diagnosticListener.IsEnabled() && diagnosticListener.IsEnabled(hostBuiltEventName))
{
Write(diagnosticListener, hostBuiltEventName, host);
} return host;
} private static void Write<T>(
DiagnosticSource diagnosticSource,
string name,
T value)
{
diagnosticSource.Write(name, value);
} private void BuildHostConfiguration()
{
IConfigurationBuilder configBuilder = new ConfigurationBuilder()
.AddInMemoryCollection(); foreach (Action<IConfigurationBuilder> buildAction in _configureHostConfigActions)
{
buildAction(configBuilder);
}
//本质是执行ConfigureProvider中的Load方法,加载对应配置
_hostConfiguration = configBuilder.Build();
} private void CreateHostingEnvironment()
{
//设置环境变量
_hostingEnvironment = new HostingEnvironment()
{
ApplicationName = _hostConfiguration[HostDefaults.ApplicationKey],
EnvironmentName = _hostConfiguration[HostDefaults.EnvironmentKey] ?? Environments.Production,
ContentRootPath = ResolveContentRootPath(_hostConfiguration[HostDefaults.ContentRootKey], AppContext.BaseDirectory),
}; if (string.IsNullOrEmpty(_hostingEnvironment.ApplicationName))
{
_hostingEnvironment.ApplicationName = Assembly.GetEntryAssembly()?.GetName().Name;
}
//程序运行路径
_hostingEnvironment.ContentRootFileProvider = _defaultProvider = new PhysicalFileProvider(_hostingEnvironment.ContentRootPath);
} private void CreateHostBuilderContext()
{
_hostBuilderContext = new HostBuilderContext(Properties)
{
HostingEnvironment = _hostingEnvironment,
Configuration = _hostConfiguration
};
} private void BuildAppConfiguration()
{
//对于已经加载过的配置不再重新加载
IConfigurationBuilder configBuilder = new ConfigurationBuilder()
.SetBasePath(_hostingEnvironment.ContentRootPath)
.AddConfiguration(_hostConfiguration, shouldDisposeConfiguration: true); //注意这里是AppConfig
foreach (Action<HostBuilderContext, IConfigurationBuilder> buildAction in _configureAppConfigActions)
{
buildAction(_hostBuilderContext, configBuilder);
}
_appConfiguration = configBuilder.Build();
//将新的配置赋值给config
_hostBuilderContext.Configuration = _appConfiguration;
} private void CreateServiceProvider()
{
var services = new ServiceCollection();
services.AddSingleton<IHostingEnvironment>(_hostingEnvironment);
services.AddSingleton<IHostEnvironment>(_hostingEnvironment);
services.AddSingleton(_hostBuilderContext);
services.AddSingleton(_ => _appConfiguration);
services.AddSingleton<IApplicationLifetime>(s => (IApplicationLifetime)s.GetService<IHostApplicationLifetime>());
services.AddSingleton<IHostApplicationLifetime, ApplicationLifetime>();
services.AddSingleton<IHostLifetime, ConsoleLifetime>();
services.AddSingleton<IHost>(_ =>
{
return new Internal.Host(_appServices,
_hostingEnvironment,
_defaultProvider,
_appServices.GetRequiredService<IHostApplicationLifetime>(),
_appServices.GetRequiredService<ILogger<Internal.Host>>(),
_appServices.GetRequiredService<IHostLifetime>(),
_appServices.GetRequiredService<IOptions<HostOptions>>());
});
services.AddOptions().Configure<HostOptions>(options => { options.Initialize(_hostConfiguration); });
services.AddLogging();
//主要加载额外注入的服务
foreach (Action<HostBuilderContext, IServiceCollection> configureServicesAction in _configureServicesActions)
{
configureServicesAction(_hostBuilderContext, services);
}
//这里返回object,主要是为了保留扩展,让用户自定义的依赖注入框架能够运行。
object containerBuilder = _serviceProviderFactory.CreateBuilder(services);
foreach (IConfigureContainerAdapter containerAction in _configureContainerActions)
{
containerAction.ConfigureContainer(_hostBuilderContext, containerBuilder);
} _appServices = _serviceProviderFactory.CreateServiceProvider(containerBuilder); if (_appServices == null)
{
throw new InvalidOperationException(SR.NullIServiceProvider);
}
//可能是想先把IConfiguration加载到内存中
_ = _appServices.GetService<IConfiguration>();
}
}

在上面的两个小单元可以看出,所有的构造是以委托的方式,最后都加载到HostBuilder内部的委托集合中,源码总结:

  • 加载环境变量和命令行参数。
  • 构建HostingEnvironment对象。
  • 构建HostBuilderContext对象,里面包含配置和执行环境。
  • 加载appsettings.json,环境变量和命令行参数等。
  • 注入一些必须的服务,加载日志配置,WebHost里面注入的服务,加载StartUp里面ConfigService里面的服务,以及其他的一些注入的服务,构建容器(最后那里获取IConfiguration猜测可能是想先缓存到根容器吧)。

运行主机Run

public async Task StartAsync(CancellationToken cancellationToken = default)
{
_logger.Starting(); using var combinedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _applicationLifetime.ApplicationStopping);
CancellationToken combinedCancellationToken = combinedCancellationTokenSource.Token; //应用程序启动和关闭事件
await _hostLifetime.WaitForStartAsync(combinedCancellationToken).ConfigureAwait(false); combinedCancellationToken.ThrowIfCancellationRequested();
_hostedServices = Services.GetService<IEnumerable<IHostedService>>();
//主要是执行一些后台任务,可以重写启动和关闭时要做的操作
foreach (IHostedService hostedService in _hostedServices)
{
//立即执行的任务,例如构建管道就是在这里
await hostedService.StartAsync(combinedCancellationToken).ConfigureAwait(false);
//执行一些后台任务
if (hostedService is BackgroundService backgroundService)
{
_ = TryExecuteBackgroundServiceAsync(backgroundService);
}
}
//通知应用程序启动成功
_applicationLifetime.NotifyStarted(); //程序启动
_logger.Started();
}

源码总结:

  • 监听程序的启动关闭事件。
  • 开始执行Hosted服务或者加载后台执行的任务。
  • 通过TaskCompletionSource来持续监听Token,hold住进程。

总结

通过模板来构建的.Net泛型主机,其实已经可以满足大部分的要求,并且微软保留大量扩展让用户来自定义,当然你也可以构建其他不同的主机类型(如:Web主机或者控制台程序启动项配置),想了解的可以点击这里

以上就是笔者通过阅读源码来分析的程序执行流程,因为篇幅问题没有把所有代码都放出来,实在是太多了,所以只放了部分代码,主要是想给阅读源码的同学在阅读的时候找到思路,可能会有一点错误,还请评论指正。

源码解析.Net中Host主机的构建过程的更多相关文章

  1. 源码解析Android中View的measure量算过程

    Android中的Veiw从内存中到呈现在UI界面上需要依次经历三个阶段:量算 -> 布局 -> 绘图,关于View的量算.布局.绘图的总体机制可参见博文< Android中View ...

  2. [源码解析] PyTorch 分布式(11) ----- DistributedDataParallel 之 构建Reducer

    [源码解析] PyTorch 分布式(11) ----- DistributedDataParallel 之 构建Reducer 目录 [源码解析] PyTorch 分布式(11) ----- Dis ...

  3. 源码解析.Net中IConfiguration配置的实现

    前言 关于IConfituration的使用,我觉得大部分人都已经比较熟悉了,如果不熟悉的可以看这里.因为本篇不准备讲IConfiguration都是怎么使用的,但是在源码部分的解读,网上资源相对少一 ...

  4. 源码解析.Net中DependencyInjection的实现

    前言 笔者的这篇文章和上篇文章思路一样,不注重依赖注入的使用方法,更加注重源码的实现,我尽量的表达清楚内容,让读者能够真正的学到东西.如果有不太清楚依赖注入是什么或怎么在.Net项目中使用的话,请点击 ...

  5. 源码解析.Net中Middleware的实现

    前言 本篇继续之前的思路,不注重用法,如果还不知道有哪些用法的小伙伴,可以点击这里,微软文档说的很详细,在阅读本篇文章前,还是希望你对中间件有大致的了解,这样你读起来可能更加能够意会到意思.废话不多说 ...

  6. Spark 源码解析 : DAGScheduler中的DAG划分与提交

    一.Spark 运行架构 Spark 运行架构如下图: 各个RDD之间存在着依赖关系,这些依赖关系形成有向无环图DAG,DAGScheduler对这些依赖关系形成的DAG,进行Stage划分,划分的规 ...

  7. QT源码解析(七)Qt创建窗体的过程,作者“ tingsking18 ”(真正的创建QPushButton是在show()方法中,show()方法又调用了setVisible方法)

    前言:分析Qt的代码也有一段时间了,以前在进行QT源码解析的时候总是使用ue,一个函数名在QTDIR/src目录下反复的查找,然后分析函数之间的调用关系,效率实在是太低了,最近总结出一个更简便的方法, ...

  8. 源码解析C#中PriorityQueue(优先级队列)的实现

    前言 前段时间看到有大佬对.net 6.0新出的PriorityQueue(优先级队列)数据结构做了解析,但是没有源码分析,所以本着探究源码的心态,看了看并分享出来.它不像普通队列先进先出(FIFO) ...

  9. multiprocessing 源码解析 更新中......

    一.参考链接 1.源码包下载·链接:   https://pypi.org/search/?q=multiprocessing+ 2.源码包 链接:https://pan.baidu.com/s/1j ...

随机推荐

  1. SpringBoot整合Guacamole教程

    前言 本文主要介绍的是SpringBoot如何整合Guacamole在浏览器是远程桌面的访问. Guacamole 介绍 Apache Guacamole 是一个无客户端远程桌面网关.它支持标准协议, ...

  2. 双击映射坚果云网盘并打开的AHK源代码

    双击映射坚果云网盘并打开的AHK源代码 #SingleInstance,force ;当此脚本已经运行时自动替换旧实例再次运行.#Persistent ;让脚本持久运行(即直到用户关闭或遇到 Exit ...

  3. csredis-in-asp.net core理论实战-哨兵模式-使用示例

    csredis 开源地址 https://github.com/2881099/csredis 续上篇 csredis-in-asp.net core理论实战-主从配置.哨兵模式 示例源码 https ...

  4. 2021字节跳动校招秋招算法面试真题解题报告--leetcode206 反转链表,内含7种语言答案

    206.反转链表 1.题目描述 反转一个单链表. 示例: 输入: 1->2->3->4->5->NULL输出: 5->4->3->2->1-> ...

  5. C++ //继承同名静态成员处理方式

    1 //继承同名静态成员处理方式 2 #include <iostream> 3 #include <string> 4 using namespace std; 5 6 cl ...

  6. 用 区间判断(if)来猜价格的高低

    1 #include <stdio.h> 2 #include <stdlib.h> 3 int main() 4 { 5 int price = 150; 6 int gue ...

  7. 在 CSS 中表示颜色的hex code方法和rgb方法

    hexadecimal code(十六进制编码),简写为 hex code. 我们通常使用 decimals,也就是十进制数字,它对每一位数字使用符号0到9来表示.Hexadecimals (或 he ...

  8. Java调用Zookeeper

    watch机制 Zookeeper watch是一种监听通知机制,可以随时监听一些数据的变化,从而实现数据的及时性. Zookeeper所有的读操作getData(), getChildren()和 ...

  9. 如何发送一个http请求—apipost

    API界面功能布局 API请求参数 Header 参数 你可以设置或者导入 Header 参数,cookie也在Header进行设置 Query 参数 Query 支持构造URL参数,同时支持 RES ...

  10. Spring全家桶--单数据源的配置

    前言 spring数据源的配置网络上有很多例子,这里我也来介绍一下单数据源配置的例子,基于SpringBoot的方式和原生的Spring的方式. 一.生成项目骨架(SpringBoot),运行一个简单 ...