前言

上一篇文章我们讲到了Scrutor第一个核心功能Scanning,本文讲解的是Scrutor第二个核心的功能Decoration 装饰器模式在依赖注入中的使用。

  • 装饰器模式允许您向现有服务类中添加新功能,而无需改变其结构
Install-Package Scrutor

本文的完整源代码在文末

Decoration 依赖注入代理模式

首先首先一个 获取 User 的服务

定义 User 类

public class User
{
public int Id { get; set; }
public string Name { get; set; } public int Age { get; set; } public string? Email { get; set; }
}

定义接口和实现

public interface IUserService
{
List<User> GetAllUsers();
} public class UserService : IUserService
{
public List<User> GetAllUsers()
{
Console.WriteLine("GetAllUsers方法被调用~");
List<User> users = [
new User(){
Id= 1,
Name="张三",
Age=18,
Email="zhangsan@163.com"
},
new User(){
Id= 2,
Name="李四",
Age=19,
Email="lisi@163.com"
},
];
return users!;
}
}

现在有了我们的获取全部用户的服务了,需求是在不破坏当前类的添加装饰器模式,为 GetAllUsers 接口添加缓存。

创建装饰器类

public class UserDecorationService(IUserService userService, IMemoryCache cache) : IUserService
{
public List<User> GetAllUsers()
{
Console.WriteLine("GetAllUsers代理方法被调用~");
return cache.GetOrCreate("allUser", cacheEntry =>
{
cacheEntry.SetAbsoluteExpiration(
TimeSpan.FromMinutes(5));
var allUsers = userService.GetAllUsers();
return allUsers ?? [];
})!;
}
}

DI 容器添加服务

 builder.Services.AddTransient<IUserService, UserService>();
builder.Services.AddMemoryCache();
builder.Services.Decorate<IUserService, UserDecorationService>();

创建接口测试一下

app.MapGet("/GetAllUsers", ([FromServices] IUserService userService) => userService.GetAllUsers()).WithSummary("获取全部用户接口");

调用第一次

GetAllUsers代理方法被调用~
GetAllUsers方法被调用~

第二次调用

GetAllUsers代理方法被调用~

可以看出第一次没缓存装饰器类和我们 UserService 都调用了,第二次因为只有了缓存所以只调用了装饰器类,可以看出我们的装饰器模式生效了。

依赖注入装饰器底层核心实现

    /// <summary>
/// Decorates all registered services using the specified <paramref name="strategy"/>.
/// </summary>
/// <param name="services">The services to add to.</param>
/// <param name="strategy">The strategy for decorating services.</param>
public static bool TryDecorate(this IServiceCollection services, DecorationStrategy strategy)
{
Preconditions.NotNull(services, nameof(services));
Preconditions.NotNull(strategy, nameof(strategy)); var decorated = false; for (var i = services.Count - 1; i >= 0; i--)
{
var serviceDescriptor = services[i]; if (IsDecorated(serviceDescriptor) || !strategy.CanDecorate(serviceDescriptor))
{
continue;
} var serviceKey = GetDecoratorKey(serviceDescriptor);
if (serviceKey is null)
{
return false;
} // Insert decorated
services.Add(serviceDescriptor.WithServiceKey(serviceKey)); // Replace decorator
services[i] = serviceDescriptor.WithImplementationFactory(strategy.CreateDecorator(serviceDescriptor.ServiceType, serviceKey)); decorated = true;
} return decorated;
}

这个代码是在 dotNet8 的环境下编译的,可以看出做了几件事:

第一 IServiceCollection 集合倒序遍历,找到符合条件的ServiceType

核心代码一

// Insert decorated
services.Add(serviceDescriptor.WithServiceKey(serviceKey));

将原先的ServiceDescription作为基础,添加了ServiceKey后再进行添加操作,新的服务描述符会被添加到服务集合的末尾,

核心代码二

 // Replace decorator
services[i] = serviceDescriptor.WithImplementationFactory(strategy.CreateDecorator(serviceDescriptor.ServiceType, serviceKey));

这一步是将原有的服务描述符替换为一个新的服务描述符,新的服务描述符使用装饰器工厂方法创建,实现了服务的装饰功能。

用的时候

app.MapGet("/GetAllUsers", ([FromServices] IUserService userService) => userService.GetAllUsers()).WithSummary("获取全部用户接口");

这样就可以获取到装饰器类提供服务,之前看到services.Add(serviceDescriptor.WithServiceKey(serviceKey));在代码的最后添加了一个服务,那 IOC 获取的时候肯定是从后面优先获取,这地方用了 dotNet8 的键控依赖注入(KeyedService),以 ServiceType 获取服务只会获取到我们提供的装饰器实例,这一手简直是神来之笔 。

最后

Scrutor的装饰器模式可以用于动态地给依赖注入的实例添加额外职责,实现动态增加和撤销功能,而无需改变原有对象结构。可以在不影响其他对象的情况下,以透明且动态的方式给对象添加新功能,实现系统的灵活扩展和维护。

本文完整源代码

