本节介绍Util应用框架依赖注入的使用和配置扩展.

文章分为多个小节,如果对设计原理不感兴趣,只需阅读基础用法部分即可.

概述

当你想调用某个服务的方法完成特定功能时,首先需要得到这个服务的实例.

最简单的办法是直接 new 一个服务实例,不过这样就把服务的实现牢牢绑死了,当你需要更换实现,除了直接修改它没有别的办法.

依赖注入是一种获取服务实例更好的方法.

通常需要先定义服务接口,然后在你的构造方法声明这些接口参数.

服务实例不是你创建的,而是从外部传入的.

你只跟服务接口打交道,所以不会被具体的实现类绑死.

依赖注入框架

现在每个服务都在自己的构造方法定义参数接收依赖项,但是最终必须在某处真正创建这些服务实例.

使用new手工创建服务实例是不可行的,因为存在依赖链,比如使用 new A() 创建服务A的实例时,服务A可能依赖服务B,需要先创建服务B的实例,而服务B可能还有依赖.

另外,某些服务可能需要特定的生命周期,比如工作单元服务,在单个请求过程,每次注入的工作单元实例必须是同一个.

我们需要一种机制,能够自动创建具有依赖的服务实例,并管理实例的生命周期.

Asp.Net Core 内置了构造方法依赖注入能力.

通过构造方法注入服务实例,是依赖注入最常见的形式.

一些专门的依赖注入框架,比如 autofac 支持属性注入等高级功能.

Util应用框架使用Asp.Net Core内置的依赖注入,对于大部分业务场景,构造方法注入已经足够了.

依赖注入生命周期

依赖注入有三种生命周期.

  • Singleton 单例

    在整个系统只创建一个实例.

    无状态或不可变的服务才能设置成单例.

  • Scope 每个请求创建一个实例

    对于 Asp.Net Core 环境,每个请求创建一个实例,在整个请求过程,获取的是同一个实例,在请求结束时销毁.

    注意: 对于非 Asp.Net Core 环境,Scope 生命周期与 Singleton 相同.

    在Util项目中,与工作单元相关的服务都需要设置成 Scope 生命周期,比如 工作单元,仓储,领域服务,应用服务等.

  • Transient 每次调用创建一个新实例

    每次注入都会创建一个新的服务实例.

依赖注入最佳实践

一个接口配置一个实现

定义接口的目的是为了方便切换实现.

一个接口可能有多个实现类,但是在同一时间,应该只有一个实现类生效.

举个例子,仓储接口有两个实现类.

  1. /// <summary>
  2. /// 仓储
  3. /// </summary>
  4. public interface IRepository {
  5. }
  6. /// <summary>
  7. /// 仓储1
  8. /// </summary>
  9. public class Repository1 : IRepository {
  10. }
  11. /// <summary>
  12. /// 仓储2
  13. /// </summary>
  14. public class Repository2 : IRepository {
  15. }

有两个应用服务,服务1需要仓储1的实例,服务2需要仓储2的实例.

  1. /// <summary>
  2. /// 服务1
  3. /// </summary>
  4. public class Service1 {
  5. public Service1( IRepository repository ) {
  6. }
  7. }
  8. /// <summary>
  9. /// 服务2
  10. /// </summary>
  11. public class Service2 {
  12. public Service2( IRepository repository ) {
  13. }
  14. }

现在, IRepository有两个实例,并且这两个实例都处于使用状态.

两个服务都注入了 IRepository 接口, 如何把正确的仓储实例注入到指定的服务中?

一些依赖注入框架可以为特定实现类命名,然后为服务传递特定命名的依赖项,不过这种方法复杂且容易出错.

一种简单有效的方法是创建更具体的接口,从而让每种生效的实现类只有一个.

  1. /// <summary>
  2. /// 仓储
  3. /// </summary>
  4. public interface IRepository {
  5. }
  6. /// <summary>
  7. /// 仓储1
  8. /// </summary>
  9. public interface IRepository1 : IRepository {
  10. }
  11. /// <summary>
  12. /// 仓储2
  13. /// </summary>
  14. public interface IRepository2 : IRepository {
  15. }
  16. /// <summary>
  17. /// 仓储1
  18. /// </summary>
  19. public class Repository1 : IRepository1 {
  20. }
  21. /// <summary>
  22. /// 仓储2
  23. /// </summary>
  24. public class Repository2 : IRepository2 {
  25. }
  26. /// <summary>
  27. /// 服务1
  28. /// </summary>
  29. public class Service1 {
  30. public Service1( IRepository1 repository ) {
  31. }
  32. }
  33. /// <summary>
  34. /// 服务2
  35. /// </summary>
  36. public class Service2 {
  37. public Service2( IRepository2 repository ) {
  38. }
  39. }

