学习ASP.NET Core,你必须知道“中间件”是什么?中间件如何注册?请求处理管道是如何通过中间件构建的?

ASP.NET Core 的请求处理管道由一个Server和一组有序排列的中间件构成,前者仅仅完成基本的请求监听、接收和响应的工作,请求接收之后和响应之前的所有工作都交给注册的中间件来完成。ASP.NET Core的中间件通过一个类型Func<RequestDelegate, RequestDelegate>的委托对象来表示,而RequestDelegate也是一个委托,它代表一项请求处理任务。


一、一个重要的委托:RequestDelegate

当Server接受到抵达的HTTP请求之后,会构建一个描述当前请求的原始上下文,Server的类型决定了这个原始上下文的类型,比如在我们模拟管道默认采用的HttpListenerServer由于采用HttpListener来监听、接收并响应请求,所以它对应的原始上下文是一个HttpListenerContext对象。但是对于管道的后续部分,即由注册的中间件构建的链表,它们需要采用统一的方式来处理请求,所以Server最终会根据原始的上下文来创建一个抽象的HTTP上下文,后者通过抽象类HttpContext来表示。


我们不仅可以利用这个HttpContext获取描述当前请求的上下文信息,同样可以利用它来实现对响应的控制。针对当前请求的任何处理操作总是在么一个上下文中进行,所以一项请求处理任务完全可以抽象成一个类型Func<HttpContext,Task>的委托来表示,实际上具有如下定义的RequestDelegate委托具有类似的定义。


   1: public delegate Task RequestDelegate(HttpContext context);


每个中间件都承载着独立的请求处理任务,它本质上也体现了在当前HttpContext下针对请求的处理操作,那么为什么中间件不直接通过一个RequestDelegate对象来表示,而是表示为一个类型为Func<RequestDelegate, RequestDelegate>的委托对象呢?原因很简单,中间件并不孤立地存在,所有注册的中间件最终会根据注册的先后顺序组成一个链表,每个中间件不仅仅需要完成各自的请求处理任务外,还需要驱动链表中的下一个中间件。


如下图所示,对于一个由多个Func<RequestDelegate, RequestDelegate>对象组成的中间链表来说,某个中间件会将后一个Func<RequestDelegate, RequestDelegate>对象的返回值作为输入,而自身的返回值则作为前一个中间件的输入。某个中间件执行之后返回的RequestDelegate对象不仅仅体现了自身对请求的处理操作,而是体现了包含自己和后续中间件一次对请求的处理。那么对于第一个中间件来说,它执行后返回的RequestDelegate对象实际上体现了整个应用对请求的处理逻辑。


二、描述当前请求的上下文:HttpContext

对当前上下文的抽象解除了管道对具体服务器类型的依赖, 这使我们为ASP.NET Core应用自由地选择寄宿方式,而不是像传统的ASP.NET应用一样只能寄宿在IIS之中。抽象HTTP上下文的目的是为了实现对请求处理流程的抽象,只有这样我们才能将针对请求的某项操作体现在一个标准的中间件上,有这个这个标准化的中间件才有所谓的请求处理管道。


ASP.NET Core通过具有如下所示的HttpContext类来表示这么一个抽象的HTTP上下文。对于一个HttpContext对象来说,它的核心体现在用于描述请求和响应的Request和Response属性之上。除此之外,我们还可以通过获取与当前请求相关的其他上下文信息,比如用来控制用户认证的AuthenticationManager对象和代表当前请求用户的ClaimsPrincipal对象,描述当前HTTP连接的ConnectionInfo对象和用于控制WebSocket的WebSocketManager。我们还可以获取并控制当前会话,也可以获取或者设置调试追踪的ID。


   1: public abstract class HttpContext

   2: { 

   4:     public abstract HttpRequest Request { get; }

   5:     public abstract HttpResponse Response { get; }

   6:  

   7:     public abstract AuthenticationManager  Authentication { get; }

   8:     public abstract ClaimsPrincipal User { get; set; }

   9:     public abstract ConnectionInfo  Connection { get; } 

  10:     public abstract WebSocketManager WebSockets { get; } 

  11:     public abstract ISession Session { get; set; } 

  12:     public abstract string  TraceIdentifier { get; set; }

  13:     public abstract CancellationToken RequestAborted { get; set; }  

  14:     public abstract IDictionary<object, object>  Items { get; set; }  

  15:  

  16:     public abstract IServiceProvider RequestServices { get; set; }

  17:     public abstract IFeatureCollection Features { get; }

  18: }


