ASP.NET Core使用HostingStartup增强启动操作

概念

    在ASP.NET Core中我们可以使用一种机制来增强启动时的操作,它就是HostingStartup。如何叫"增强"操作,相信了解过AOP概念的同学应该都非常的熟悉。我们常说AOP使用了关注点分离的方式,增强了对现有逻辑的操作。而我们今天要说的HostingStartup就是为了"增强"启动操作,这种"增强"的操作甚至可以对对现有的程序可以做到无改动的操作。例如,外部程序集可通过HostingStartup实现为应用提供配置服务、注册服务或中间件管道操作等。

使用方式

    HostingStartup属性表示要在运行时激活的承载启动程序集。大致分为两种情况,一种是自动扫描当前Web程序集中通过HostingStartup指定的类,另一种是手动添加配置hostingstartupassembles指定外部的程序集中通过HostingStartup指定的类。第一种方式相对简单,但是对Web程序本身有入侵,第二种方式稍微复杂一点点,但是可以做到对现有代码无入侵操作,接下来我们分别演示这两种使用方式。

ASP.NET Core中直接定义

首先是在ASP.NET Core程序中直接使用HostingStartup,这种方式比较简单首先在Web程序中随便定义一个类,然后实现IHostingStartup接口,最后别忘了在程序集中添加HostingStartupAttribute指定要启动的类的类型,具体代码如下所示