.Net依赖注入神器Scrutor(下)的更多相关文章

  1. 【半小时大话.net依赖注入】(下)详解AutoFac+实战Mvc、Api以及.NET Core的依赖注入

    系列目录 上|理论基础+实战控制台程序实现AutoFac注入 下|详解AutoFac+实战Mvc.Api以及.NET Core的依赖注入 前言 本来计划是五篇文章的,每章发个半小时随便翻翻就能懂,但是 ...

  2. <Pro .NET MVC4> 三大工具之依赖注入神器——Ninject

    这篇内容是对<Pro .NET MVC4>一书中关于Ninject介绍的总结. Ninject是.NET MVC的一款开源的依赖注入工具. 使用场景:当MVC项目中使用了依赖注入技术来给程 ...

  3. 从零开始,搭建博客系统MVC5+EF6搭建框架(2),测试添加数据、集成Autofac依赖注入

    一.测试仓储层.业务层是否能实现对数据库表的操作 1.创建IsysUserInfoRepository接口来继承IBaseRepository父接口 namespace Wchl.WMBlog.IRe ...

  4. Spring的核心机制——依赖注入(Dependency Inject)

    Spring不仅提供对象,还提供对象的属性值,而不是由使用该对象的程序所提供的. Java应用是由一些相互协作的对象所组成的,在Spring中这种相互协作的关系就叫依赖关系. 如果A组件调用了B组件的 ...

  5. (五)Angularjs - 依赖注入

    如何找到API? AngularJS提供了一些功能的封装,但是当你试图通过全局对象angular去 访问这些功能时,却发现与以往遇到的库大不相同. 比如,AngularJS暴露了一个全局对象:angu ...

  6. AngularJS应用开发思维之3:依赖注入

    找不到的API? AngularJS提供了一些功能的封装,但是当你试图通过全局对象angular去 访问这些功能时,却发现与以往遇到的库大不相同. $http 比如,在jQuery中,我们知道它的AP ...

  7. 基于ABP模块组件与依赖注入组件的项目插件开发

    注意,阅读本文,需要先阅读以下两篇文章,并且对依赖注入有一定的基础. 模块系统:http://www.cnblogs.com/mienreal/p/4537522.html 依赖注入:http://w ...

  8. angular2的依赖注入

    更好阅读体验,请看原文 在读这篇文章之前,你要先了解一下什么是依赖注入,网上关于这个的解释很多,大家可以自行Google. 我们这一篇文章还是以QuickStart项目为基础,从头开始讲解怎么在Ang ...

  9. Spring的核心机制——依赖注入(Dependency Inject)

    Spring不仅提供对象,还提供对象的属性值,而不是由使用该对象的程序所提供的. Java应用是由一些相互协作的对象所组成的,在Spring中这种相互协作的关系就叫依赖关系. 如果A组件调用了B组件的 ...

  10. SignalR中的依赖注入

    什么是依赖注入? 如果你已经熟悉依赖注入可以跳过此节. 依赖注入 (DI) 模式下,对象并不为自身的依赖负责. 下边的例子是一个主动 DI. 假设你有个对象需要消息日志.你可能定义了一个日志接口: C ...

随机推荐

  1. 《ASP.ENT Core 与 RESTful API 开发实战》-- (第5章)-- 读书笔记(下)

    第 5 章 使用 Entity Framework Core 5.4 重构 Controller 和 Action 重构 AuthorController 构造函数重构 public IMapper ...

  2. JS leetcode 搜索插入位置 题解分析

    壹 ❀ 引 今天来做一道特别特别简单的题,来自leetcode35. 搜索插入位置,题目描述如下: 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引.如果目标值不存在于数组中,返回它将会 ...

  3. JS 这一次彻底理解冒泡排序

    壹 ❀ 引 在面试环节中,算法总是逃不掉的一关,对于我这种非班科出生且大学不接触数学的人来说,逻辑思维方面确实较为欠缺,昨晚跟百度的同学聊到凌晨,自我感觉差距较大,受了不小打击,所以决心抓一抓算法,做 ...

  4. NC50243 小木棍

    题目链接 题目 题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50.现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度.给出每段小木 ...

  5. 【分布式】load balance 03-一致性哈希算法 java 实现

    负载均衡系列专题 01-负载均衡基础知识 02-一致性 hash 原理 03-一致性哈希算法 java 实现 04-负载均衡算法 java 实现 本节我们来看一下如何实现一个一致性 hash 框架. ...

  6. mktemp命令

    mktemp命令 mktemp命令用于安全地创建一个临时文件或目录,并输出其名称,TEMPLATE在最后一个组件中必须至少包含3个连续的X,如果未指定TEMPLATE,则使用tmp.XXXXXXXXX ...

  7. Spring Boot图书管理系统项目实战-8.续借图书

    导航: pre:  7.借阅图书 next:9.归还图书 只挑重点的讲,具体的请看项目源码. 1.项目源码 需要源码的朋友,请捐赠任意金额后留下邮箱发送:) 2.页面设计 2.1 bookReBorr ...

  8. spark conf、config配置项总结

    1.structured-streaming的state 配置项总结 Config Name Description Default Value spark.sql.streaming.stateSt ...

  9. Java 异常整合练习

    1 package com.bytezero.throwable2; 2 3 /** 4 * 5 * @Description 异常练习 6 * @author Bytezero·zhenglei! ...

  10. Java 方法的重载(overload)

    1 /* 2 * 3 * 方法的重载(overload) 4 * 1.定义:在同一个类中,允许存在一个以上的同名方法,只要他们的参数个数或者参数类型不同 5 * 6 * "两同一不同&quo ...