当需要中指对请求的处理时,我们可以通过为RequestAborted属性设置一个CancellationToken对象从而将终止通知发送给管道。如果需要对整个管道共享一些与当前上下文相关的数据,我们可以将它保存在通过Items属性表示的字典中。我们一再提到依赖注入被广泛地应用ASP.NET Core管道中,HttpContext的RequestServices属性返回利用应用启动时设置的服务注册信息创建的ServiceProvider,只要相应的服务被预先注册到指定的服务接口上,我们就可能利用这个ServiceProvider根据这个接口得到对应的服务对象。


如下所示的是抽象类HttpRequest是对HTTP请求的描述,它是HttpContext的只读属性的返回类型。我们可以利用这个对象获取到描述当前请求的各种相关信息,比如请求的协议(HTTP或者HTTPS)、HTTP方法、地址,也可以获取代表请求的HTTP消息的首部和主体。


   1: public abstract class HttpRequest

   2: {

   3:     public abstract HttpContext HttpContext { get; }

   4:     public abstract string Method { get; set; }

   5:     public abstract string  Scheme { get; set; }

   6:     public abstract bool  IsHttps { get; set; }

   7:     public abstract HostString  Host { get; set; }

   8:     public abstract PathString PathBase { get; set; }

   9:     public abstract PathString  Path { get; set; }

  10:     public abstract QueryString QueryString { get; set; }

  11:     public abstract IQueryCollection Query { get; set; }

  12:     public abstract string  Protocol { get; set; }

  13:     public abstract IHeaderDictionary   Headers { get; } >

  14:     public abstract IRequestCookieCollection Cookies { get; set; }

  15:     public abstract string ContentType { get; set; }

  16:     public abstract Stream Body { get; set; }

  17:     public abstract bool HasFormContentType { get; }

  18:     public abstract IFormCollection Form { get; set; }

  19:  

  20:     public abstract Task<IFormCollection> ReadFormAsync(CancellationToken cancellationToken);

  21: }


在了解HttpContext表示请求的抽象类HttpRequest之后,我们再来认识一个与之相对的HttpResponse类型。如下面的代码片断所示,HttpResponse依然是一个抽象类,我们可以通过定义在它之上的属性和方法来控制对请求的响应。从原则上讲,我们对请求的所做的任意类型的响应都可以利用它来说实现。


 1: public abstract class HttpResponse

   2: {

   3:     public abstract HttpContext HttpContext { get; }

   4:     public abstract int StatusCode { get; set; }

   5:     public abstract IHeaderDictionary Headers { get; }

   6:     public abstract Stream Body { get; set; }

   7:     public abstract long?   ContentLength { get; set; }

   8:     public abstract IResponseCookies Cookies { get; }

   9:     public abstract bool HasStarted { get; }

  10:  

  11:     public abstract void OnStarting(Func<object, Task> callback, object state);

  12:     public virtual void OnStarting(Func<Task> callback);

  13:     public abstract void OnCompleted(Func<object, Task> callback, object state);

  14:     public virtual void RegisterForDispose(IDisposable disposable);

  15:     public virtual void OnCompleted(Func<Task> callback);

  16:     public virtual void Redirect(string location);

  17:     public abstract void Redirect(string location, bool permanent);

  18: }


当我们通过表示当前上下文的HttpContext对象得到表示响应的HttpResponse之后,我们可以不仅仅将希望的内容写入响应消息的主体,还可以设置响应状态码以及添加相应的首部。表2列出了定义在HttpResponse中的所有属性和方法所代表的含义。


