ASP.NET Core 过滤器 使用依赖项注入

news2024/11/17 16:02:19

        过滤器是 ASP.NET Core 中的特殊组件,允许我们在请求管道的特定阶段控制请求的执行。这些过滤器在中间件执行后以及 MVC 中间件匹配路由并调用特定操作时发挥作用。

        简而言之,过滤器提供了一种在操作级别自定义应用程序行为的方法。它们就像检查点,允许我们执行特定任务,例如异常处理、缓存或添加自定义响应标头。

        当请求到达某个操作时,过滤器可以收集有关已选择哪个操作以及关联的路由数据的信息。此信息可用于做出决策并执行特定于该特定操作或控制器的操作。

        通过使用过滤器,我们在处理请求时拥有更多的控制权和灵活性。我们可以根据我们的要求实现自定义逻辑并将特定行为应用于不同的操作或控制器。这有助于在 ASP.NET Core 应用程序中实现更加定制和高效的请求处理流程。

过滤器的类型

        ASP.NET Core 提供了一组预定义的过滤器类型,允许我们在操作或控制器的上下文中控制请求的执行。这些过滤器有不同的用途,可以根据我们的要求进行定制。

以下是 ASP.NET Core 中预定义过滤器的主要类型:

  1. AuthorizationFilter:此过滤器在路由执行开始时执行。它确定是否允许用户访问该路由或执行请求的操作。

  2. ResourceFilter:该过滤器在授权后运行,可用于绕过剩余管道的执行。如果预处理的响应可用,我们想要发送缓存的响应,从而跳过管道的其余部分,那么它非常有用。

  3. ActionFilter:动作过滤器在动作执行之前和之后运行。它提供了一种用自定义逻辑包围操作或在执行操作之前和之后执行其他任务的方法。

  4. ResultFilter:此过滤器在操作生成结果之前和之后执行。它允许我们用附加行为包围结果执行或根据结果执行某些操作。

  5. ExceptionFilter:每当操作或控制器中抛出未捕获的异常时,异常过滤器就会运行。它提供了一种处理异常并设计特定于操作或控制器的自定义错误响应的方法。

  6. ServiceFilter:此过滤器运行另一个过滤器,其类型在其自身内传递。当传递的过滤器类型注册为服务时使用它。它解决通过依赖项注入 (DI) 传递给过滤器类型的任何依赖项。

  7. TypeFilter:与服务过滤器类似,类型过滤器也运行未注册为服务的过滤器类型。它允许我们应用自定义过滤器而无需注册。

        这些预定义的过滤器类型使我们能够灵活地控制请求执行过程,并在 ASP.NET Core 应用程序的管道的不同阶段添加特定行为。

过滤器范围 

        ASP.NET Core 中过滤器的范围取决于它们附加到 MVC 管道的方式。这使我们能够控制过滤器执行的时间和地点。过滤器的主要范围分为三个:

  1. 特定于操作的范围:过滤器可以应用于控制器内的特定操作方法。通过使用过滤器属性修饰操作,过滤器将仅在选择该特定操作来处理传入请求时执行。

  2. 特定于控制器的范围:过滤器也可以应用于整个控制器类。当过滤器应用于控制器级别时,它将针对该控制器内的所有操作执行。

  3. 全局范围:全局过滤器应用于路由中间件匹配和拾取的每条路由。这些过滤器被注册为全局过滤器,并且无论特定操作或控制器如何都会被执行。但是,如果没有为请求选择端点,则不会执行任何过滤器。

        要将过滤器注册为全局过滤器,可以将其添加到 Startup 类中的 ConfigureServices() 方法内的过滤器数组中。这是一个例子:

services.AddControllers(options => {
    options.Filters.Add(typeof(ConsoleGlobalActionFilter));
});

        在此示例中,使用 Add(typeof(...)) 方法将 ConsoleGlobalActionFilter 注册为全局筛选器。这确保过滤器将应用于路由中间件匹配和处理的每个路由。

        通过控制过滤器的范围,我们可以精确地确定过滤器将在 ASP.NET Core 应用程序中应用的时间和位置,从而允许我们添加特定的行为并有效地控制请求执行过程。

如何创建简单的过滤器