由于注入了更具体的接口,所以不需要特定的依赖配置方法.

不要奇怪,虽然现在每个接口只有一个实现,但你在任何时候都可以增加实现类进行切换.

唯一需要记住的是,任何时候,生效的实现类应该只有一个.

依赖注入的使用范围

通常对服务类型使用依赖注入,比如控制器,应用服务,领域服务,仓储等.

实体可能也包含某些依赖项,但不能使用依赖注入框架创建实体.

简单实体使用 new 创建,更复杂的实体创建过程使用工厂进行封装.

基础用法

通过构造方法获取依赖服务

只需在构造方法定义需要的服务参数即可.

范例:

  1. /// <summary>
  2. /// 测试服务
  3. /// </summary>
  4. public class TestService {
  5. public TestService( ITestRepository repository ) {
  6. }
  7. }

配置依赖服务

Asp.Net Core 标准的依赖配置方法是调用 IServiceCollection 扩展方法.

范例:

配置 ITestService 接口的实现类为 TestService,生命周期为 Scope.

  1. var builder = WebApplication.CreateBuilder( args );
  2. builder.Services.AddScoped<ITestService, TestService>();

不过,大部分时候,你都不需要手工配置依赖服务,它由Util应用框架自动扫描配置.

依赖配置扩展

Util应用框架提供了三个接口,用于自动配置相应生命周期的依赖服务.

  • Util.Dependency.ISingletonDependency

    配置生命周期为 Singleton 的服务.

  • Util.Dependency.IScopeDependency

    配置生命周期为 Scope 的服务.

  • Util.Dependency.ITransientDependency

    配置生命周期为 Transient 的服务.

限制: 必须把 ISingletonDependency 这三个接口放在需要配置的接口上,不能放在实现类上.

范例:

服务基接口 IService 继承了 IScopeDependency 接口.

所有继承了 IService 的服务接口,在启动时自动查找相应的实现类,并设置为 Scope 服务.

  1. /// <summary>
  2. /// 服务
  3. /// </summary>
  4. public interface IService : IScopeDependency {
  5. }

更改实现类依赖配置优先级

当使用 ISingletonDependency 等接口自动配置依赖关系时,如果服务接口有多个实现类,究竟哪个生效?

Util应用框架提供了 Util.Dependency.IocAttribute 特性,用于更改依赖优先级,从而精确指定实现类.

范例:

服务 Service1 实现了服务接口 IService, IService 从 IScopeDependency 继承.

实现类的默认优先级为 0.

IocAttribute 特性接收一个表示优先级的整数,值越大,表示优先级越高.

服务 Service2 的依赖优先级设置为 1,比 Service1 大,所以注入 IService 接口的实现类是 Service2.

  1. /// <summary>
  2. /// 服务1
  3. /// </summary>
  4. public class Service1 : IService {
  5. }
  6. /// <summary>
  7. /// 服务2
  8. /// </summary>
  9. [Ioc(1)]
  10. public class Service2 : IService {
  11. }

服务定位器

构造方法依赖注入简单清晰,只需查看构造方法就能了解依赖的服务.

不过它也带来了一些问题.

如果服务基类使用了构造方法依赖注入,每当依赖服务发生变化,都需要修改所有子类的构造方法,这会导致架构的脆弱性.

另一个问题是无法通过依赖注入为静态方法提供依赖项.

在业务场景使用静态方法是一种陋习,需要坚决抵制.

但是某些工具类使用静态方法可能更方便.

服务定位器概述

服务定位器从对象容器中主动拉取依赖服务.

依赖注入和服务定位器都从对象容器获取依赖项,但依赖注入的依赖项是从外部被动推入的.

服务定位器比依赖注入的耦合度高,也更难测试,不过它能解决之前提到的问题.