封装“特性”的容器:FeatureCollection

HttpContext的另一个只读属性Features体现了HTTP上下文抽象的实现方式。Server在接收到请求之后会创建一个原始的上下文,管道不仅仅利用这个原始上下文来获取与请求相关的信息,它对请求的最终响应实际上也是通过这个原始上下文来完成的。所以对一个HttpContext对象来说,有它描述的上下文信息不仅仅来源于这个原始的上下文,我们针对HttpContext所做的任何响应操作最终都需要分发给这个原始上下文来完成, 否则是不会生效的。HttpContext和由Server创建的原始上下文之间的“双向绑定”究竟是如何实现的呢?


这个所谓的“双向绑定”即使其实很简单。当原始上下文被创建出来之后,Server会将它封装成一系列标准的特性对象,HttpContext正式针对这些特性对象而创建的。这些对象所对应的类型均实现了标准的接口,接口中不仅仅定义相应的属性来读写原始上下文中描述的信息,还定义了相应的方法来操作原始上下文。HttpContext的属性Features返回的就是这组特性对象的集合,它的返回类型为IFeatureCollection,我们将实现了该接口的类型以及对应的对象统称为FeatureCollection。


   1: public interface IFeatureCollection : IEnumerable<KeyValuePair<Type, object>>

   2: {

   3:     TFeature Get<TFeature>();

   4:     void Set<TFeature>(TFeature instance);

   5:  

   6:     boo IsReadOnly { get; }

   7:     object this[Type key] { get; set; }

   8:     int Revision { get; }

   9: }


一个FeatureCollection对象本质上就是一个Key和Value分别为Type和Object类型的字段。我们通过调用Set方法将一个特性对象针对指定的类型(一般为特性接口)注册到这个字典对象上,并通过Get方法根据注册的类型获取它,特性对象的注册和获取也可以利用定义的索引来完成。如果IsReadOnly属性返回True,我们将不能注册新的特性或者修改已经注册的特性。 整数类型的之都属性Revision可以视为整个FeatureCollection对象的版本,不论是采用何种方式注册新的特性还是修改现有的特性,这个属性的值都将改变。


具有如下定义的FeatureCollection类实现了IFeatureCollection接口,我们默认使用的FeatureCollection就是这么一个类型的对象。FeatureCollection具有两个构造函数重载,默认无参构造函数帮助我们创建一个空的特性集合,另一个构造函数则需要指定一个FeatureCollection对象来提供默认特性。对于采用第二个构造函数重载创建的 FeatureCollection对象来说,如果我们通过指定某个特性接口类型试图获取对应的特性对象时,如果对应的特性没有注册到当前FeatureCollection对象上,而是注册到提供默认特性的FeatureCollection对象上,后者将会与提供最终的特性。


   1: public class FeatureCollection : IFeatureCollection

   2: {   

   3:     //其他成员

   4:     public FeatureCollection();

   5:     public FeatureCollection(IFeatureCollection defaults);

   6: }


对于FeatureCollection类型来说,它 的IsReadOnly总是返回False,所以它永远是可读可写的。对于调用默认无参构造函数创建的FeatureCollection对象来说,它 的Revision默认返回零。如果我们通过指定另一个FeatureCollection对象为参数调用第二个构造函数来创建一个FeatureCollection对象,前者的Revision属性值将成为后者同名属性的默认值。不论我们采用何种形式(调用Set方法或者索引)添加一个新的特性或者改变了一个已经注册的特性,FeatureCollection对象的Revision属性都将自动递增。上述的这些关于FeatureCollection的特性都体现在如下所示的代码片段中。


   1: FeatureCollection defaults = new FeatureCollection();

   2: Debug.Assert(defaults.Revision == 0);

   3:  

   4: defaults.Set<IFoo>(new Foo());

   5: Debug.Assert(defaults.Revision == 1);

   6:  

   7: defaults[typeof(IBar)] = new Bar();

   8: Debug.Assert(defaults.Revision == 2);

   9:  

  10: FeatureCollection features = new FeatureCollection(defaults);

  11: Debug.Assert(features.Revision == 2);

  12: Debug.Assert(features.Get<IFoo>().GetType() == typeof(Foo));

  13:  

  14: features.Set<IBaz>(new Baz());

  15: Debug.Assert(features.Revision == 3);