有两种方法可以创建我们之前讨论的类型的过滤器。我们以创建ActionFilter为例:

  1. 实现接口:要创建根据操作执行进行操作的自定义操作过滤器,我们可以创建一个实现 IActionFilter 或 IAsyncActionFilter 接口的类。这些接口提供了我们可以重写以添加自定义逻辑的方法。通过实现这些接口,我们可以完全控制操作过滤器的行为。

  2. 扩展属性类:或者,我们可以创建一个扩展 ActionFilterAttribute 类的自定义操作过滤器类。ActionFilterAttribute 类已经实现了 IActionFilter 和 IAsyncActionFilter 接口。通过扩展此类,我们可以重写我们感兴趣的方法并添加自定义逻辑。这种方法提供了一种更方便的方法来创建操作过滤器,因为我们可以直接扩展基类并专注于实现必要的方法。

        同样,对于提到的其他类型的过滤器(如授权过滤器、资源过滤器、结果过滤器、异常过滤器),也有相应的可以实现的接口或可以扩展的属性类。

以下是上述过滤器的接口和属性类:

  • IAuthorizationFilter:可以通过AuthorizationFilterAttribute实现或扩展。

  • IResourceFilter:可以通过ResourceFilterAttribute实现或扩展。

  • IActionFilter:可以通过ActionFilterAttribute实现或扩展。

  • IResultFilter:可以通过ResultFilterAttribute实现或扩展。

  • IExceptionFilter:可以通过ExceptionFilterAttribute实现或扩展。

        其中一些过滤器附带有实现相应接口的属性类,使我们能够灵活地重写适合我们需要的特定方法。以下是一些过滤器的属性类:

  • ActionFilterAttribute:该属性类实现 IActionFilter 和 IAsyncActionFilter 接口。通过扩展这个类,我们可以直接继承这些接口的实现并重写我们感兴趣的方法。这使得我们可以根据我们的需求自定义动作过滤器的行为。

  • ExceptionFilterAttribute:该属性类实现 IExceptionFilter 接口。通过扩展此类并重写其方法,我们可以处理和自定义对操作或控制器中发生的异常的处理。这使我们能够控制异常的处理方式,并允许我们提供自定义错误响应。

  • ResultFilterAttribute:该属性类实现 IResultFilter 接口。通过扩展此类并重写其方法,我们可以在将操作生成的结果发送回客户端之前对其进行修改。这允许我们向结果添加额外的处理或转换。

  • FormatFilterAttribute:此属性类用于指定操作或控制器支持的响应格式。它有助于内容协商并根据客户的偏好选择适当的响应格式。

  • ServiceFilterAttribute:此属性类用于应用注册为服务的过滤器。它允许我们使用依赖项注入来解决过滤器所需的任何依赖项。当过滤器需要额外的服务或依赖项才能正常运行时,这会很有帮助。

  • TypeFilterAttribute:该属性类与ServiceFilterAttribute类似,但它允许我们应用未注册为服务的过滤器。我们可以直接指定过滤器类型及其依赖项。

            例如,ActionFilterAttribute类已经为我们实现了IActionFilter和IAsyncActionFilter接口,因此我们可以直接扩展ActionFilterAttribute类并重写我们感兴趣的方法。

using System; 
using System.Threading.Tasks; 
using Microsoft.AspNetCore.Mvc; 
using Microsoft.AspNetCore.Mvc.Filters; 
using Newtonsoft.Json;  

public class AddResponseHeaderFilter : ActionFilterAttribute 
{     
    // Async method which can surround the action execution
    // Invoked before and after the action execution
    public asyn coverride Task OnActionExecutionAsync(         ActionExecutingContext context, ActionExecutionDelegate next)     
    {         
        // Access the request         
        context.HttpContext.Response.Headers.Add(             
            "X-MyCustomHeader", Guid.NewGuid().ToString());                  
        
        var result = await next.Invoke();                  
        
        // Access the response         
        Console.WriteLine(JsonConvert.SerializeObject(result.Result));     
    } 
}  