为了让服务基类稳定,可以在基类构造方法获取 IServiceProvider 参数.

IServiceProvider 是 .Net 服务提供程序,可以调用它获取依赖服务.

下面来看看Util应用服务基类.

  1. /// <summary>
  2. /// 应用服务
  3. /// </summary>
  4. public abstract class ServiceBase : IService {
  5. /// <summary>
  6. /// 初始化应用服务
  7. /// </summary>
  8. /// <param name="serviceProvider">服务提供器</param>
  9. protected ServiceBase( IServiceProvider serviceProvider ) {
  10. ServiceProvider = serviceProvider ?? throw new ArgumentNullException( nameof( serviceProvider ) );
  11. Session = serviceProvider.GetService<ISession>() ?? NullSession.Instance;
  12. IntegrationEventBus = serviceProvider.GetService<IIntegrationEventBus>() ?? NullIntegrationEventBus.Instance;
  13. var logFactory = serviceProvider.GetService<ILogFactory>();
  14. Log = logFactory?.CreateLog( GetType() ) ?? NullLog.Instance;
  15. }
  16. /// <summary>
  17. /// 服务提供器
  18. /// </summary>
  19. protected IServiceProvider ServiceProvider { get; }
  20. /// <summary>
  21. /// 用户会话
  22. /// </summary>
  23. protected ISession Session { get; }
  24. /// <summary>
  25. /// 集成事件总线
  26. /// </summary>
  27. protected IIntegrationEventBus IntegrationEventBus { get; }
  28. /// <summary>
  29. /// 日志操作
  30. /// </summary>
  31. protected ILog Log { get; }
  32. }

应用服务基类定义了用户会话和日志操作等依赖项,但不是从构造方法获取的,而是调用服务提供程序 IServiceProviderGetService 方法.

通过传递 IServiceProvider 参数,服务子类不需要在构造方法声明用户会话等其它依赖项,减轻了负担.

当依赖项发生变化时,不需要修改基类的构造方法参数,直接通过服务提供程序获取依赖.

构造方法获取 IServiceProvider 参数解决了服务基类的问题,但 IServiceProvider 参数本身还是通过依赖注入方式提供的.

无法通过依赖注入为静态工具类传递参数,在静态工具方法中传递 IServiceProvider 参数又会导致API难用.

服务定位器工具类

一个常见的需求是在静态工具方法中获取当前 HttpContext 实例,并访问它的某些功能.

在更早的 Asp.Net 中, 我们可以通过 HttpContext.Current 静态属性来获取当前Http上下文.

但 Asp.Net Core 已经抛弃这种用法,现在需要先依赖注入 IHttpContextAccessor 实例,并使用它获取当前Http上下文.

Util提供了一个服务定位器工具类 Util.Helpers.Ioc .

通过调用 Ioc 静态方法 Create 就能获取依赖服务.

范例:

下面的例子演示了如何在静态方法中获取远程IP地址.

先通过 Ioc.Create 获取Http上下文访问器, 然后得到当前Http上下文,调用它的 Connection.RemoteIpAddress 获取远程IP地址.

  1. public static class Tool {
  2. /// <summary>
  3. /// 获取客户端Ip地址
  4. /// </summary>
  5. public static string GetIp() {
  6. var httpContext = Ioc.Create<IHttpContextAccessor>()?.HttpContext;
  7. return httpContext?.Connection.RemoteIpAddress?.ToString();
  8. }
  9. }

使用 Ioc.Create 方法获取依赖项要小心,只有在 Asp.Net Core 环境中才能安全使用.

在后台任务等其它环境中, Ioc.Create 与依赖注入使用的对象容器可能不同.

由于它具有副作用, Util静态工具方法已经很少使用它.

Util.Helpers.Ioc 现在用在不太重要的一些场景,业务开发中应严格使用依赖注入获取依赖.

Util应用框架提供了另一个工具类 Util.Helpers.Web 来支持 Asp.Net Core 静态工具方法.

使用 Util.Helpers.Web 改造上面的例子.

  1. public static class Tool {
  2. /// <summary>
  3. /// 获取客户端Ip地址
  4. /// </summary>
  5. public static string GetIp() {
  6. return Web.HttpContext?.Connection.RemoteIpAddress?.ToString();
  7. }
  8. }