HttpContext的默认实现:DefaultHttpContext

ASP.NET Core默认使用的HttpContext类型为DefaultHttpContext,上面我们介绍的针对描述原始上下文“特性集合”来创建HttpContext的策略就体现在这个类型之上。DefaultHttpContext具有一个如下的构造函数,作为参数的FeatureCollection对象就是这么一个特性集合。

   1: public class DefaultHttpContext : HttpContext

   2: {

   3:     public DefaultHttpContext(IFeatureCollection features);

   4: }


不论是对于组成管道的中间件还是建立在管道上的应用,在默认的情况下都是利用这个DefaultHttpContext对象来获取请求的相关信息,同时也是利用这个对象来控制最终发送的响应。但是DefaultHttpContext对象这个这个过程中仅仅是一个“代理”,针对它的调用(属性或者方法)最终都需要转发给由具体Server创建的那个原始上下文,在构造函数中指定的这个FeatureCollection对象所代表的特性集合成为了两者沟通的唯一渠道。对应定义在DefaultHttpContext中的所有属性,它们几乎都具有一个对应的特性,这些特性都对应着一个接口。下表列出了部分特性接口以及DefaultHttpContext对应的属性。



对于上面列出的众多特性接口,在后续相关章节中都会涉及到,所以我们只需要了解一下两个最重要的特性接口,即表示请求和响应的IHttpRequestFeatureIHttpResponseFeature。从下面给出的代码片断我们不难看出,这两个接口的定义分别与抽象类HttpRequest和HttpResponse具有一致的定义。对于DefaultHttpContext类型来说,它的Request和Response属性分别返回的是一个DefaultHttpRequest和DefaultHttpResponse对象。DefaultHttpRequest和DefaultHttpResponse分别继承自HttpRequest和HttpResponse,它们分别利用这个两个特性实现了从基类继承下来的所有抽象成员。


   1: public interface IHttpRequestFeature

   2: {

   3:     Stream Body { get; set; }

   4:     IHeaderDictionary  Headers { get; set; }

   5:     string Method { get; set; }

   6:     string Path { get; set; }

   7:     string PathBase { get; set; }

   8:     string Protocol { get; set; }

   9:     string QueryString { get; set; }

  10:     string Scheme { get; set; }

  11: }

  12:  

  13: public interface IHttpResponseFeature

  14: {

  15:     Stream  Body { get; set; }

  16:     bool HasStarted { get; }

  17:     IHeaderDictionary Headers { get; set; }

  18:     string ReasonPhrase { get; set; }

  19:     int StatusCode { get; set; }

  20:  

  21:     void OnCompleted(Func<object, Task> callback, object state);

  22:     void OnStarting(Func<object, Task> callback, object state);

  23: }


对于实现请求监听、接收和响应的Server来说,它们都需要通过实现上面这些特性接口来定义针对性的特性类。如下图所示,当成功接收到请求之后,Server会创建相应的特性并将它们组合成一个FeatureCollection对象,最后创建出一个DefaultHttpContext对象,我们注册的所有中间件针对这个DefaultHttpContext完成各自的请求处理工作。


上下文的创建者:HttpContextFactory

