一題多解,ASP.NET Core應用啟動初始化的N種方案[上篇]

ASP.NET Core應用本質上就是一個由中間件構成的管道,承載系統將應用承載于一個托管進程中運行起來,其核心任務就是將這個管道構建起來。在ASP.NET Core的發展歷史上先后出現了三種應用承載的編程方式,而且后一種編程模式都提供了針對之前編程模式的全部或者部分兼容,這就導致了一種現象:相同的功能具有N種實現方式。對這個發展歷程不是特別了解的讀者會有很多疑問?為什么這么多不同的編程模式都在做同一件事?它們之間的有什么差別之處?為什么有的API在最新的Minimal API又不能用了呢?[本文部分內容來源于《ASP.NET Core 6框架揭秘》第15章]

目錄
一、應用承載過程中需要哪些初始化工作?
二、第一代應用承載模型
???? 基本編程模式
???? 利用環境變量和命令行參數
??? 承載環境設置方法
??? 使用Startup類型
三、第二代應用承載模型
???? 基本編程模式
???? 承載環境設置方法
???? 針對IWebHostBuilder的適配
??? Startup構造函數注入的限制

一、應用承載過程中需要哪些初始化工作?

我們所謂的應用承載(Hosting)本就是將一個ASP.NET Core應用在一個具體的進程(Self-Host進程、IIS工作進程或者Windows Service進程等)中被啟動的過程,在這個過程中需要利用提供的API完成一些必要的初始化工作。由于ASP.NET Core應用本質上就是一個由中間件構成的管道,所有整個初始化過程的目的就是為了構建這一中間件管道,毫不夸張地說,構建的中間件管道就是“應用”本身,所以“中間件注冊”是最為核心的初始化工作。由于依賴注入的廣泛應用,中間件的功能基本都依賴于注入的服務來完成,所以將依賴服務注冊到依賴注入框架是另一項核心的初始化工作。

和任何類型的應用一樣,ASP.NET Core同樣需要通過配置來動態改變其運行時行為,所以針對配置的設置也是并不可少的。一個ASP.NET Core應用的配置分為兩類,一種是用在中間件管道構建過程中,也就是應用承載過程中,我們將其稱為“承載配置(Hosting Configuration)”。另一類配置則被用來控制中間件管道處理請求的行為,正如上面所說,中間件管道就是應用本身,所以這類配置被稱為應用配置(App Configuration)。承載配置中有一個重要的組成部分,那就是描述當前的承載環境(Hosting Environment),比如應用的標識、部署環境的名稱、存放內容文件和Web資源的目錄等。承載配置最終會合并到應用配置中。

綜上所示,ASP.NET Core應用承載的編程模型主要完成如下幾種初始化工作,這些工作都具有N種實現方法。在接下來的內容中,我們將逐個介紹在三種不同的應用承載方式中,這些功能都有哪些實現方式。

  • 中間件注冊

  • 服務注冊

  • 承載配置的設置

  • 應用配置的設置

  • 承載環境的設置

二、第一代應用承載模型

ASP.NET Core 1.X/2.X采用的承載模型以如下圖所示的IWebHostBuilder和IWebHost為核心。IWebHost對象代表承載Web應用的宿主(Host),管道隨著IWebHost對象的啟動被構建出來。IWebHostBuilder對象作為宿主對象的構建者,我們針對管道構建的設置都應用在它上面。

ffa8897c7c4d81bb582c4671f4765f06.png

基本編程模式

現在我們將針對上述5種初始化設置放在一個簡單的演示實例中。該演示實例會注冊如下這個FoobarMiddleware中間件,后者利用注入的IHandler服務完成請求的處理工作。作為IHandler接口的默認實現類型,Handler利用構造函數注入的IOptions<FoobarbazOptions>對象得到配置選項FoobarbazOptions,并將其內容作為請求的響應。