你可以通过 Web.HttpContext 获取当前Http上下文,比使用 Ioc.Create 方便得多.

源码解析

DependencyServiceRegistrar 依赖服务注册器

依赖服务注册器提供对 Util.Dependency.ISingletonDependency 等接口的依赖配置扩展支持.

通过类型查找器分别查找实现了 ISingletonDependency,IScopeDependency,ITransientDependency 三个接口的所有class.

对每个class类,查找它们的接口,并注册相应生命周期的依赖关系.

  1. /// <summary>
  2. /// 依赖服务注册器 - 用于扫描注册ISingletonDependency,IScopeDependency,ITransientDependency
  3. /// </summary>
  4. public class DependencyServiceRegistrar : IServiceRegistrar {
  5. /// <summary>
  6. /// 获取服务名
  7. /// </summary>
  8. public static string ServiceName => "Util.Infrastructure.DependencyServiceRegistrar";
  9. /// <summary>
  10. /// 排序号
  11. /// </summary>
  12. public int OrderId => 100;
  13. /// <summary>
  14. /// 是否启用
  15. /// </summary>
  16. public bool Enabled => ServiceRegistrarConfig.IsEnabled( ServiceName );
  17. /// <summary>
  18. /// 注册服务
  19. /// </summary>
  20. /// <param name="serviceContext">服务上下文</param>
  21. public Action Register( ServiceContext serviceContext ) {
  22. return () => {
  23. serviceContext.HostBuilder.ConfigureServices( ( context, services ) => {
  24. RegisterDependency<ISingletonDependency>( services, serviceContext.TypeFinder, ServiceLifetime.Singleton );
  25. RegisterDependency<IScopeDependency>( services, serviceContext.TypeFinder, ServiceLifetime.Scoped );
  26. RegisterDependency<ITransientDependency>( services, serviceContext.TypeFinder, ServiceLifetime.Transient );
  27. } );
  28. };
  29. }
  30. /// <summary>
  31. /// 注册依赖
  32. /// </summary>
  33. private void RegisterDependency<TDependencyInterface>( IServiceCollection services, ITypeFinder finder, ServiceLifetime lifetime ) {
  34. var types = GetTypes<TDependencyInterface>( finder );
  35. var result = FilterTypes( types );
  36. foreach ( var item in result )
  37. RegisterType( services, item.Item1, item.Item2, lifetime );
  38. }
  39. /// <summary>
  40. /// 获取接口类型和实现类型列表
  41. /// </summary>
  42. private List<(Type, Type)> GetTypes<TDependencyInterface>( ITypeFinder finder ) {
  43. var result = new List<(Type, Type)>();
  44. var classTypes = finder.Find<TDependencyInterface>();
  45. foreach ( var classType in classTypes ) {
  46. var interfaceTypes = Util.Helpers.Reflection.GetInterfaceTypes( classType, typeof( TDependencyInterface ) );
  47. interfaceTypes.ForEach( interfaceType => result.Add( (interfaceType, classType) ) );
  48. }
  49. return result;
  50. }
  51. /// <summary>
  52. /// 过滤类型
  53. /// </summary>
  54. private List<(Type, Type)> FilterTypes( List<(Type, Type)> types ) {
  55. var result = new List<(Type, Type)>();
  56. foreach ( var group in types.GroupBy( t => t.Item1 ) ) {
  57. if ( group.Count() == 1 ) {
  58. result.Add( group.First() );
  59. continue;
  60. }
  61. result.Add( GetTypesByPriority( group ) );
  62. }
  63. return result;
  64. }
  65. /// <summary>
  66. /// 获取优先级类型
  67. /// </summary>
  68. private (Type, Type) GetTypesByPriority( IGrouping<Type, (Type, Type)> group ) {
  69. int? currentPriority = null;
  70. Type classType = null;
  71. foreach ( var item in group ) {
  72. var priority = GetPriority( item.Item2 );
  73. if ( currentPriority == null || priority > currentPriority ) {
  74. currentPriority = priority;
  75. classType = item.Item2;
  76. }
  77. }
  78. return ( group.Key, classType );
  79. }
  80. /// <summary>
  81. /// 获取优先级
  82. /// </summary>
  83. private int GetPriority( Type type ) {
  84. var attribute = type.GetCustomAttribute<IocAttribute>();
  85. if ( attribute == null )
  86. return 0;
  87. return attribute.Priority;
  88. }
  89. /// <summary>
  90. /// 注册类型
  91. /// </summary>
  92. private void RegisterType( IServiceCollection services, Type interfaceType, Type classType, ServiceLifetime lifetime ) {
  93. services.TryAdd( new ServiceDescriptor( interfaceType, classType, lifetime ) );
  94. }
  95. }