在Server接收到抵达的请求时,它并不会直接利用原始的上下文去创建HttpContext对象,HttpContext在管道中的创建是间接地通过HttpContextFactory来完成的。 HttpContextFactory是对所有实现了IHttpContextFactory接口的所有类型及其对象的统称,如下面的代码片段所示,IHttpContextFactory接口除了定义创建HttpContext对象的Create方法之外,还定义了另一个方法Dispose来释放指定的HttpContext对象。HttpContextFactory类是该接口的默认实现者,由它的Create方法创建并返回的自然是一个DefaultHttpContext对象。


   1: public interface IHttpContextFactory

   2: {

   3:     HttpContext Create(IFeatureCollection featureCollection);

   4:     void Dispose(HttpContext httpContext);

   5: }

   6:  

   7: public class HttpContextFactory : IHttpContextFactory

   8: {    

   9:     //省略其他成员

  10:     public HttpContext Create(IFeatureCollection featureCollection);

  11:     public void Dispose(HttpContext httpContext);

  12: }


三、管道的注册者和构建者:ApplicationBuilder

以类型为Func<RequestDelegate, RequestDelegate>的委托对象表示的中间件需要在启动的时候注册到应用程序上,所有注册的中间件最终会转换成一个RequestDelegate对象,它们按照注册顺序对请求的处理流程最终体现在对这个委托对象的执行。不论是最终将中间件转换成RequestDelegate对象,还是最初对它们的注册,都是通过一个ApplicationBuilder对象来完成的。


ApplicationBuilder是我们对所有实现了IApplicationBuilder接口的所有类型以及对应对象的统称。接口IApplicationBuilder定义如下,针对中间件的注册和RequestDelegate对象的生成分别通过调用它的Use和Build方法来完成。除了这两个核心方法,IApplicationBuilder接口还定义了三个属性,其中ApplicationServices返回根据最初服务注册生成的ServiceProvider对象,而ServerFeatures属性返回的FeatureCollection对象是描述Server的特性集合。字典类型的Properties属性用户存储任意自定义的属性,而New方法会根据自己“克隆”出一个新的ApplicationBuilder对象,这两个ApplicationBuilder对象应用具有相同的属性集合。


   1: public interface IApplicationBuilder

   2: {

   3:     IServiceProvider ApplicationServices { get; set; }

   4:     IFeatureCollection ServerFeatures { get; }

   5:     IDictionary<string, object>  Properties { get; }

   6:  

   7:     RequestDelegate  Build();

   8:     IApplicationBuilder New();

   9:     IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware);

  10: }


具有如下定义的ApplicationBuilder类型是对IApplicationBuilder接口的默认实现。ApplicationBuilder类型利用一个List<Func<RequestDelegate, RequestDelegate>>对象来保存注册的中间件,所以Use方法只需要将指定的中间件添加到这个列表中即可,而Build方法只需要逆序调用这些注册的中间件对应的Func<RequestDelegate, RequestDelegate>对象就能得到我们需要的RequestDelegate对象。值得一提的是,Build方法实际上在中间件链条的尾部添加了一个额外的中间件,该中间件会负责将响应状态码设置为404,如果我们没有注册一个中间件对请求作最终的响应(这样的中间件将不会试图调用后续中间件),整个管道比较回复一个状态码为404的响应。


   1: public class ApplicationBuilder : IApplicationBuilder

   2: {

   3:     private readonly IList<Func<RequestDelegate, RequestDelegate>> middlewares

              = new List<Func<RequestDelegate, RequestDelegate>>();

   4:  

   5:     public IDictionary<string, object> Properties { get; }

   6:  

   7:     public IServiceProvider ApplicationServices

   8:     {

   9:         get { return GetProperty<IServiceProvider>("application.Services"); }

  10:         set { SetProperty<IServiceProvider>("application.Services", value); }

  11:     }

  12:  

  13:     public IFeatureCollection ServerFeatures

  14:     {

  15:         get { return GetProperty<IFeatureCollection>("server.Features"); }

  16:     }

  17:  

  18:  

  19:     public ApplicationBuilder(IServiceProvider serviceProvider)

  20:     {

  21:         this.Properties = new Dictionary<string, object>();

  22:         ApplicationServices = serviceProvider;

  23:     }

  24:  

  25:     public ApplicationBuilder(IServiceProvider serviceProvider, object server) : this(serviceProvider)

  26:     {

  27:         SetProperty("server.Features", server);

  28:     }

  29:  

  30:     public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)

  31:     {

  32:         middlewares.Add(middleware);

  33:         return this;

  34:     }

  35:  

  36:     public IApplicationBuilder New()

  37:     {

  38:         return new ApplicationBuilder(this);

  39:     }

  40:  

  41:     public RequestDelegate Build()

  42:     {

  43:         RequestDelegate app = context =>

  44:         {

  45:             context.Response.StatusCode = 404;

  46:             return Task.FromResult(0);

  47:         };

  48:         foreach (var component in middlewares.Reverse())

  49:         {

  50:             app = component(app);

  51:         }

  52:         return app;

  53: }

  54:  

  55:     private ApplicationBuilder(ApplicationBuilder builder)

  56:     {

  57:         this.Properties = builder.Properties;

  58:     }        

  59:  

  60:     private T GetProperty<T>(string key)

  61:     {

  62:         object value;

  63:         return Properties.TryGetValue(key, out value) ? (T)value : default(T);

  64:     }

  65:  

  66:     private void SetProperty<T>(string key, T value)

  67:     {

  68:         this.Properties[key] = value;

  69:     }

  70: }