[Route("api/[controller]")] 
[ApiController] 
public class HomeController : ControllerBase 
{     
    [AddResponseHeaderFilter]     
    [Route("")]     
    [HttpGet]     
    public IActionResult Index()     
    {         
        return Ok(new { Message = "I'm Alive" });     
    } 
}

        在此示例中,我们通过扩展ActionFilterAttribute创建了自定义AddResponseHeaderFilter类。此过滤器在执行操作之前和之后向 HTTP 响应添加自定义响应标头。它重写OnActionExecutionAsync方法,该方法允许我们自定义操作执行周围的行为。

        然后,我们使用 [ AddResponseHeaderFilter ] 属性将AddResponseHeaderFilter过滤器应用到HomeController类的Index操作。这可确保针对该特定操作执行过滤器。

        调用该操作时,过滤器使用Response.Headers.Add方法将自定义标头添加到响应中。然后它调用 next.Invoke() 方法来继续执行操作。执行操作后,它会访问响应结果并使用JsonConvert.SerializeObject 将其写入控制台。

        通过利用ActionFilterAttribute并创建自定义过滤器类,我们可以扩展和自定义 ASP.NET Core 请求管道的行为,以添加其他功能并根据我们的要求修改响应。

动作过滤器的实现
示例 1:实现 IActionFilter 的同步操作过滤器 

using Microsoft.AspNetCore.Mvc.Filters;

namespace ActionFilters.Filters
{
    public class ActionFilterExample : IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext context)
        {
            // Code executed before the action method executes
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            // Code executed after the action method executes
        }
    }
}

        在此示例中,我们创建一个名为 ActionFilterExample 的类,该类实现 IActionFilter 接口。该接口要求我们实现两个方法:OnActionExecuting 和 OnActionExecuted。OnActionExecuting 方法包含将在操作方法之前执行的代码,而 OnActionExecuted 方法包含将在操作方法之后执行的代码。 

示例 2:实现 IAsyncActionFilter 的异步操作过滤器 

using Microsoft.AspNetCore.Mvc.Filters;
using System.Threading.Tasks;

namespace ActionFilters.Filters
{
    public class AsyncActionFilterExample : IAsyncActionFilter
    {
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            // Code executed before the action method executesvar result = await next();

            // Code executed after the action method executes
        }
    }
}

        在此示例中,我们创建一个名为 AsyncActionFilterExample 的类,该类实现 IAsyncActionFilter 接口。该接口要求我们实现 OnActionExecutionAsync 方法,该方法是动作过滤器的异步版本。该方法接收一个 ActionExecutingContext 和一个 ActionExecutionDelegate。ActionExecutionDelegate 代表管道中的下一个操作,我们可以等待它执行操作方法。通过这样做,我们可以在操作方法执行之前和之后运行代码。

        通过创建实现 IActionFilter 或 IAsyncActionFilter 的类,我们可以定义要在 ASP.NET Core 中的操作方法之前和之后执行的自定义逻辑。这些操作过滤器提供了一种向请求管道添加附加行为并根据需要修改请求或响应的方法。

 操作过滤器的范围
可以在不同范围级别添加操作过滤器:全局、操作和控制器。

1. 全局范围

要全局使用操作过滤器,您可以在ConfigureServices方法的AddControllers()方法中注册它:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(config =>
    {
        config.Filters.Add(new GlobalFilterExample());
    });
}

在.NET 6及更高版本中,由于缺少 Startup 类,您可以使用 Program 类: 

builder.Services.AddControllers(config => 
{ 
    config.Filters.Add( new GlobalFilterExample()); 
});

通过将过滤器添加到全局范围,它将应用于整个应用程序中的所有操作方法。

2. 操作和控制器范围

如果要在操作或控制器级别使用操作过滤器,则需要在ConfigureServices方法中将其注册为IoC容器中的服务:

services.AddScoped<ActionFilterExample>(); 
services.AddScoped<ControllerFilterExample>();

在 .NET 6 及更高版本中: 

builder.Services.AddScoped<ActionFilterExample>(); 
builder.Services.AddScoped<ControllerFilterExample>();

        通过将操作过滤器注册为服务,您可以通过使用相应的过滤器属性装饰它们,有选择地将其应用到特定的操作方法或控制器。

        最后,要使用在操作或控制器级别注册的过滤器,您需要使用ServiceFilter属性将其应用到相应的控制器或操作方法之上
namespace AspNetCore.Controllers
{
    [ServiceFilter(typeof(ControllerFilterExample))]
    [Route("api/[controller]")]
    [ApiController]
    public class TestController : ControllerBase
    {
        [HttpGet]
        [ServiceFilter(typeof(ActionFilterExample))]
        public IEnumerable<string> Get()
        {
            return new string[] { "example", "data" };
        }

    }
}

        通过使用[ServiceFilter]属性并指定过滤器类型,您可以将注册的过滤器(ControllerFilterExample 和 ActionFilterExample)分别与控制器和操作方法关联起来。

        这允许将过滤器应用于指定的范围,控制执行流程并在执行操作方法之前和之后提供附加功能。
        以这种方式使用ServiceFilter属性可以帮助您利用依赖项注入的优势,并将所需的过滤器应用到 ASP.NET 应用程序中的特定控制器和操作方法。