Ioc 服务定位器工具类

Ioc 工具类内置了一个对象容器,如果没有为它设置服务提供器,它将从内置对象容器获取依赖,这是导致副作用的根源.

  1. /// <summary>
  2. /// 容器操作
  3. /// </summary>
  4. public static class Ioc {
  5. /// <summary>
  6. /// 容器
  7. /// </summary>
  8. private static readonly Util.Dependency.Container _container = Util.Dependency.Container.Instance;
  9. /// <summary>
  10. /// 获取服务提供器操作
  11. /// </summary>
  12. private static Func<IServiceProvider> _getServiceProviderAction;
  13. /// <summary>
  14. /// 服务范围工厂
  15. /// </summary>
  16. public static IServiceScopeFactory ServiceScopeFactory { get; set; }
  17. /// <summary>
  18. /// 创建新容器
  19. /// </summary>
  20. public static Util.Dependency.Container CreateContainer() {
  21. return new Util.Dependency.Container();
  22. }
  23. /// <summary>
  24. /// 获取服务集合
  25. /// </summary>
  26. public static IServiceCollection GetServices() {
  27. return _container.GetServices();
  28. }
  29. /// <summary>
  30. /// 设置获取服务提供器操作
  31. /// </summary>
  32. /// <param name="action">获取服务提供器操作</param>
  33. public static void SetServiceProviderAction( Func<IServiceProvider> action ) {
  34. _getServiceProviderAction = action;
  35. }
  36. /// <summary>
  37. /// 获取
  38. /// </summary>
  39. public static IServiceProvider GetServiceProvider() {
  40. var provider = _getServiceProviderAction?.Invoke();
  41. if ( provider != null )
  42. return provider;
  43. return _container.GetServiceProvider();
  44. }
  45. /// <summary>
  46. /// 创建对象
  47. /// </summary>
  48. /// <typeparam name="T">对象类型</typeparam>
  49. public static T Create<T>() {
  50. return Create<T>( typeof( T ) );
  51. }
  52. /// <summary>
  53. /// 创建对象
  54. /// </summary>
  55. /// <typeparam name="T">返回对象类型</typeparam>
  56. /// <param name="type">对象类型</param>
  57. public static T Create<T>( Type type ) {
  58. var service = Create( type );
  59. if( service == null )
  60. return default;
  61. return (T)service;
  62. }
  63. /// <summary>
  64. /// 创建对象
  65. /// </summary>
  66. /// <param name="type">对象类型</param>
  67. public static object Create( Type type ) {
  68. if( type == null )
  69. return null;
  70. var provider = GetServiceProvider();
  71. return provider.GetService( type );
  72. }
  73. /// <summary>
  74. /// 创建对象集合
  75. /// </summary>
  76. /// <typeparam name="T">返回类型</typeparam>
  77. public static List<T> CreateList<T>() {
  78. return CreateList<T>( typeof( T ) );
  79. }
  80. /// <summary>
  81. /// 创建对象集合
  82. /// </summary>
  83. /// <typeparam name="T">返回类型</typeparam>
  84. /// <param name="type">对象类型</param>
  85. public static List<T> CreateList<T>( Type type ) {
  86. Type serviceType = typeof( IEnumerable<> ).MakeGenericType( type );
  87. var result = Create( serviceType );
  88. if( result == null )
  89. return new List<T>();
  90. return ( (IEnumerable<T>)result ).ToList();
  91. }
  92. /// <summary>
  93. /// 创建服务范围
  94. /// </summary>
  95. public static IServiceScope CreateScope() {
  96. var provider = GetServiceProvider();
  97. return provider.CreateScope();
  98. }
  99. /// <summary>
  100. /// 清理
  101. /// </summary>
  102. public static void Clear() {
  103. _container.Clear();
  104. }
  105. }