通过上面的代码片段我们不难看到,不论是通过ApplicationServices属性返回的ServiceProvider对象,还是通过ServerFeatures属性返回的用户描述Server特性的FeatureCollection对象,它们实际上都保存在通过Properties属性返回字典对象上。ApplicationBuilder具有两个公共构造函数重载,它们具有一个公共的参数,及初始化ApplicationServices属性的参数serviceProvider。


一个构造函数具有一个名为server的参数,但是这个参数并不是表示管道使用的Server,而是描述Server相关特性的FeatureCollection对象,不过这个参数类型并定义成Object,而不是IFeatureCollection接口。New方法直接调用私有构造函数创建出一个新的ApplicationBuilder对象,这个对象与自己的Properties属性共享同一个字典对象,由于ApplicationServices和ServerFeatures属性的返回值也存放在这个字典对象上,所以New方法得到的ApplicationBuilder对象与自身对象在功能上是等效的。


ApplicationBuilder的创建者:ApplicationBuilderFactory

ApplicationBuilderFactory是ASP.NET Core它用来创建ApplicationBuilder的工厂,它是对所有实现了接口IApplicationBuilderFactory的所有类型以及对应对象的统称。如下面的代码片段所示,该接口定义了唯一个方法CreateBuilder根据提供的FeatureCollection对象创建出对应的ApplicationBuilder对象,这个FeatureCollection对象正是表示描述Server的特性集合。ApplicationBuilderFactory类型是该接口的默认实现者,当CreateBuilder方法被调用的时候,它会直接将构造时提供ServiceProvider对象和serverFeatures参数表示的FeatureCollection对象来创建返回的ApplicationBuilder对象。


   1: public interface IApplicationBuilderFactory

   2: {

   3:     IApplicationBuilder CreateBuilder(IFeatureCollection serverFeatures);

   4: }

   5:  

   6: public class ApplicationBuilderFactory : IApplicationBuilderFactory

   7: {

   8:     private readonly IServiceProvider _serviceProvider;

   9:  

  10:     public ApplicationBuilderFactory(IServiceProvider serviceProvider)

  11:     {

  12:         this._serviceProvider = serviceProvider;

  13:     }

  14:  

  15:     public IApplicationBuilder CreateBuilder(IFeatureCollection serverFeatures)

  16:     {

  17:         return new ApplicationBuilder(this._serviceProvider, serverFeatures);

  18:     }

  19: }




请扫描此二维码或者搜索“大内老A”关注蒋金楠(Artech)微信公众帐号,你将会得到及时的高质量技术文章推送信息。

内容转载自公众号

大内老A
大内老A
了解更多

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

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

相关文章