调用顺序
我们的过滤器的执行顺序如下:

我们可以通过向ServiceFilter属性添加一个名为Order 的附加属性来更改多个过滤器的调用顺序 

namespace AspNetCore.Controllers
{
    [ServiceFilter(typeof(ControllerFilterExample), Order=2)]
    [Route("api/[controller]")]
    [ApiController]
    public class TestController : ControllerBase
    {
        [HttpGet]
        [ServiceFilter(typeof(ActionFilterExample), Order=1)]
        public IEnumerable<string> Get()
        {
            return new string[] { "example", "data" };
        }

    }
}

        在本例中,ControllerFilterExample将在ActionFilterExample之前执行,因为它们的顺序值分别为 2 和 1。通过为过滤器分配不同的顺序值,您可以控制它们的执行顺序。

您还可以将多个过滤器应用于同一操作方法并定义它们的执行顺序:
[HttpGet] 
[ServiceFilter(typeof(ActionFilterExample), Order=2)] 
[ServiceFilter(typeof(ActionFilterExample2), Order=1)] 
public IEnumerable<string> Get() 
{ 
    return new string[] { "example", "data ” }; 
}

         在这种情况下,ActionFilterExample2将根据其顺序值分别为 1 和 2,在ActionFilterExample之前执行。

        通过指定每个过滤器的顺序,您可以对执行顺序进行细粒度控制,并且可以应用具有不同优先级的多个过滤器以在 ASP.NET 应用程序中实现所需的行为。

使用操作过滤器改进代码
        为了使用操作过滤器改进代码,让我们重点关注存储库中AppStart文件夹中起始项目的Controllers文件夹中的MoveController类。该控制器包含所有 CRUD 操作的实现。

尽管我们的操作已经干净且可读,但由于全局异常处理,我们可以进一步增强它们。

需要注意的一件重要事情是我们的Movie模型继承自IEntity接口:

[Table("Movie")]
public class Movie: IEntity
{
    [Key]
    public Guid Id { get; set; }
    [Required(ErrorMessage = "Name is required")]
    public string Name { get; set; }
    [Required(ErrorMessage = "Genre is required")]
    public string Genre { get; set; }
    [Required(ErrorMessage = "Director is required")]
    public string Director { get; set; }
}

         现在,让我们关注POSTPUT操作的验证代码。通过将适当的操作过滤器应用于POSTPUT操作,我们可以消除这些操作中显式验证代码的需要,使它们更加简洁和可维护。

使用操作过滤器进行验证
        为了改进POST和PUT操作中的验证代码,我们可以使用操作过滤器。通过将验证逻辑提取到自定义操作过滤器中,我们可以使代码更具可重用性并保持操作更清晰。

        首先,我们在解决方案资源管理器中创建一个名为“ActionFilters”的新文件夹。在该文件夹中,创建一个名为ValidationFilterAttribute的新类:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Linq;

namespace ActionFilters.ActionFilters
{
    public class ValidationFilterAttribute : IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext context)
        {
            var param = context.ActionArguments.SingleOrDefault(p => p.Value is IEntity);
            if (param.Value == null)
            {
                context.Result = new BadRequestObjectResult("Object is null");
                return;
            }
            
            if (!context.ModelState.IsValid)
            {
                context.Result = new UnprocessableEntityObjectResult(context.ModelState);
            }
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {          
        }
    }
}

         在ValidationFilterAttribute类中,我们重写OnActionExecuting方法来执行验证逻辑。我们检查操作参数是否为 IEntity 类型,如果为 null,则返回BadRequestObjectResult。此外,如果 ModelState 无效,我们将返回UnprocessableEntityObjectResult

        接下来,让我们在ConfigureServices方法中将此操作过滤器注册为服务:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<MovieContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("sqlConString")));

    services.AddScoped<ValidationFilterAttribute>();

    services.AddControllers();
}

对于.NET 6,我们需要在 Program 类中使用构建器变量: 

builder.Services.AddDbContext<MovieContext>(options => 
    options.UseSqlServer(Configuration.GetConnectionString( "sqlConString" ))); 