Ioc 工具类需要获取正确的服务提供器,可以通过 SetServiceProviderAction 方法进行设置.

对于 Asp.Net Core 环境, AspNetCoreServiceRegistrar 服务注册器已经正确设置Ioc工具类的服务提供器.

但对于非 Asp.Net Core 环境, 设置正确的服务提供器可能非常困难.

  1. /// <summary>
  2. /// AspNetCore服务注册器
  3. /// </summary>
  4. public class AspNetCoreServiceRegistrar : IServiceRegistrar {
  5. /// <summary>
  6. /// 获取服务名
  7. /// </summary>
  8. public static string ServiceName => "Util.Infrastructure.AspNetCoreServiceRegistrar";
  9. /// <summary>
  10. /// 排序号
  11. /// </summary>
  12. public int OrderId => 200;
  13. /// <summary>
  14. /// 是否启用
  15. /// </summary>
  16. public bool Enabled => ServiceRegistrarConfig.IsEnabled( ServiceName );
  17. /// <summary>
  18. /// 注册服务
  19. /// </summary>
  20. /// <param name="serviceContext">服务上下文</param>
  21. public Action Register( ServiceContext serviceContext ) {
  22. serviceContext.HostBuilder.ConfigureServices( ( context, services ) => {
  23. RegisterHttpContextAccessor( services );
  24. RegisterServiceLocator();
  25. } );
  26. return null;
  27. }
  28. /// <summary>
  29. /// 注册Http上下文访问器
  30. /// </summary>
  31. private void RegisterHttpContextAccessor( IServiceCollection services ) {
  32. var httpContextAccessor = new HttpContextAccessor();
  33. services.TryAddSingleton<IHttpContextAccessor>( httpContextAccessor );
  34. Web.HttpContextAccessor = httpContextAccessor;
  35. }
  36. /// <summary>
  37. /// 注册服务定位器
  38. /// </summary>
  39. private void RegisterServiceLocator() {
  40. Ioc.SetServiceProviderAction( () => Web.ServiceProvider );
  41. }
  42. }

禁用依赖服务注册器

如果你不想自动扫描注册 ISingletonDependency,IScopeDependency,ITransientDependency 相关依赖,可以禁用它.

  1. ServiceRegistrarConfig.Instance.DisableDependencyServiceRegistrar();
  2. builder.AsBuild().AddUtil();