public?class?FoobarMiddleware
{private?readonly?RequestDelegate?_next;public?FoobarMiddleware(RequestDelegate?_)?{?}public?Task?InvokeAsync(HttpContext?httpContext,?IHandler?handler)?=>?handler.InvokeAsync(httpContext);
}public?interface?IHandler
{Task?InvokeAsync(HttpContext?httpContext);
}public?class?Handler?:?IHandler
{private?readonly?FoobarbazOptions?_options;private?readonly?IWebHostEnvironment?_environment;public?Handler(IOptions<FoobarbazOptions>?optionsAccessor,?IWebHostEnvironment?environment){_options?=?optionsAccessor.Value;_environment?=?environment;}public?Task?InvokeAsync(HttpContext?httpContext){var?payload?=?@$"
Environment.ApplicationName:?{_environment.ApplicationName}
Environment.EnvironmentName:?{_environment.EnvironmentName}
Environment.ContentRootPath:?{_environment.ContentRootPath}
Environment.WebRootPath:?{_environment.WebRootPath}
Foo:?{_options.Foo}
Bar:?{_options.Bar}
Baz:?{_options.Baz}
";return?httpContext.Response.WriteAsync(payload);}
}public?class?FoobarbazOptions
{public?string?Foo?{?get;?set;?}?=?default!;public?string?Bar?{?get;?set;?}?=?default!;public?string?Baz?{?get;?set;?}?=?default!;
}

我們會利用與當前“承載環境”對應配置來綁定配置選項FoobarbazOptions,后者的三個屬性分別來源于三個獨立的配置文件。其中settings.json被所有環境共享,settings.dev.json針對名為“dev”的開發環境。我們為承載環境提供更高的要求,在環境基礎上進步劃分子環境,settings.dev.dev1.json針對的就是dev下的子環境dev1。針對子環境的設置需要利用上述的承載配置來提供。

f658e235facba65aeb969237a48d488f.png

如下所示的就是上述三個配置文件的內容。如果當前環境和子環境分別為dev和dev1,那么配置選項FoobarbazOptions的內容將來源于這三個配置文件。細心的朋友可能還注意到了:我們并沒有放在默認的根目錄下,而是放在創建的resources目錄下,這是因為我們需要利用針對承載環境的設置改變ASP.NET Core應用存放內容文件和Web資源文件的根目錄。

settings.json
{"Foo":?"123"
}settings.dev.json
{"Bar":?"abc"
}settings.dev.dev1.json
{"Baz":?"xyz"
}

如下的應用承載程序涵蓋了上述的5種初始化操作。中間件的注冊通過調用IWebHostBuilder的Configure方法來完成,該方法的參數類型為Action<IApplicationBuilder>,中間件就是通過調用UseMiddleware<TMiddleware>方法注冊到IApplicationBuilder對象上。IWebHostBuilder并未對承載配置定義專門的方法,但是我們可以利用UseSettings方法以鍵值對的形式對其進行設置,這里我們采用這種方式完成了針對“環境”、“內容文件根目錄”、“Web資源文件根目錄”和“子環境”的設置,前三個是“承載環境”的三個重要屬性。承載配置最終會體現到表示承載上下文的WebHostBuilderContext對象上。

using?App;
new?WebHostBuilder().UseKestrel().UseSetting(WebHostDefaults.EnvironmentKey,"dev").UseSetting(WebHostDefaults.ContentRootKey?,?Path.Combine(Directory.GetCurrentDirectory(),?"resources")).UseSetting(WebHostDefaults.WebRootKey,?Path.Combine(Directory.GetCurrentDirectory(),?"resources",?"web")).UseSetting("SubEnvironment",?"dev1").ConfigureAppConfiguration((context,?configBuilder)?=>?configBuilder.AddJsonFile(path:?"settings.json",?optional:?false).AddJsonFile(path:?$"settings.{context.HostingEnvironment.EnvironmentName}.json",?optional:?true).AddJsonFile(path:?$"settings.{context.HostingEnvironment.EnvironmentName}.{context.Configuration["SubEnvironment"]}.json",?optional:?true)).ConfigureServices((context,?services)?=>?services.AddSingleton<IHandler,?Handler>().Configure<FoobarbazOptions>(context.Configuration)).Configure(app?=>?app.UseMiddleware<FoobarMiddleware>()).Build().Run();

依賴服務利用IWebHostBuilder的ConfigureServices方法進行注冊,該方法的參數類型為Action<WebHostBuilderContext, IServiceCollection>,意味著我們可以針對之前提供的承載配置(比如承載環境)進行針對性的服務注冊。在這里我們不僅注冊了依賴服務Handler,還利用當前配置對配置選項FoobarbazOptions實施了綁定。應用配置通過專門的方法ConfigureAppConfiguration進行設置,該方法的參數類型為Action<WebHostBuilderContext, IConfigurationBuilder>,意味著承載配置依然可以利用WebHostBuilderContext上下文獲取到,這里我們這是利用它得到對當前環境匹配的三個配置文件。程序啟動后,請求可以得到如下的響應內容。

fbf54fe76867916f3dcfa25589c02ad5.png

利用環境變量和命令行參數

由于ASP.NET Core應用在啟動時會使用前綴為“ASPNETCORE_”的環境變量作為承載配置,所以上述利用UseSettings方法針對承載配置的設置都可以按照如下的方式利用環境變量代替。

Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT",?"dev");
Environment.SetEnvironmentVariable("ASPNETCORE_SUBENVIRONMENT",?"dev1");
Environment.SetEnvironmentVariable("ASPNETCORE_CONTENTROOT",?Path.Combine(Directory.GetCurrentDirectory(),?"resources"));
Environment.SetEnvironmentVariable("ASPNETCORE_WEBROOT",?Path.Combine(Directory.GetCurrentDirectory(),?"resources",?"web"));WebHost.CreateDefaultBuilder(args).ConfigureAppConfiguration((context,?configBuilder)?=>?configBuilder.AddJsonFile(path:?"settings.json",?optional:?false).AddJsonFile(path:?$"settings.{context.HostingEnvironment.EnvironmentName}.json",?optional:?true).AddJsonFile(path:?$"settings.{context.HostingEnvironment.EnvironmentName}.{context.Configuration["SubEnvironment"]}.json",?optional:?true)).ConfigureServices((context,?services)?=>?services.AddSingleton<IHandler,?Handler>().Configure<FoobarbazOptions>(context.Configuration)).Configure(app?=>?app.UseMiddleware<FoobarMiddleware>()).Build().Run();

上面的代碼片段并沒有直接創建WebHostBuilder對象,而是調用WebHost的靜態方法CreateDefaultBuilder方法創建了一個具有默認配置的IWebHostBuilder對象。由于該方法傳入了命令行參數args,它會將命令行參數作為承載配置源之一,所以程序中四個針對承載配置選項也可以利用命令行參數來完成。

承載環境設置方法

其實承載環境(環境名稱、內容文件根目錄和Web資源文件根目錄)具有專門的方法,所以最方便的還是直接按照如下的方式調用這些方法對它們進行設置。對于我們演示的實例來說,針對環境名稱、內容文件和Web資源文件根目錄的設置可以直接調用IWebHostBuilder的UseEnvironment、UseContentRoot和UseWebRoot擴展方法來完成。

WebHost.CreateDefaultBuilder(args).UseEnvironment("dev").UseContentRoot(Path.Combine(Directory.GetCurrentDirectory(),?"resources")).UseWebRoot(Path.Combine(Directory.GetCurrentDirectory(),?"resources",?"web")).UseSetting("SubEnvironment",?"dev1").ConfigureAppConfiguration((context,?configBuilder)?=>?configBuilder.AddJsonFile(path:?"settings.json",?optional:?false).AddJsonFile(path:?$"settings.{context.HostingEnvironment.EnvironmentName}.json",?optional:?true).AddJsonFile(path:?$"settings.{context.HostingEnvironment.EnvironmentName}.{context.Configuration["SubEnvironment"]}.json",?optional:?true)).ConfigureServices((context,?services)?=>?services.AddSingleton<IHandler,?Handler>().Configure<FoobarbazOptions>(context.Configuration)).Configure(app?=>?app.UseMiddleware<FoobarMiddleware>()).Build().Run();

使用Startup類型

為了不讓應用承載程序代碼顯得過于臃腫,我們一般都會將服務注冊和中間件注冊移到按照約定定義的Startup類型中。如下面的代碼片段所示,中間件和服務注冊分別實現在Startup類型的ConfigureServices和Configure方法中,我們直接在構造函數中注入IConfiguration對象得到承載配置對象。值得一提,對于第一代應用承載方式,我們可以在Startup類型的構造函數中注入通過調用IWebHostBuilder的ConfigureServices方法注冊的任何服務(包括ASP.NET Core內部通過調用這個方法注冊的服務,比如本例的IConfiguration對象)。Startup類型只需要調用IWebHostBuilder的UseStartup<TStartup>擴展方法進行注冊即可。

WebHost.CreateDefaultBuilder(args).UseEnvironment("dev").UseContentRoot(Path.Combine(Directory.GetCurrentDirectory(),?"resources")).UseWebRoot(Path.Combine(Directory.GetCurrentDirectory(),?"resources",?"web")).UseSetting("SubEnvironment",?"dev1").ConfigureAppConfiguration((context,?configBuilder)?=>?configBuilder.AddJsonFile(path:?"settings.json",?optional:?false).AddJsonFile(path:?$"settings.{context.HostingEnvironment.EnvironmentName}.json",?optional:?true).AddJsonFile(path:?$"settings.{context.HostingEnvironment.EnvironmentName}.{context.Configuration["SubEnvironment"]}.json",?optional:?true)).UseStartup<Startup>().Build().Run();public?class?Startup
{public?IConfiguration?Configuration?{?get;?}public?Startup(IConfiguration?configuration)?=>?Configuration?=?configuration;public?void?ConfigureServices(IServiceCollection?services)?=>?services.AddSingleton<IHandler,?Handler>().Configure<FoobarbazOptions>(Configuration);public?void?Configure(IApplicationBuilder?app)?=>?app.UseMiddleware<FoobarMiddleware>();
}

三、第二代應用承載模型

除了承載Web應用,我們還有很多針對后臺服務(比如很多批處理任務)的承載需求,為此微軟推出了以IHostBuilder/IHost為核心的服務承載系統。Web應用本身實際上就是一個長時間運行的后臺服務,我們完全可以將應用定義成一個IHostedService服務,該類型就是下圖所示的GenericWebHostService。如果將上面介紹的稱為第一代應用承載模式的話,這就是第二代承載模式。

f07756ebe1ddc8b69e4ab5873b9c97da.png

基本編程模式

和所有的Builder模式一樣,絕大部分API都落在作為構建者的IHostBuilder接口上,服務注冊、承載配置、應用配置都具有對應的方法。由于中間件隸屬于GenericWebHostService這一單一的承載服務,所以只能記住與IWebHostBuilder。如果采用第二代應用承載模型,上面演示的程序可以改寫成如下的形式。

Host.CreateDefaultBuilder().ConfigureHostConfiguration(config?=>?config.AddInMemoryCollection(new?Dictionary<string,?string>?{[WebHostDefaults.EnvironmentKey]?=?"dev",[WebHostDefaults.ContentRootKey]?=?Path.Combine(Directory.GetCurrentDirectory(),?"resources"),[WebHostDefaults.WebRootKey]?=?Path.Combine(Directory.GetCurrentDirectory(),?"resources","web"),["SubEnvironment"]?=?"dev1"})).ConfigureAppConfiguration((context,?configBuilder)?=>?configBuilder.AddJsonFile(path:?"settings.json",?optional:?false).AddJsonFile(path:?$"settings.{context.HostingEnvironment.EnvironmentName}.json",?optional:?true).AddJsonFile(path:?$"settings.{context.HostingEnvironment.EnvironmentName}.{context.Configuration["SubEnvironment"]}.json",?optional:?true)).ConfigureServices((context,services)?=>?services.AddSingleton<IHandler,?Handler>().Configure<FoobarbazOptions>(context.Configuration)).ConfigureWebHost(webHostBuilder?=>?webHostBuilder.Configure(app=>app.UseMiddleware<FoobarMiddleware>())).Build().Run();

如上面的代碼片段所示,我們通過調用Host的靜態方法CreateDefaultBuilder方法創建一個具有默認配置的IHostBuidler對象。IHostBuilder為承載配置的設置提供了獨立的ConfigureHostConfiguration方法,該方法的參數類型為Action<IConfigurationBuilder>,我們演示的例子利用這個方法注冊了一個基于內存字典的配置源,承載環境(環境名稱、內容文件和Web資源文件根目錄)和子環境名稱在這里進行了設置。針對應用配置的設置通過ConfigureAppConfiguration方法來完成,該方法的參數類型為Action<HostBuilderContext, IConfigurationBuilder>,代表承載上下文的HostBuilderContext可以得到預先設定的承載環境和承載配置,我們的例子利用到定位與當前環境相匹配的配置文件。

IHostBuilder同樣定義了ConfigureServices方法,該方法的參數類型為Action<HostBuilderContext, IServiceCollection>,意味著服務依然可以針對承載環境和承載配置進行注冊。由于中間件的注冊依然落在IWebHostBuilder上,所以IHostBuilder提供了ConfigureWebHost/ConfigureWebHostDefaults這兩個擴展方法予以適配,它們具有一個類型為Action<IWebHostBuilder>的參數。

承載環境設置方法

和IWebHostBuilder一樣,IHostBuidler同樣提供了用來直接設置承載環境的方法。對于我們演示的實例來說,針對環境名稱、內容文件和Web資源文件根目錄的設置可以直接調用IHostBuidler的UseEnvironment、UseContentRoot和UseWebRoot擴展方法來完成。由于Web資源文件并未“服務承載”的范疇,所以針對Web資源文件根目錄的設置還得采用直接設置承載配置的方式(或者調用IWebHostBuilder的UseWebRoot擴展方法)。

Host.CreateDefaultBuilder().UseEnvironment("dev").UseContentRoot(Path.Combine(Directory.GetCurrentDirectory(),?"resources")).ConfigureHostConfiguration(config?=>?config.AddInMemoryCollection(new?Dictionary<string,?string>?{[WebHostDefaults.WebRootKey]?=?Path.Combine(Directory.GetCurrentDirectory(),?"resources","web"),["SubEnvironment"]?=?"dev1"})).ConfigureAppConfiguration((context,?configBuilder)?=>?configBuilder.AddJsonFile(path:?"settings.json",?optional:?false).AddJsonFile(path:?$"settings.{context.HostingEnvironment.EnvironmentName}.json",?optional:?true).AddJsonFile(path:?$"settings.{context.HostingEnvironment.EnvironmentName}.{context.Configuration["SubEnvironment"]}.json",?optional:?true)).ConfigureServices((context,services)?=>?services.AddSingleton<IHandler,?Handler>().Configure<FoobarbazOptions>(context.Configuration)).ConfigureWebHost(webHostBuilder?=>?webHostBuilder.Configure(app=>app.UseMiddleware<FoobarMiddleware>())).Build().Run();

針對IWebHostBuilder的適配

由于IHostBuilder利用擴展方法ConfigureWebHost/ConfigureWebHostDefaults提供了針對IWebHostBuilder的適配,意味著前面采用第一代應用承載方法編寫的代碼可以直接移植過來。如下面的代碼片段所示,靜態方法ConfigureWebHost完全依然利用IWebHostBuilder完成所有的初始化工作,我們只需要將指向該方法的Action<IWebHostBuilder>委托傳入IHostBuilder的ConfigureWebHostDefaults擴展方法就可以了。

Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(ConfigureWebHost).Build().Run();static?void?ConfigureWebHost(IWebHostBuilder?webHostBuilder)
{webHostBuilder.UseEnvironment("dev").UseContentRoot(Path.Combine(Directory.GetCurrentDirectory(),?"resources")).UseWebRoot(Path.Combine(Directory.GetCurrentDirectory(),?"resources",?"web")).UseSetting("SubEnvironment",?"dev1").ConfigureAppConfiguration((context,?configBuilder)?=>?configBuilder.AddJsonFile(path:?"settings.json",?optional:?false).AddJsonFile(path:?$"settings.{context.HostingEnvironment.EnvironmentName}.json",?optional:?true).AddJsonFile(path:?$"settings.{context.HostingEnvironment.EnvironmentName}.{context.Configuration["SubEnvironment"]}.json",?optional:?true)).UseStartup<Startup>();
}public?class?Startup
{public?IConfiguration?Configuration?{?get;?}public?Startup(IConfiguration?configuration)?=>?Configuration?=?configuration;public?void?ConfigureServices(IServiceCollection?services)?=>?services.AddSingleton<IHandler,?Handler>().Configure<FoobarbazOptions>(Configuration);public?void?Configure(IApplicationBuilder?app)?=>?app.UseMiddleware<FoobarMiddleware>();
}

Startup構造函數注入的限制

第二代應用承載模型利用ConfigureWebHost/ConfigureWebHostDefaults擴展方法對之前定義在IWebHostBuilder上的API(絕大部分是擴展方法)提供了100%的支持(除了Build方法),但是針對Startup構造函數中注入的服務則不再那么自由。如果采用基于IWebHostBuilder/IWebHost的應用承載方式,通過調用IWebHostBuilder的ConfigureServices方法注冊的服務都可以注入Startup的構造函數中,如果采用基于IHostBuilder/IHost的應用承載方式,只有與“承載配置(承載環境屬于承載配置的一部分)”相關的如下三個服務能夠注入到Startup的構造函數中。

  • IHostingEnvironment

  • IWebHostEnvironment

  • IHostEnvironment

  • IConfiguration

對于如下這段代碼,雖然注入Startup構造函數的Foobar同時通過調用IHostBuilder和IWebHostBuilder的ConfigureServices方法中進行了注冊,但是在創建Startup實例的時候依然會拋出異常。

Host.CreateDefaultBuilder(args).ConfigureServices(sevices=>sevices.AddSingleton<Foobar>()).ConfigureWebHostDefaults(ConfigureWebHost).Build().Run();static?void?ConfigureWebHost(IWebHostBuilder?webHostBuilder)
{webHostBuilder.UseEnvironment("dev").ConfigureServices(sevices?=>?sevices.AddSingleton<Foobar>()).UseContentRoot(Path.Combine(Directory.GetCurrentDirectory(),?"resources")).UseWebRoot(Path.Combine(Directory.GetCurrentDirectory(),?"resources",?"web")).UseSetting("SubEnvironment",?"dev1").ConfigureAppConfiguration((context,?configBuilder)?=>?configBuilder.AddJsonFile(path:?"settings.json",?optional:?false).AddJsonFile(path:?$"settings.{context.HostingEnvironment.EnvironmentName}.json",?optional:?true).AddJsonFile(path:?$"settings.{context.HostingEnvironment.EnvironmentName}.{context.Configuration["SubEnvironment"]}.json",?optional:?true)).UseStartup<Startup>();
}public?class?Startup
{public?IConfiguration?Configuration?{?get;?}public?Startup(IConfiguration?configuration,Foobar?foobar)?=>?Configuration?=?configuration;public?void?ConfigureServices(IServiceCollection?services)?=>?services.AddSingleton<IHandler,?Handler>().Configure<FoobarbazOptions>(Configuration);public?void?Configure(IApplicationBuilder?app)?=>?app.UseMiddleware<FoobarMiddleware>();
}public?class?Foobar
{?}

綜上所述,最初版本的ASP.NET Core由于只考慮到Web應用自身的承載,所以設計出了基于IWebHostBuilder/IWebHost模型。后來產生了基于后臺服務承載的需求,所以推出了基于IHostBuilder/IHost的服務承載模型,原本的Web應用作為一個“后臺服務(GenericWebHostService.)”進行承載。由于之前很多API都落在IWebHostBuilder(主要無數的擴展方法),出于兼容性的需求,一個名為GenericWebHostBuilder的實現類型被定義出來,它將針對IWebHostBuilder的方法調用轉移到IHostBuilder/IHost的服務承載模型中。

.NET 6在IHostBuilder/IHost服務承載模型基礎上推出了更加簡潔的Minimal API,此時又面臨相同的“抉擇”。這次它不僅需要兼容IWebHostBuilder,還得兼容IHostBuilder,再加上Minimal API自身提供的API,所以“一題多解”的現象就更多了。如果你對ASP.NET Core的歷史不甚了解,將會感到非常困惑。令你們更加感到困惑的時,此時定義在IWebHostBuilder和IHostBuilder的API并非全部可用,本文的下篇將為你一一解惑。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/281843.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/281843.shtml
英文地址,請注明出處:http://en.pswp.cn/news/281843.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java程序猿面試問缺點怎么回答_JAVA程序員面試32問,你能回答多少題

第一&#xff0c;談談final&#xff0c; finally&#xff0c; finalize的區別。第二&#xff0c;Anonymous Inner Class (匿名內部類) 是否可以extends(繼承)其它類&#xff0c;是否可以implements(實現)interface(接口)&#xff1f;第三&#xff0c;Static Nested Class 和 In…

ExecutorService——shutdown方法和awaitTermination方法

ExecutorService的關閉shutdown和awaitTermination為接口ExecutorService定義的兩個方法&#xff0c;一般情況配合使用來關閉線程池。 方法簡介shutdown方法&#xff1a;平滑的關閉ExecutorService&#xff0c;當此方法被調用時&#xff0c;ExecutorService停止接收新的任務并且…

WPF 簡單模仿 VSCode 界面布局

WPF 簡單模仿 VSCode 界面布局本文經原作者授權以原創方式二次分享&#xff0c;歡迎轉載、分享。WPF 簡單模仿 VSCode 界面布局作者&#xff1a;弈虎-呂女士會翻墻原文鏈接&#xff1a; https://github.com/Chen-Lin-Zhao-Wei/WPFLikeVSCode分享一篇群友這幾天自己寫的 WPF 簡…

compare()方法+使用compare方法

compare()方法 compare(lob1,lob2,amount,offset_1,offset_2) 1用于比較2個lob存儲的數據&#xff0c;比較的方式是從指定偏移量開始&#xff0c;對指定數量的字符或者字節進行比較。 2如果比較內容相同&#xff0c;返回0&#xff0c;否則返回-1或1. 3如果參數設置有誤或不合…

linux 下 mysql默認表_linux環境下mysql默認是區分表名大小寫的

在linux環境下&#xff0c;mysql默認表明是區分大小寫的&#xff0c;我們可以查看全局變量發現:mysql> show variables like lower%;-------------------------------| Variable_name | Value |-------------------------------| lower_case_file_system | OFF || lower_cas…

dot-- 資源匯總

http://graphviz.org/doc/info/attrs.htmlhttp://graphs.grevian.org/http://graphs.grevian.org/examplehttp://www.linuxdevcenter.com/pub/a/linux/2004/05/06/graphviz_dot.html轉載于:https://www.cnblogs.com/itzxy/p/7860276.html

兩將軍問題和TCP三次握手

兩將軍問題&#xff0c;又被稱為兩將軍悖論、兩軍問題&#xff0c; 是一個經典的計算機思想實驗。首先&#xff0c; 為避免混淆&#xff0c;我們需要認識到兩將軍問題雖然與拜占庭將軍問題相關&#xff0c;但兩者不是一個東西。拜占庭將軍問題是一個更通用的兩將軍問題版本&…

微信小程序開發系列五:微信小程序中如何響應用戶輸入事件

2019獨角獸企業重金招聘Python工程師標準>>> 微信小程序開發系列教程 微信小程序開發系列一&#xff1a;微信小程序的申請和開發環境的搭建 微信小程序開發系列二&#xff1a;微信小程序的視圖設計 微信小程序開發系列三&#xff1a;微信小程序的調試方法 微信小程序…

理解Object.defineProperty的作用

Object.defineProperty 是vue中雙向綁定的基礎。vue是通過數據劫持的方式來做數據綁定的&#xff0c;最核心的方法是通過 Object.defineProperty()方法來實現對屬性的劫持&#xff0c;達到能監聽到數據的變動。要實現數據的雙向綁定&#xff0c; 當使用存取器描述屬性的特性的時…

java.lang包有哪些類_Java中Lang包的工具類有哪些

Java中Lang包的工具類有哪些發布時間&#xff1a;2020-12-08 16:15:36來源&#xff1a;億速云閱讀&#xff1a;76作者&#xff1a;Leah今天就跟大家聊聊有關Java中Lang包的工具類有哪些&#xff0c;可能很多人都不太了解&#xff0c;為了讓大家更加了解&#xff0c;小編給大家總…

直播修仙:使用.NET 的 WebView2 如何獲取請求的響應內容,以微信直播的互動直播為例...

背景近幾年直播行業快速發展&#xff0c;門檻也越來越低&#xff0c;越來越的人涌入直播大軍。不得不說&#xff0c;直播不僅帶來了更多的娛樂消遣&#xff0c;還提供了一個新型的就業方式。說起直播的類型&#xff0c;有一個非常小眾的娛樂直播&#xff0c;沒有主播&#xff0…

2017年我的閱讀書單

2017年我正在看或者已經看完的書單&#xff0c;基本上都是紙質書&#xff0c;主要是長時間閱讀也不傷眼。由于現階段的主要工作是 DevOps 和運維管理&#xff0c;所以涉及運維方面的書我沒有再細看&#xff0c;如果是工作用到的話會稍為翻閱下。主要還是看運維開發和 Python 及…

web第6次作業position

position 屬性指定了元素的定位類型。 position 屬性的五個值&#xff1a; static &#xff08;靜態定位&#xff09; HTML元素的默認值&#xff0c;即沒有定位&#xff0c;元素出現在正常的流中。 靜態定位的元素不會受到 top, bottom, left, right影響。 div.stati…

GeneralUpdate版本更新公告20221009

大家好我是juster&#xff0c;GeneralUpdate的開源項目作者。這次將發布GeneralUpdate兼容.NET MAUI和多平臺為核心的版本。經過國慶假期的打磨修復了大量開源社區開發者的提交的bug和不合理修改建議&#xff0c;重構、刪除了大量代碼和結構使用和上一個版本沒有太大變化。1.更…

實驗2 java_《Java程序設計》實驗2

1、使用java語言編程&#xff0c;從鍵盤輸入N個整數存儲到數組中&#xff0c;求數組所有元素的和、最大值和平均值。import java.util.Scanner;public class Program01{public static void main(String [] args){Scanner scanner new Scanner(System.in);System.out.println(&…

WPF遍歷當前容器中某種控件的方法

原文:WPF遍歷當前容器中某種控件的方法版權聲明&#xff1a;本文為博主原創文章&#xff0c;未經博主允許不得轉載。 https://blog.csdn.net/m0_37591671/article/details/79528845 WPF遍歷當前容器中某種控件的方法 WPF遍歷當前容器中某種控件的方法1.目的&#xff1a;2.實現思…

gradle 轉 maven

1. 預備 1.1. java 環境 驗證 java -version   1.2. gradle 安裝&#xff0c; 參考&#xff0c; 這里列舉下windows下的安裝 b.1 下載包&#xff1a;https://gradle.org/releases b.2 解壓&#xff0c;例如放到D:\soft\gradle\gradle-3.5 b.3 添加到環境變量&#xff0c; 把D…

詳細的DedeCMS(織夢)目錄權限安全設置教程

一、目錄權限根據統計&#xff0c;絕大部分網站的攻擊都在根目錄開始的&#xff0c;因此&#xff0c;欄目目錄不能設置在根目錄。DEDECMS部署完成后&#xff0c;重點目錄設置如下&#xff1a;1&#xff09;將install刪除。2) data、templets、uploads、a或html目錄&#xff0c;…

java證書的安裝_Java 第三方證書安裝

1、進入dos命令行&#xff0c;進入目錄C:\Program Files (x86)\Java\jre6\lib\security&#xff0c;2、導入命令&#xff1a; keytool -import -keystore "C:\Program Files (x86)\Java\jre6\lib\security\cacerts" -storepass changeit -keypass changeit -alias em…

善用Object.defineProperty巧妙找到修改某個變量的準確代碼位置

2019獨角獸企業重金招聘Python工程師標準>>> 我今天的工作又遇到一個難題。前端UI右下角這個按鈕被設置為"禁用(disabled)"狀態。 這個按鈕的可用狀態由屬性enabled控制。我通過調試發現&#xff0c;一旦下圖第88行代碼執行完畢之后&#xff0c;這個按鈕的…