2016蓝桥杯省赛---java---B---8(四平方和)

题目描述 四平方和 代码实现 package com.atguigu.TEST;import java.util.Scanner;class Main{public static void main(String[] args) {Scanner sc new Scanner(System.in);int N0;if (sc.hasNext()){Nsc.nextInt();}for (int a 0; a < 2400; a) {for (int b a; b…

在物理内存中观察CLR托管内存及GC行为

虽然看了一些书&#xff0c;还网络上的一些博文&#xff0c;不过对CLR托管内存细节依然比较模糊。而且因为工作原因总会有很多质疑&#xff0c;想要亲眼看到内存里二进制数据的变化。 所以借助winhex直接查看内存以证实书上的描述或更进一步揣摩CLR托管内存的运作方式&#xff…

2017蓝桥杯省赛---java---B---1(购物单)

题目描述 &#xff08;购物单&#xff09; 思路分析 cmd 打开电脑上的计算器算(算出来的结果是00结尾&#xff0c;作为检测) 答案 5200

DataProtection设置问题引起不同ASP.NET Core站点无法共享用户验证Cookie

这是这两天ASP.NET Core迁移中遇到的一个问题。2个ASP.NET Core站点&#xff08;对应于2个不同的ASP.NET Core Web应用程序&#xff09;&#xff0c;2个站点都可以登录&#xff0c;但在其中任1个站点登录后&#xff0c;在当前站点处于登录状态&#xff0c;访问另外1个站点却处于…

mybatis+spring报错PropertyAccessException 1

男生关注会更帅&#xff0c;女生关注会更美&#xff01;mybatisspring报错PropertyAccessException 1: org.springframework.beans.MethodInvocationExceptionorg.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘dataSource’ defined…

字符串暴力匹配算法+思路分析

思路分析 代码实现 package com.atguigu.kmp;/*** 创建人 wdl* 创建时间 2021/4/4* 描述*/ public class ViolenceMatch {public static void main(String[] args) {//测试暴力匹配算法String str1 "硅硅谷 尚硅谷你尚硅 尚硅谷你尚硅谷你尚硅你好";String str2&quo…

springmvc中报错Request processing failed;

今天在整个ssm的时候出现了个低级错误&#xff0c;找了好久才找出来&#xff0c;在发现真理的那一刻&#xff0c;我都有死的冲动了。 报错如下&#xff1a; HTTP Status 500 - Request processing failed; nested exception is java.lang.IllegalStateException: Optional int …

在.NET Core 上运行的 WordPress

在.NET Core 上运行的 WordPress,无需安装PHP既可跨平台运行WordPress。 在Peachpie中实现PHP所需的功能数月后&#xff0c;现在终于可以运行一个真实的应用程序&#xff1a;WordPress。 本文是基于Peachpie https://github.com/iolevel/peachpie Peachpie是一个基于Microsof…

求集合中的公共元素

package com.atguigu.TEST;import javax.swing.plaf.basic.BasicScrollPaneUI; import java.util.HashSet;/*** 创建人 wdl* 创建时间 2021/4/4* 描述*/ public class Test {public static void main(String[] args) {HashSet<String> hashSet1 new HashSet<>();H…

mybatis使用全注解的方式案例(包含一对多关系映射)

前面我写过ssh&#xff1a;ssh(SpringSpring mvchibernate)简单增删改查案例 和ssm&#xff1a;ssm(SpringSpring mvcmybatis)的案例&#xff0c;需要了解的可以去看看&#xff0c;今天我写了一下ssm(springspringmvcmybatis)全注解的方式又重新写了一遍两表增删改查的案例&…

Visual Studio 2017全面上市

自从1997年第一版发布的20年以来&#xff0c;微软Visual Studio开发工具一向以易学易用、功能齐全而闻名&#xff0c;帮助开发者以简驭繁&#xff0c;即使面对越来越快速的交付压力&#xff0c;也能大幅提高生产力&#xff0c;好整以暇。对于Visual Studio的使用者而言&#xf…

Visual Studio 2017发布会:黄金时代的家族聚会

美国时间三月七日&#xff08;北京2017年3月8日&#xff09;&#xff0c;微软正式发布了Visual Studio 2017&#xff0c;自己旗舰开发工具的最新版本。同日发布的主要产品还有 .NET Core Tooling 1.0.NET Core 微服务实例Visual Studio for Mac Preview 4Visual Studio Mobile …

java开发可以转什么软件有哪些_转行开发软件Java编程必须会什么

原标题&#xff1a;转行开发软件Java编程必须会什么要想开发软&#xff0c;Java编程必须会什么&#xff1f;最起码的就是逻辑思维要好&#xff0c;只要不是特别差就没有什么问题。数学是相对比较能够体现出一个人的逻辑思维如何。先想想自己以前上学的时候&#xff0c;数学成绩…

2017蓝桥杯省赛---java---B---2(纸牌三角形)

题目描述 纸牌三角形 思路分析 全排列特殊去重 ans/6 代码实现 package com.atguigu.TEST;class Main{public static int[] a{1,2,3,4,5,6,7,8,9};public static int ans;public static void f(int k){if(k9){int x1 a[0] a[1] a[2] a[3];int x2 a[3] a[4] a[5] …

微软开源基于云的生理学研究工具

Bio Model Analyzer是一款微软基于云的生理学研究工具&#xff0c;可以用于对化细胞交互和通信进行建模&#xff0c;现已经在GitHub上开源&#xff0c;在MIT许可之下。 研究人员使用Bio Model Analyzer (BMA) 去创建计算机模型&#xff0c;该模型可以比较健康和不健康细胞内的处…

2017蓝桥杯省赛---java---B---3(承压计算)

题目描述 7 5 8 7 8 8 9 2 7 2 8 1 4 9 1 8 1 8 8 4 1 7 9 6 1 4 5 4 5 6 5 5 6 9 5 6 5 5 4 7 9 3 5 5 1 7 5 7 9 7 4 7 3 3 1 4 6 4 5 5 8 8 3 2 4 3 1 1 3 3 1 6 6 5 5 4 4 2 9 9 9 2 1 9 1 9 2 9 5 7 9 4 3 3 7 7 9 3 6 1 3 8 8 3 7 3 6 8 1 5 3 9 5 8 3 8 1 8 3 3 8 3 2 3…

[C#7] 1.Tuples(元组)

1. 老版本代码 class Program { static void Main(string[] args) { var fullName GetFullName(); Console.WriteLine(fullName.Item1);// Item1,2,3不能忍&#xff0c;&#xff0c;, Console.WriteLine(fullName.Item2); Console.WriteLine(fullName.Item3); } static Tuple&…

mysql 行转列分级输出_MySQL如何实现行转列分级输出?_MySQL

概述好久没写SQL语句&#xff0c;今天看到问答中的一个问题&#xff0c;拿来研究一下。问题链接&#xff1a;关于Mysql 的分级输出问题情景简介学校里面记录成绩&#xff0c;每个人的选课不一样,而且以后会添加课程&#xff0c;所以不需要把所有课程当作列。数据表里面数据如下…

Visual Studio 2017正式版离线安装及介绍

Visual Studio 2017 RTM正式版离线安装及介绍。 首先至官网下载&#xff1a;https://www.visualstudio.com/zh-hans/downloads/ VS 2017 正式版介绍&#xff1a; https://www.visualstudio.com/zh-hans/vs/whatsnew/ VS 2017 离线模式只离线.NET Core部分&#xff1a; Visual S…

Linux下查找命令

转载自 Linux下查找命令 一.Linux查找文件的相关命令 常 用 命 令 简要中文说明 程序所在目录 more 分页显示一个文件或任何输出结果 /bin less 分页显示一个文件并且可以回头 /usr/bin whereis 寻找文件工具 /usr/bin find 寻找文件工具 /usr/bin locate 寻…