Util应用框架基础(一) - 依赖注入的更多相关文章

  1. Spring框架学习之依赖注入

    Spring框架从2004年发布的第一个版本以来,如今已经迭代到5.x,逐渐成为JavaEE开发中必不可少的框架之一,也有人称它为Java下的第一开源平台.单从Spring的本身来说,它贯穿着整个表现 ...

  2. 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之九 || 依赖注入IoC学习 + AOP界面编程初探

    更新 1.如果看不懂本文,或者比较困难,先别着急问问题,我单写了一个关于依赖注入的小Demo,可以下载看看,多思考思考注入的原理: https://github.com/anjoy8/BlogArti ...

  3. 从零写Java Web框架——实现Ioc依赖注入

    大概思路 通过读取配置文件,获取框架要加载的包路径:base-package,类似于 Spring 配置文件中的: <context:component-scan base-package=&q ...

  4. Z从壹开始前后端分离【 .NET Core2.2/3.0 +Vue2.0 】框架之九 || 依赖注入IoC学习 + AOP界面编程初探

    本文梯子 本文3.0版本文章 更新 代码已上传Github+Gitee,文末有地址 零.今天完成的绿色部分 一.依赖注入的理解和思考 二.常见的IoC框架有哪些 1.Autofac+原生 2.三种注入 ...

  5. 手写web框架之实现依赖注入功能

    我们在Controller中定义了Service成员变量,然后在Controller的Action方法中调用Service成员变量的方法,那么如果实现Service的成员变量? 之前定义了@Injec ...

  6. ASP.NET Core 1.0基础之依赖注入

      来源https://docs.asp.net/en/latest/fundamentals/dependency-injection.html ASP.NET Core 1.0在设计上原生就支持和 ...

  7. Spring基础—— 泛型依赖注入

    一.为了更加快捷的开发,为了更少的配置,特别是针对 Web 环境的开发,从 Spring 4.0 之后,Spring 引入了 泛型依赖注入. 二.泛型依赖注入:子类之间的依赖关系由其父类泛型以及父类之 ...

  8. Spring 基础知识 - 依赖注入

    所谓的依赖注入是指容器负责创建对象和维护对象间的依赖关系,而不是通过对象本身负责自己的创建和解决自己的依赖. 依赖注入主要目的是为了解耦,体现了一种“组合”的理念. 无论是xml配置.注解配置还是Ja ...

  9. Spring框架中的依赖注入

    依赖注入(DI : Dependency Injection)是基于.xml配置文件内节点的书写. 注入类型: 1.设置注入,调用了Bean的setXXX()进行值注入 普通属性(value值表示要显 ...

  10. 使用 .NETCore自带框架快速实现依赖注入

    Startup 在Startup的ConfigureServices()中配置DI的接口与其实现 public void ConfigureServices(IServiceCollection se ...

随机推荐

  1. [转]-- ISP(图像信号处理)算法概述、工作原理、架构、处理流程

    目录 ISP的主要内部构成: ISP内部包含 CPU.SUP IP(各种功能模块的通称).IF 等设备 ISP的控制结构:1.ISP逻辑 2.运行在其上的firmware ISP上的Firmware包 ...

  2. Cilium 系列-2-Cilium 快速安装

    系列文章 Cilium 系列文章 前言 在本章中,我们将直接将 Cilium 安装到 Kubernetes 集群中. 在实验中,我们用到的组件及版本为: Cilium 1.13.4 K3s v1.26 ...

  3. React Native集成CodePush热更新遇到的坑,以及折腾过程。"CFBundleShortVersionString" key needs to specify a valid semver string

    最近开始一个React Native的新项目.按惯例,在创建完项目后,先集成CodePush热更新功能. 这种活已经干过不止一两次了,当然没啥问题,直接上手开干. 可问题恰恰出在了本以为应该很顺利的地 ...

  4. 【go笔记】TCP编程

    前言 TCP服务端的处理流程 监听端口 接收客户端请求建立连接 创建goroutine处理链接 示例代码:TCP服务端 package main import ( "net" &q ...

  5. GitOps 与 DevOps:了解关键差异,为企业做出最佳选择

    在软件开发领域,GitOps 和 DevOps 是加强协作和实现软件交付流程自动化的重要技术.虽然这两种模式都旨在提高软件开发生命周期的效率,但它们的核心原则和实施方式却各不相同. 本篇文章将帮助您了 ...

  6. Web安全漏洞解决方案

    1.已解密的登录请求 推理: AppScan 识别了不是通过 SSL 发送的登录请求. 测试请求和响应: 1.1.1 产生的原因 登录接口,前端传入的密码参数没有经过md5的加密就直接传给了后端 1. ...

  7. 应用程序通过 Envoy 代理和 Jaeger 进行分布式追踪 —— Ingress Controller + Http服务 + Grpc服务(三)

    1.概述 在<应用程序通过 Envoy 代理和 Jaeger 进行分布式追踪(一)>这篇博文中,我们详细介绍了单个应用程序通过 Envoy 和 Jaeger 实现链路追踪的过程.通过这个示 ...

  8. 重磅| Falcon 180B 正式在 Hugging Face Hub 上发布!

    引言 我们很高兴地宣布由 Technology Innovation Institute (TII) 训练的开源大模型 Falcon 180B 登陆 Hugging Face! Falcon 180B ...

  9. ViTPose+:迈向通用身体姿态估计的视觉Transformer基础模型

    身体姿态估计旨在识别出给定图像中人或者动物实例身体的关键点,除了典型的身体骨骼关键点,还可以包括手.脚.脸部等关键点,是计算机视觉领域的基本任务之一.目前,视觉transformer已经在识别.检测. ...

  10. WorkPress使用BackWPup插件备份后手动还原方法记录

    前提 拿到BackWPup插件备份的zip包(下文均以backup.zip来指代).这个是备份包是事先从源WorkPress上备份好的. 环境 OS:Centos7.9 Apache:2.4.6 PH ...