using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
//通过HostingStartup指定要启动的类型
[assembly: HostingStartup(typeof(HostStartupWeb.HostingStartupInWeb))]
namespace HostStartupWeb
{public class HostingStartupInWeb : IHostingStartup{public void Configure(IWebHostBuilder builder){//程序启动时打印依据话,代表执行到了这里Debug.WriteLine("Web程序中HostingStartupInWeb类启动");//可以添加配置builder.ConfigureAppConfiguration(config => {//模拟添加一个一个内存配置var datas = new List<KeyValuePair<string, string>>{new KeyValuePair<string, string>("ServiceName", "HostStartupWeb")};config.AddInMemoryCollection(datas);});//可以添加ConfigureServicesbuilder.ConfigureServices(services=> {//模拟注册一个PersonDtoservices.AddScoped(provider=>new PersonDto { Id = 1, Name = "yi念之间", Age = 18 });});//可以添加Configurebuilder.Configure(app => {//模拟添加一个中间件app.Use(async (context, next) =>{await next();});});}}
}

仅仅使用上面所示的这些代码,便可在Web程序启动的时候去自动执行HostingStartupInWeb的Configure方法,在这里面我们几乎可以使用所有针对ASP.NET Core程序配置的操作,而且不需要在Web程序中额外添加别的代码就可以自动调用HostingStartupInWeb的Configure方法。

外部程序集引入

我们之前也说过,上面的方式虽然使用起来相对简单一点,仅仅是一点,那就是省去了指定启动程序集的逻辑。但是,上面的方式需要在Web程序中添加,这样的话还是会修改代码。而且,可能更多的时候我们是在外部的程序集中编写HostingStartup逻辑,这时候就需要使用另一种方式在将外部程序集中引入HostingStartup。首先我们要在自定义的程序集中至少引入Microsoft.AspNetCore.Hosting包才能使用HostingStartup

<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" />

如果你不需要使用注册中间件的逻辑那么仅仅引入Microsoft.AspNetCore.Hosting.Abstractions即可

<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" />

如果需要使用其他功能包,可以自行在定义的程序集中引入。比如我们定义了一个名为HostStartupLib的Standard类库,并创建了名为HostStartupLib的类

using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
[assembly: HostingStartup(typeof(HostStartupLib.HostingStartupInLib))]
namespace HostStartupLib
{public class HostingStartupInLib : IHostingStartup{public void Configure(IWebHostBuilder builder){Debug.WriteLine("Lib程序中HostingStartupInLib类启动");//添加配置builder.ConfigureAppConfiguration((context, config) => {var datas = new List<KeyValuePair<string, string>>{new KeyValuePair<string, string>("ServiceName", "HostStartupLib")};config.AddInMemoryCollection(datas);});//添加ConfigureServicesbuilder.ConfigureServices(services=> {services.AddScoped(provider=>new PersonDto { Id = 2, Name = "er念之间", Age = 19 });});//添加Configurebuilder.Configure(app => {app.Use(async (context, next) =>{await next();});});}}
}

然后我们将自定义的HostStartupLib这个Standard类库引入Web项目中,运行Web程序,发现HostingStartupInLib的Configure方法并不能被调用。其实我们上面说过了,将HostingStartup从外部程序集引入的话需要手动指定启动程序集的名称。指定启动程序集的方式有两种,一种是指定IWebHostBuilder的扩展UseSetting指定

public static IHostBuilder CreateHostBuilder(string[] args) =>Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder =>{//通过UseSetting的方式指定程序集的名称webBuilder.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, "HostStartupLib");//如果HostingStartup存在多个程序集中可以使用;分隔,比如HostStartupLib;HostStartupLib2//webBuilder.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, "HostStartupLib;HostStartupLib2");webBuilder.UseStartup<Startup>();});

另一种通过添加环境变量ASPNETCORE_HOSTINGSTARTUPASSEMBLIES的方式,可以通过设置launchSettings.json中

"environmentVariables": {"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "HostStartupLib"//如果HostingStartup存在多个程序集中可以使用;分隔,比如HostStartupLib;HostStartupLib2//"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "HostStartupLib;HostStartupLib2"
}

可以引入多个包含HostingStartup的程序集,在设置WebHostDefaults.HostingStartupAssembliesKey或者ASPNETCORE_HOSTINGSTARTUPASSEMBLIES指定多个程序集名称可以使用英文分号(;)隔开程序集名称。虽然是两种形似指定,但是其实本质是一样的那就是设置配置key为hostingStartupAssemblie配置的值,下面我们会详细讲解。
    通过在程序中设置环境变量的方式等同于Window系统中Set的方式设置环境变量,或Linux系统中export的方式设置环境变量,亦或是直接设置系统环境变量,效果都是一致的。指定完成启动程序集之后,再次运行程序便可以看到HostingStartupInLib的Configure方法被调用到了。在这里我们可以看到如果是使用的环境变量的方式去指定启动程序集的话,对现有代码可以做到完全无入侵。

源码探究

在上面我们简单的介绍了HostingStartup的概念及基本的使用方式,基于这些我们产生了几个疑问

  • 首先是关于HostingStartup的基本工作方式是什么

  • 其次是为什么HostingStartup在Web程序中不需要配置程序集信息就可以被调用到,而通过外部程序集引入HostingStartup需要手动指定程序集

  • 最后是通过外部程序集引入HostingStartup的指定方式为何只能是UseSetting和环境变量的方式
    基于以上几个疑问,我们来探索一下HostingStartup的相关源码,来揭开它的神秘面纱。首先废话不多说直接找到源码位置[点击查看源码????]在GenericWebHostBuilder类中的ExecuteHostingStartups方法中,关于GenericWebHostBuilder类我们在上篇文章深入探究ASP.NET Core Startup初始化中主要就是分析这个类,因为这是构建WebHost的默认类,而我们接下来要说的ExecuteHostingStartups方法也是承载在这个类中,直接贴代码如下所示

private void ExecuteHostingStartups()
{//通过配置_config和当前程序集名称构建WebHostOptions类var webHostOptions = new WebHostOptions(_config, Assembly.GetEntryAssembly()?.GetName().Name);//如果PreventHostingStartup属性为true则直接返回//通过这个可以配置阻止启动逻辑if (webHostOptions.PreventHostingStartup){return;}var exceptions = new List<Exception>();//构建HostingStartupWebHostBuilder_hostingStartupWebHostBuilder = new HostingStartupWebHostBuilder(this);//GetFinalHostingStartupAssemblies获取最终要执行的程序集名称foreach (var assemblyName in webHostOptions.GetFinalHostingStartupAssemblies().Distinct(StringComparer.OrdinalIgnoreCase)){try{//通过程序集名称加载程序集信息,因为使用了AssemblyName所以只需要使用程序集名称即可var assembly = Assembly.Load(new AssemblyName(assemblyName));//获取包含HostingStartupAttribute的程序集foreach (var attribute in assembly.GetCustomAttributes<HostingStartupAttribute>()){//实例化HostingStartupAttribute的HostingStartupType属性的对象实例//即我们上面声明的[assembly: HostingStartup(typeof(HostStartupWeb.HostingStartupInWeb))]var hostingStartup = (IHostingStartup)Activator.CreateInstance(attribute.HostingStartupType);//调用HostingStartup的Configure方法hostingStartup.Configure(_hostingStartupWebHostBuilder);}}catch (Exception ex){exceptions.Add(new InvalidOperationException($"Startup assembly {assemblyName} failed to execute. See the inner exception for more details.", ex));}}if (exceptions.Count > 0){_hostingStartupErrors = new AggregateException(exceptions);}
}

通过上面的源码我们就可以很清楚的了解到HostingStartup的基本工作方式。获取的程序集中包含的HostingStartupAttribute,通过获取HostingStartupAttribute的HostingStartupType属性得到要执行的IHostingStartup实例,最后执行Configure方法,Configure方法需要传递IWebHostBuilder的实例,而HostingStartupWebHostBuilder正是实现了IWebHostBuilder接口。
    我们了解到了HostStartup的工作方式,接下来我们来探究一下为什么HostingStartup在Web程序中不需要配置程序集信息就可以被调用到,而通过外部程序集引入HostingStartup需要手动指定程序集。通过上面的源码我们可以得到一个信息那就是所有需要启动的程序集信息都是来自WebHostOptions的GetFinalHostingStartupAssemblies方法,接下来我们就来查看一下GetFinalHostingStartupAssemblies方法的实现源码[点击查看源码????]

public IEnumerable<string> GetFinalHostingStartupAssemblies()
{return HostingStartupAssemblies.Except(HostingStartupExcludeAssemblies, StringComparer.OrdinalIgnoreCase);
}

从这里我们可以看出程序集信息来自于HostingStartupAssemblies属性,而且还要排除掉HostingStartupExcludeAssemblies包含的程序集。我们找到他们初始化的相关逻辑大致如下

//承载启动是需要调用的HostingStartup程序集
public IReadOnlyList<string> HostingStartupAssemblies { get; set; }
//承载启动时排除掉不不要执行的程序集
public IReadOnlyList<string> HostingStartupExcludeAssemblies { get; set; }
//是否阻止HostingStartup启动执行功能,如果设置为false则HostingStartup功能失效
//通过上面的ExecuteHostingStartups方法源码可知
public bool PreventHostingStartup { get; set; }
//应用程序名称
public string ApplicationName { get; set; }public WebHostOptions(IConfiguration configuration, string applicationNameFallback)
{ApplicationName = configuration[WebHostDefaults.ApplicationKey] ?? applicationNameFallback;HostingStartupAssemblies = Split($"{ApplicationName};{configuration[WebHostDefaults.HostingStartupAssembliesKey]}");HostingStartupExcludeAssemblies = Split(configuration[WebHostDefaults.HostingStartupExcludeAssembliesKey]);PreventHostingStartup = WebHostUtilities.ParseBool(configuration, WebHostDefaults.PreventHostingStartupKey);
}//分隔配置的程序集信息,分隔依据为";"分号,这也是我们上面说过配置多程序集的时候采用分号分隔的原因
private IReadOnlyList<string> Split(string value)
{return value?.Split(';', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)?? Array.Empty<string>();
}

首先,通过HostingStartupAssemblies的初始化逻辑我们可以得出,默认会是有两个数据来源,一个是当前的ApplicationName,另一个是通过HostingStartupAssembliesKey配置的程序集信息。这也解答了我们上面说过的为什么HostingStartup在Web程序中不需要配置程序集信息就可以被调用到,而通过外部程序集引入HostingStartup需要手动指定程序集。其次,我们可以了解到通过配置HostingStartupExcludeAssemblies信息排除你不想启动的HostingStartup程序集,而且还可以通过配置PreventHostingStartup值来禁止使用HostingStartup的功能。
通过上面的代码我们还了解到这三个属性的来源的配置名称都是来自WebHostDefaults这个常量类,接下来我们查看一下这三个属性对应的配置名称

public static readonly string HostingStartupAssembliesKey = "hostingStartupAssemblies";
public static readonly string HostingStartupExcludeAssembliesKey = "hostingStartupExcludeAssemblies";
public static readonly string PreventHostingStartupKey = "preventHostingStartup";

也就是说,我们可以可以通过配置这三个名称的配置,来完成HostingStartup相关的功能比如

public static IHostBuilder CreateHostBuilder(string[] args) =>Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder =>{//通过UseSetting的方式指定程序集的名称webBuilder.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, "HostStartupLib");//如果HostingStartup存在多个程序集中可以使用;分隔,比如HostStartupLib;HostStartupLib2//webBuilder.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, "HostStartupLib;HostStartupLib2");//排除执行HostStartupLib2程序集执行HostingStartup逻辑webBuilder.UseSetting(WebHostDefaults.HostingStartupExcludeAssembliesKey, "HostStartupLib2");//禁用HostingStartup功能webBuilder.UseSetting(WebHostDefaults.PreventHostingStartupKey, "true");webBuilder.UseStartup<Startup>();});

或通过环境变量的方式去操作

"environmentVariables": {"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "HostStartupLib",//如果HostingStartup存在多个程序集中可以使用;分隔,比如HostStartupLib;HostStartupLib2//"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "HostStartupLib;HostStartupLib2"//排除执行HostStartupLib2程序集执行HostingStartup逻辑"ASPNETCORE_HOSTINGSTARTUPEXCLUDEASSEMBLIES":"HostStartupLib2",//禁用HostingStartup功能"ASPNETCORE_PREVENTHOSTINGSTARTUP":"true"
}

其实这两种配置方式是完全等价的,为什么这么说呢?首先是在Configuration中获取配置是忽略大小写的,其实是使用ConfigureWebHostDefaults配置WebHost相关信息的时候会添加configBuilder.AddEnvironmentVariables(prefix: "ASPNETCORE_")逻辑这样的话获取环境变量的时候可以忽略ASPNETCORE_前缀。
那么到目前为止,还有一个疑问尚未解决,那就是为何只能通过UseSetting和环境变量的方式去配置HostingStartup相关配置,解铃还须系铃人,我们在上面的ExecuteHostingStartups方法中看到了这个逻辑

//这里传递了一个_config
var webHostOptions = new WebHostOptions(_config, Assembly.GetEntryAssembly()?.GetName().Name);

我们可以看到传递了配置Configuration的实例_config,我们到初始化_config地方有如下逻辑

var configBuilder = new ConfigurationBuilder().AddInMemoryCollection();
if (!options.SuppressEnvironmentConfiguration)
{//添加环境变量configBuilder.AddEnvironmentVariables(prefix: "ASPNETCORE_");
}
//构建了_config实例
private readonly IConfiguration _config = configBuilder.Build();

也就可以解释为何我们可以通过环境变量去配置HostingStartup,然后我们再来看UseSetting方法的逻辑

public IWebHostBuilder UseSetting(string key, string value)
{_config[key] = value;return this;
}

原来UseSetting也是给_config实例设置值,所以无论通过UseSetting或环境环境变量的方式去配置,本质都是在操作_config这个配置实例,到此为止所有谜团均以解开。

在SkyAPM中的使用

我们上面说了HostingStartup可以增强启动时候的操作,可以通过对现有代码无入侵的方式增强程序功能。而SkyAPM-dotnet也正是使用了这个功能,实现了无入侵启动APM监控。我们来回顾一下SkyAPM-dotnet的使用方式

  • 首先是使用Nuget添加SkyAPM.Agent.AspNetCore程序集引用。

  • 其次是在launchSettings.json文件中添加ASPNETCORE_HOSTINGSTARTUPASSEMBLIES:"SkyAPM.Agent.AspNetCore"环境变量配置(等同于set ASPNETCORE_HOSTINGSTARTUPASSEMBLIES=SkyAPM.Agent.AspNetCore或export ASPNETCORE_HOSTINGSTARTUPASSEMBLIES=SkyAPM.Agent.AspNetCore
    的方式,本质都是在配置环境变量)

  • 最后通过SKYWALKING__SERVICENAME设置程序名称
    这里我们通过需要配置ASPNETCORE_HOSTINGSTARTUPASSEMBLIES名称可以看出确实是使用了HostingStartup功能,而通过HostingStartup增强的操作入口肯定就在SkyAPM.Agent.AspNetCore程序集中,我们找到SkyAPM.Agent.AspNetCore程序集的源码[点击查看源码????]看到了SkyApmHostingStartup类实现如下

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using SkyApm.Agent.AspNetCore;
using SkyApm.AspNetCore.Diagnostics;[assembly: HostingStartup(typeof(SkyApmHostingStartup))]namespace SkyApm.Agent.AspNetCore
{internal class SkyApmHostingStartup : IHostingStartup{public void Configure(IWebHostBuilder builder){builder.ConfigureServices(services => services.AddSkyAPM(ext => ext.AddAspNetCoreHosting()));}}
}

通过这个我们可以看出确实如此,当然也是等同于我们通过UseSetting(WebHostDefaults.HostingStartupAssembliesKey, "SkyApm.Agent.AspNetCore")去配置,我们甚至可使用如下的方式去使用SkyAPM-dotnet

public void ConfigureServices(IServiceCollection services)
{services.AddSkyAPM(ext => ext.AddAspNetCoreHosting())
}

这些写法其实是完全等价的,但是通过环境变量的方式配置HostingStartup启动程序集的方式无疑是最优雅的。所以我们在日常的学习开发中,最好还是通过这种方式去操作。

改造Zipkin使用

我们在之前的文章ASP.NET Core整合Zipkin链路跟踪中曾演示过基于诊断日志DiagnosticSource改进Zipkin的集成方式,通过本篇文章讲述的HostingStartup我们可以进步一改进Zipkin的集成方式,可以让它使用起来和SkyAPM-dotnet类似的方式,我们基于之前的示例中的ZipkinExtensions程序集中添加一个ZipkinHostingStartup类,用于承载集成Zipkin的操作,代码如下

using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;namespace ZipkinExtensions
{public class ZipkinHostingStartup: IHostingStartup{public void Configure(IWebHostBuilder builder){builder.ConfigureServices(services=> {services.AddZipkin();services.AddSingleton<ITraceDiagnosticListener, HttpDiagnosticListener>();});builder.Configure(app=> {IHostApplicationLifetime lifetime = app.ApplicationServices.GetService<IHostApplicationLifetime>();ILoggerFactory loggerFactory = app.ApplicationServices.GetService<ILoggerFactory>();IConfiguration configuration = app.ApplicationServices.GetService<IConfiguration>();string serivceName = configuration.GetValue<string>("ServiceName");string zipKinUrl = configuration.GetValue<string>("ASPNETCORE_ZIPKINADDRESS");app.UseZipkin(lifetime, loggerFactory, serivceName, zipKinUrl);});}}
}

然后在每个项目的launchSettings.json文件中添加如下所示的配置即可,这样的话就可以做到对现有业务代码无任何入侵。

 "environmentVariables": {"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "ZipkinExtensions","ASPNETCORE_ZIPKINADDRESS": "http://localhost:9411/"}

总结

    本文介绍了HostingStartup的基本概念,基础使用以及对其源码的分析和在SkyAPM-dotnet中的应用,最后我们改造了Zipkin的集成方式。HostingStartup在一些集成APM或者链路跟踪的类似场景还是非常实用的,或者如果我们有集成一些基础组件或者三方的组件,但是我们的代码中并不需要直接的使用这些组件中的类或者直接的代码关系,均可以使用HostingStartup的方式去集成,为我们实现对现有代码提供无入侵增强提供了强大的支持。关于HostingStartup我也是在看源码中无意发现的,后来发现微软ASP.NET Core官方文档Use hosting startup assemblies in ASP.NET Core一文中有讲解,然后联想到自己使用过的SkyAPM-dotnet正是使用了HostingStartup+诊断日志DiagnosticSource的方式实现了对代码无入侵的方式进行监控和链路跟踪。于是决定深入研究一下,可谓收获满满,便写下这篇文章希望更多的人能够了解使用这个功能。

????欢迎扫码关注我的公众号????

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/306756.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络实验arp协议分析,计算机网络ARP地址协议解析实验报告

计算机网络ARP地址协议解析实验报告 (5页)本资源提供全文预览&#xff0c;点击全文预览即可全文预览,如果喜欢文档就下载吧&#xff0c;查找使用更方便哦&#xff01;9.9 积分计算机网络实验报告、实验目的:1. 掌握ARP协议的报文格式2. 掌握ARP协议的工作原理3. 理解ARP高速缓存…

数据结构——图-最短路径长度中最大的一个

#include<stdio.h> #include<string.h> #define INF 32767 #define MAXVEX 30 int dist[MAXVEX]; //建立dist数组int path[MAXVEX]; //建立path数组int S[MAXVEX]; //建立S数组typedef char VertexType;typedef struct graph {int n,e;VertexType vexs[MAXVE…

一个情怀引发的生产事故

在一个项目中&#xff0c;需要轻量级用到脚本语言&#xff0c;来提高应用服务的灵活性。因为知道Roslyn可以动态编辑C#&#xff0c;本着情怀&#xff0c;就自然用Roslyn来处理这块业务了。开在windows上执行&#xff0c;一次调用风平浪静&#xff0c;因为这个功能使用频次不高&…

python standardscaler_教你用python一步步解决“维度灾难”

全文共7016字&#xff0c;预计学习时长40分钟或更长现代科技时代产生和收集的数据越来越多。然而在机器学习中&#xff0c;太多的数据可不是件好事。某种意义上来说&#xff0c;特征或维度越多&#xff0c;越会降低模型的准确性&#xff0c;因为需要对更多的数据进行泛化——这…

c++的输入和输出

1. 输入/输出流的成员函数 put()函数 put函数常用的调用形式&#xff1a; cout.put(char ch); 功能&#xff1a;用于输出一个字符&#xff0c;还可以是ASCII代码&#xff08;或者是ASCII表达式&#xff09; cout.put(65)&#xff1b; get()函数 常用形式为&#xff1a;cin.g…

被 C# 的 ThreadStatic 标记的静态变量,都存放在哪里了?

一&#xff1a;背景 1. 讲故事前几天公号里有一位朋友留言说&#xff0c;你windbg玩的溜&#xff0c;能帮我分析下被 ThreadStatic 修饰的变量到底存放在哪里吗&#xff1f;能不能帮我挖出来????????????&#xff0c;其实这个问题问的挺深的&#xff0c;玩高级语言…

唐山师范学院计算机论文,唐山师范学院校园网络解决方案 毕业论文

唐山师范学院校园网络解决方案 毕业论文 (43页)本资源提供全文预览&#xff0c;点击全文预览即可全文预览,如果喜欢文档就下载吧&#xff0c;查找使用更方便哦&#xff01;9.90 积分1 唐山师范学院 专 科毕业论文 题 目 唐山师范学院校园网络解决方案 学 生 指导教师 年 级 200…

基本系统设备感叹号更新不了_电脑识别不了U盘?别紧张,免费教你如何解决...

你的电脑会出现识别不了U盘的情况吗&#xff1f;为什么会识别不了U盘你了解过吗&#xff1f;首先我们说一下&#xff0c;U盘作为一个办公必备品&#xff0c;用来存储文件以便随时使用&#xff0c;简单方便。但是如果某一天你的电脑突然之间无法识别U盘的话&#xff0c;就代表新…

群同态基本定理证明_群论(7): 群代数, 群表示基础

内容提要:1 群代数; 2 域上的有限维群代数和Maschke定理; 3 函数环; 4 代数闭域上的群表示论; 本文主要参考文献.本文的前置内容为:格罗卜&#xff1a;群论(1): 群, 同构定理, 循环群格罗卜&#xff1a;群论(2): 群作用, Sylow定理更多内容&#xff0c;请移步专栏目录:格罗卜&a…

c++的文件输入/输出

1文件的概述 根据文件中数据的组织形式分为&#xff1a; 1 文本文件 文本文件又称ASCII文件&#xff0c;它的每一个字节存放一个ASCII代码&#xff0c;代表一个字符 输出文本&#xff1a;接收从内存输出的数据 输出文件&#xff0c;向它写入数据 ofstream fout; 输入文件&…

南阳理工计算机全国排名,全国工科实力最强的10所高校排名,

评价工科实力一个非常明显的指标就是学科实力&#xff0c;2017年公布的第四次学科评估结果就是最好的参考。下面就以学科评估排名全国5%的学科数作为评比基准&#xff0c;对国内主要高校的工科实力进行一个排名&#xff0c;结果如下&#xff1a;前3甲&#xff1a;清华大学、浙江…

真正的高手,都有增长思维!(深度好文)

点击蓝字关注&#xff0c;回复“职场进阶”获取职场进阶精品资料一份职场&认知洞察 丨 作者 / findyi这是findyi公众号分享的第99篇原创文章最近几年互联网红利消亡&#xff0c;老板们非常焦虑。这也一度让所谓的「首席增长官」被媒体热炒。仿佛来一个增长的救世主&#xf…

service层中有某个事物要立马提交_硬货你要的,binder机制来了

欢迎关注专栏&#xff1a;里面定期分享Android和Flutter架构技术知识点及解析&#xff0c;还会不断更新的BATJ面试专题&#xff0c;欢迎大家前来探讨交流&#xff0c;如有好的文章也欢迎投稿。Android高级进阶​zhuanlan.zhihu.com前言Binder做为Android中核心机制&#xff0c;…

学习笔记 | 传统企业互联网改革之道

【学习笔记】| 作者 / Edison Zhou这是EdisonTalk的第306篇学习分享最近在看刘润老师的《互联网战略版&#xff1a;传统企业&#xff0c;互联网在踢门》&#xff0c;学习了传统企业互联网改革的价值模型与三大模式&#xff0c;醍醐灌顶受益良多。因此&#xff0c;我将我学到的总…

蓝桥杯杂题

蓝桥杯杂题开始聊天吧气球升起度熊所居住开始聊天吧 #include<bits/stdc.h> using namespace std; set<char>S; int main() {string s;while(cin>>s){S.clear();for(auto x:s)//使用x拷贝s字符串 中的每一个字符 {S.insert(x); //将每一个字符插入到集合S中/…

setnx是原子操作吗_谈谈Volatile关键字?为什么不能保证原子性?用什么可以替代?为什么?...

大家好&#xff0c;欢迎关注我的公众号码猿bug,需要资料的话可以加我微信好友。再谈volatile关键字之前&#xff0c;首先必须聊聊JMM内存模型&#xff01;JMM主要的特性&#xff1a;可见性、原子性&#xff0c;顺序性Java 虚拟机规范试图定义一种 Java 内存模型&#xff08;JMM…

360 屏蔽ajax,怎么在easy ui做全局Ajax拦截啊?

满意答案egiuas2014.08.13采纳率&#xff1a;53% 等级&#xff1a;9已帮助&#xff1a;167人在web.xml中定义一个全局过滤器拦截所有请求&#xff1a;自定义filter your difine filter name /* 后台代码&#xff1a;过滤器中获取session用户对象 如果为空&#xff0c;返回超…

用python排序算法_Python - 八大排序算法

1、序言 本文使用Python实现了一些常用的排序方法。文章结构如下&#xff1a; 1.直接插入排序 2.希尔排序 3.冒泡排序 4.快速排序 5.简单选择排序 6.堆排序 7.归并排序 8.基数排序 上述所有的排序均写在一个Python自定义类中&#xff0c;作为成员函数。 2、排序方法详细介绍 1.…

剑指offer-数组中的重复的数字-p39

数组 c中的STL中的vector (STL中的vector每次扩容量时&#xff0c;新的容量都是之前一次的两倍) 在c/c中&#xff0c;数组和指针是相互关联又有区别的两个概念。 关联&#xff1a;当我们声明一个数组时&#xff0c;其数组的名字也是一个指针&#xff0c;该指针指向数组的第一个…

尝鲜!.NET5实操之docker+k8s,这10个坑,你不得不知!

2016年发布了.NET Core第一个正式版本&#xff0c;2020年11月.NET5也正式来临了&#xff0c;技术日新月异&#xff0c;也有点让人应接不暇。在框架设计上&#xff0c;.NET Framework的全家桶理念&#xff0c;培养了一大批的CRUD&#xff0c;而.NET Core转变成了按需使用(Pay fo…