builder.Services.AddScoped<ValidationFilterAttribute>(); 

builder.Services.AddControllers();

最后,从 POST 和 PUT 操作中删除验证代码,并将ValidationFilterAttribute作为服务应用:

[HttpPost]
[ServiceFilter(typeof(ValidationFilterAttribute))]
public IActionResult Post([FromBody] Movie movie)
{
    _context.Movies.Add(movie);
    _context.SaveChanges();

    return CreatedAtRoute("MovieById", new { id = movie.Id }, movie);
}

[HttpPut("{id}")]
[ServiceFilter(typeof(ValidationFilterAttribute))]
public IActionResult Put(Guid id, [FromBody] Movie movie)
{
    var dbMovie = _context.Movies.SingleOrDefault(x => x.Id.Equals(id));
    if (dbMovie == null)
    {
        return NotFound();
    }

    dbMovie.Map(movie);

    _context.Movies.Update(dbMovie);
    _context.SaveChanges();

    return NoContent();
}

        通过将 ValidationFilterAttribute 应用为服务过滤器,我们无需在操作中使用验证代码。代码现在更干净、更具可读性。此外,只要我们的模型类继承自 IEntity 接口,此验证逻辑就可以重用。

        为了确保操作过滤器的验证优先于 [ApiController] 属性的默认验证行为,我们需要禁止默认验证。在 Startup 类(或 .NET 6 的 Program 类)中,添加以下配置:
services.Configure<ApiBehaviorOptions>(options => 
{ 
    options.SuppressModelStateInvalidFilter = true ; 
});

         此配置可确保返回操作过滤器的验证结果(例如,UnprocessableEntity),而不是返回验证错误的默认BadRequest结果。

        通过这些更改,我们的验证过滤器已准备好进行测试。

动作过滤器中的依赖注入
        为了消除通过 ID 从数据库中获取电影并在 GetById、DELETE 和 PUT 操作中检查其是否存在的代码重复,我们可以创建一个新的操作过滤器来执行此任务。我们还将使用依赖注入将 MovieContext 注入到动作过滤器中。

让我们在 ActionFilters 文件夹中创建一个名为 ValidateEntityExistsAttribute<T> 的新操作过滤器类:

using System; 
using System.Linq; 
using Microsoft.AspNetCore.Mvc; 
using Microsoft.AspNetCore.Mvc.Filters;  

namespace ActionFilters.ActionFilters 
{     
    public class ValidateEntityExistsAttribute<T> : IActionFilter where T : class, IEntity     
    {         
        private readonly MovieContext _context;          
    
        public ValidateEntityExistsAttribute(MovieContext context)         
        {             
            _context = context;         
        }          
        
        public void OnActionExecuting(ActionExecutingContext context)         
        {             
            Guid id = Guid.Empty;              
            
            if (context.ActionArguments.ContainsKey("id"))             
            {                 
                id = (Guid)context.ActionArguments["id"];             
            }             
            else             
            {                 
                context.Result = new BadRequestObjectResult("Bad id parameter");                 
                return;             
            }              
                
            var entity = _context.Set<T>().SingleOrDefault(x => x.Id.Equals(id));             
            if (entity == null)             
            {                 
                context.Result = new NotFoundResult();             
            }             
            else             
            {                 
                context.HttpContext.Items.Add("entity", entity);             
            }         
        }          
        public void OnActionExecuted(ActionExecutedContext context)         
        {         
        }     
     } 
} 

        在ValidateEntityExistsAttribute<T>类中,我们使用依赖注入通过构造函数注入MovieContext 。该类是通用的,以便它可以重用于我们项目中的任何模型。在OnActionExecuting方法中,我们从操作上下文中获取 ID 参数并检查该实体是否存在于数据库中。如果找到实体,我们会将其存储在HttpContext .Items 集合中,以便稍后在操作方法中使用。

        接下来,让我们在ConfigureServices方法中注册操作过滤器:

 services.AddScoped<ValidateEntityExistsAttribute<Movie>>();

        对于 .NET 6,我们在 Program 类中使用构建器变量: 

builder.Services.AddScoped<ValidateEntityExistsAttribute<Movie>>();

最后,修改我们的操作以将 ValidateEntityExistsAttribute 应用为服务过滤器:

[HttpGet("{id}", Name = "MovieById")] [ServiceFilter(typeof(ValidateEntityExistsAttribute<Movie>))] 
public IActionResult Get(Guid id) 
{     
    var dbMovie = HttpContext.Items["entity"] as Movie;      
    return Ok(dbMovie); 
}  

[HttpPut("{id}")] 
[ServiceFilter(typeof(ValidationFilterAttribute))] 
[ServiceFilter(typeof(ValidateEntityExistsAttribute<Movie>))] 
public IActionResult Put(Guid id, [FromBody]Movie movie) 
{     
    var dbMovie = HttpContext.Items["entity"] as Movie;      
    
    dbMovie.Map(movie);      
    
    _context.Movies.Update(dbMovie);     
    _context.SaveChanges();      
    
    return NoContent(); 
}  

[HttpDelete("{id}")] 
[ServiceFilter(typeof(ValidateEntityExistsAttribute<Movie>))] 
public IActionResult Delete(Guid id) 
{     
    var dbMovie = HttpContext.Items["entity"] as Movie;      
    
    _context.Movies.Remove(dbMovie);     
    _context.SaveChanges();      
    
    return NoContent(); 
} 

        通过应用ValidateEntityExistsAttribute<Movie>作为服务过滤器,我们确保从数据库中获取电影实体并通过HttpContext.Items在操作方法中可用。这消除了在获取实体并检查其存在时重复代码的需要。

        通过这些更改,我们的操作更加清晰、更具可读性,并且通过 ID 获取实体的代码现在可以重用。

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

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

相关文章

Idea设置代理后无法clone git项目

背景 对于我们程序员来说&#xff0c;经常上github找项目、找资料是必不可少的&#xff0c;但是一些原因&#xff0c;我们访问的时候速度特别的慢&#xff0c;需要有个代理&#xff0c;才能正常的访问。 今天碰到个问题&#xff0c;使用idea工具 clone项目&#xff0c;速度特…

三、防御保护---防火墙安全策略篇

三、防御保护---防火墙安全策略篇 一、什么是安全策略二、安全策略的组成1.匹配条件2.动作3.策略标识 三、防火墙的状态检测和会话表1.会话表2.状态检测技术 四、ASPF--隐形通道五、用户认证1.用户认证的分类2.认证方式3.认证策略4.认证域 一、什么是安全策略 传统的包过滤防火…

计算机毕业设计 基于SpringBoot的车辆违章信息管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

Android 中的动态应用程序图标

Android 中的动态应用程序图标 一、需求二、解决方案三、方案实现四、结论 一、需求 您可能遇到过那些可以实现巧妙技巧的应用程序 - 更改应用程序图标&#xff08;也许是在您的生日那天&#xff09;&#xff0c;然后无缝切换回常规图标。这种功能会激起你的好奇心&#xff0c…

websocket 通信协议

websocket是什么 答: 它是一种网络通信协议&#xff0c;是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。 意思就是服务器可以主动向客户端推送信息&#xff0c;客户端也可以主动向服务器发送信息 属于服务器推送技术的一种. 为什么需要websocket? 疑问?…

Java 面试题之 IO(一)

字节流 文章目录 字节流InputStream&#xff08;字节输入流&#xff09;OutputStream&#xff08;字节输出流&#xff09; 文章来自Java Guide 用于学习如有侵权&#xff0c;立即删除 InputStream&#xff08;字节输入流&#xff09; InputStream用于从源头&#xff08;通常是…

【command】使用nr简化npm run命令

参考文章 添加 alias nrnpm run通过alias启动命令可以帮助我们节省运行项目输入命令的时间 $ cd ~ $ vim .bash_profile $ source ~/.bashrc

【51单片机Keil+Proteus8.9】门锁控制电路

门锁控制电路 二、设计思路 电路设计 1.电源部分&#xff1a;使用BATTERY为整个电路提供电源&#xff0c;可以在电路中加入一个电 源开关&#xff0c;以便控制电源的开启和关闭。 2.处理器部分&#xff1a;使用AT89C51芯片作为主处理器&#xff0c;通过编写程序实现门锁的 …

【Java IO 源码详解】: InputStream

本文主要从JDK 11 源码角度分析InputStream。 Java IO - 源码: InputStream InputStream 类实现关系InputStream 抽象类源码实现InputStreamFilterInputStreamByteArrayInputStreamBufferedInputStream 参考文章 InputStream 类实现关系 InputStream是输入字节流&#xff0c;具…

来聊聊大厂面试题:求Java对象的大小

写在文章开头 日常使用Java进行业务开发时&#xff0c;我们基本不关心一个Java对象的大小&#xff0c;所以经常因为错误的估算导致大量的内存空间在无形之间被浪费了&#xff0c;所以今天笔者就基于这篇文章来聊聊一个Java对象的大小。 你好&#xff0c;我叫sharkchili&#x…

网络体系结构 和网络原理之UDP和TCP

目录 网络分层 一. 应用层 http协议 二. 传输层 1. 介绍 2.UDP协议 (1)组成 (2)细节 3.TCP协议 (1)特性如下链接&#xff1a; (2)组成 (3)特点 三. 网络层 四. 数据链路层 1.介绍 2.以太网协议 3.mac地址和ip地址 五. 物理层 DNS 网络分层 一. 应用层 应用程序 现成的…

【深度优先搜索】【组合数学】【动态规划】1467.两个盒子中球的颜色数相同的概率

作者推荐 【动态规划】【字符串】【行程码】1531. 压缩字符串 本文涉及知识点 动态规划汇总 深度优先搜索 组合数学 LeetCode1467 两个盒子中球的颜色数相同的概率 桌面上有 2n 个颜色不完全相同的球&#xff0c;球上的颜色共有 k 种。给你一个大小为 k 的整数数组 balls …

数据写入HBase(scala)

package sourceimport org.apache.hadoop.hbase.{HBaseConfiguration, TableName} import org.apache.hadoop.hbase.client.{ConnectionFactory, Put} import org.apache.hadoop.hbase.util.Bytesobject ffff {def main(args: Array[String]): Unit {//hbase连接配置val conf …

c++连接mysql

c连接mysql 安装mysql以及c对应的库进入数据库&#xff0c;创建数据库&#xff0c;表&#xff0c;并新建管理员用户编写c代码编译运行&#xff0c;测试结果头文件解释 安装mysql以及c对应的库 sudo apt-get update sudo apt-get install mysql-server sudo apt-get install li…

2023年算法CDO-CNN-BiLSTM-ATTENTION回归预测(matlab)

2023年算法CDO-CNN-BiLSTM-ATTENTION回归预测&#xff08;matlab&#xff09; CDO-CNN-BiLSTM-Attention切诺贝利灾难优化器优化卷积-长短期记忆神经网络结合注意力机制的数据回归预测 Matlab语言。 切诺贝利灾难优化器Chernobyl Disaster Optimizer (CDO)是H. Shehadeh于202…

新书推荐——《趣读数字经济》

文章目录 缘起:“躺嬴”的一天/ 001 第1章 名花解语,石心铁肠&#xff0c;当属“人工智能”/ 009 1.1 自学成才的人工智能/ 011 1.2 狂飙的话病ChatGPT / 017 1.3 算力、算法与数据:人工智能的核心/ 026 1.4 人工智能会抢走我们的饭碗吗/032 1.5 人工智能有多能/ 036 1.6 AI…

地址解析工具---AddressParseUtil

一、工具源码 package com.rural_vibration.common.utils;import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern;/*** description: 地址解析工具 <…

FTP服务之WindowsServer2019中搭建私有FTP服务器

WindowsServer2019搭建FTP服务器 文章目录 WindowsServer2019搭建FTP服务器1. 查看FTP服务是否开启2. 配置FTP服务站点3. 访问 1. 查看FTP服务是否开启 WindowsServer2019默认是开启FTP服务的&#xff0c;如果未开启&#xff0c;则按下面步骤开启即可 打开服务器管理 添加角色和…

java OA办公自动化系统

java OA办公自动化系统&#xff0c;java项目&#xff0c;springboot项目。eclipse和idea都能打开运行。 前端技术&#xff1a;Bootstrap&#xff0c;Jquery&#xff0c;My97 DatePicker&#xff0c;kindeditor&#xff0c;freemarker 后端技术&#xff1a;SpringBoot&#xf…

KVM 内存概述

KVM 内存概述 CPU缓存基本概念内存基本概念EPT和VPID内存过载使用大页透明大页透明大页使用 KSM NUMA CPU缓存基本概念 CPU工作过程中会直接读取内存的数据&#xff0c;而大部分同学对内存的感觉是内存条的一个概念&#xff0c;其实CPU中也有内存的概念&#xff0c;称之为L1-L…