一、Autofac容器
1、autofac原理与简介
Autofac是一个开源的依赖注入(DI)容器,用于.NET应用程序的组件解耦和管理。它提供了一种简单而强大的方式来实现依赖注入,并且可以与各种.NET框架和库无缝集成。
Autofac的原理是基于反射和动态代理技术。它通过扫描应用程序中的类型,自动发现和创建对象的实例,并将这些实例注入到需要它们的地方。这样可以实现组件之间的松耦合,提高代码的可测试性、可维护性和可扩展性。
2、核心功能
Autofac的核心功能包括:
-
依赖注入:Autofac可以自动解析和注入对象的依赖关系,使得组件之间的依赖关系更加清晰和可控。它支持构造函数注入、属性注入和方法注入等多种注入方式。
-
生命周期管理:Autofac提供了多种生命周期管理方式,如瞬时(Transient)、作用域(Scoped)和单例(Singleton)等。可以根据需要选择适合的生命周期来管理对象的创建和销毁。
-
AOP支持:Autofac支持面向切面编程(AOP),可以通过拦截器(Interceptor)来实现横切关注点的统一处理,例如日志记录、性能监控等。
-
模块化设计:Autofac支持模块化设计,可以将相关的组件配置和注册放在一个模块中,便于管理和维护。模块可以按需加载,提高应用程序的性能和灵活性。
-
扩展性:Autofac提供了丰富的扩展点,可以通过自定义注册源(Registration Source)、解析器(Resolver)和装饰器(Decorator)等来扩展其功能和行为。
3、autofac与内置DI容器
3.1、autofac的优点
Autofac和.NET内置的依赖注入(DI)容器在功能和使用方式上有一些区别,下面是它们的优缺点比较:
Autofac的优点:
- 灵活性:Autofac提供了更多的灵活性和扩展性。它支持更多的生命周期管理方式、AOP支持和模块化设计,可以满足更复杂的应用程序需求。
- 配置简单:Autofac的配置相对简单,可以通过代码或配置文件进行配置。它提供了丰富的API和扩展点,使得配置更加灵活和可控。
- 强大的注册和解析能力:Autofac支持多种注册方式,如按类型、按名称、按条件等。它还支持泛型注册和动态代理等高级特性,使得注册和解析对象更加方便和强大。
3.2、内置DI容器的优缺点
.NET内置DI容器的优点:
- 轻量级:由于是.NET框架的一部分,内置DI容器不需要额外的引用和配置,可以直接使用。这使得它更加轻量级,适合简单的应用程序或小型项目。
- 无需第三方库:内置DI容器不需要引入第三方库,可以直接使用.NET框架提供的功能。这样可以减少项目的依赖和维护成本。
- 集成性:内置DI容器与其他.NET框架和库的集成更加紧密。例如,在ASP.NET Core中,内置DI容器与中间件和管道模型紧密结合,可以更好地支持Web应用程序的开发。
然而,内置DI容器也存在一些限制:
- 功能相对有限:相比于Autofac,内置DI容器的功能相对有限。它不支持一些高级特性,如AOP、模块化设计和自定义扩展等。如果应用程序需要这些功能,可能需要额外的工作来实现。
- 配置复杂度:内置DI容器的配置相对复杂,尤其是在ASP.NET Core中。虽然可以通过扩展方法简化配置,但仍然需要一定的学习和理解成本。
4、小总结
内置DI容器与第三方容器各有优缺点,具体的选择还应视实际应用场景而定。
二、IOC详解
1、IOC原理
IOC(Inversion of Control,控制反转)是一种软件设计原则,用于解耦组件之间的依赖关系。它的核心思想是将对象的创建和管理交给容器来完成,而不是由应用程序显式地创建和管理对象。
IOC的原理是通过依赖注入(Dependency Injection,DI)来实现控制反转。依赖注入是指将一个对象所依赖的其他对象通过构造函数、属性或方法参数的方式注入进来,而不是在对象内部主动去创建这些依赖对象。
IOC的作用是降低组件之间的耦合度,提高代码的可维护性、可测试性和可扩展性。它可以使得应用程序更加灵活,易于修改和扩展,同时也方便进行单元测试和模块化开发。
2、IOC的作用
IOC的好处包括:
1. 解耦组件:通过IOC容器管理对象的创建和依赖关系,可以将组件之间的耦合度降到最低,使得代码更加清晰和可维护。
2. 提高可测试性:依赖注入使得对象的依赖关系明确,可以轻松地替换依赖对象进行单元测试,从而提高代码的可测试性。
3. 简化配置和管理:IOC容器负责对象的创建和管理,减少了手动配置和管理对象的工作量,提高了开发效率。
4. 支持面向切面编程:通过IOC容器可以方便地实现面向切面编程(AOP),例如日志记录、事务管理等横切关注点的统一处理。
3、实现IOC的方式
具体实现IOC的方式有多种,常见的包括:
1. 依赖注入容器:使用第三方的依赖注入容器,如Autofac、Unity、Spring.NET等,通过配置和注册对象的方式实现IOC。
2. 手动注入:手动通过构造函数、属性或方法参数的方式注入依赖对象。
3. 服务定位器模式:使用一个中心化的服务定位器来管理对象的创建和获取,通过服务定位器来解决对象之间的依赖关系。
4、小总结
总结来说,IOC通过控制反转和依赖注入的方式,将对象的创建和管理交给容器来完成,从而降低组件之间的耦合度,提高代码的可维护性和可测试性。它的具体实现方式包括依赖注入容器、手动注入和服务定位器模式等。
三、AOP详解
1、aop原理简介
AOP(Aspect-Oriented Programming,面向切面编程)是一种软件开发的方法论,用于将横切关注点与核心业务逻辑分离。它的核心思想是通过在程序中定义切面(Aspect),将通用的横切关注点(如日志记录、性能监测、事务管理等)从核心业务逻辑中抽离出来,以提高代码的可维护性和可重用性。
AOP的原理是通过动态代理技术实现的。在运行时,AOP框架会根据预定义的切面规则,动态地生成代理对象,并将切面逻辑织入到目标对象的方法调用前、后或周围。这样,在不修改原有代码的情况下,可以对目标对象的方法进行增强或拦截。
2、aop的作用
AOP的作用主要体现在以下几个方面:
1. 解耦关注点:AOP将横切关注点与核心业务逻辑分离,使得代码更加清晰和易于维护。通过将通用的横切关注点抽象成切面,可以避免在每个业务逻辑中重复编写相同的代码。
2. 提高模块化和重用性:AOP将横切关注点抽离出来,使得其可以独立于业务逻辑进行开发和测试。这样可以提高代码的模块化和重用性,减少重复劳动。
3. 统一处理横切关注点:AOP可以统一处理多个模块中的横切关注点,例如日志记录、异常处理、性能监控等。这样可以保证横切关注点的一致性和统一性。
4. 简化代码:AOP可以将一些非核心的辅助功能从业务逻辑中剥离出来,使得核心代码更加简洁和易于理解。
3、aop的实现方式
实现AOP的方式有多种,常见的包括:
1. 静态代理:手动编写代理类,在代理类中添加横切逻辑,并调用目标对象的方法。
2. 动态代理:使用反射或字节码生成技术,在运行时动态生成代理对象,并将横切逻辑织入到目标对象的方法中。常见的动态代理技术有JDK动态代理和CGLIB动态代理。
3. AOP框架:使用第三方的AOP框架,如AspectJ、Spring AOP等。这些框架提供了更高级的AOP功能和配置方式,可以更方便地实现AOP。
4、小总结
总结来说,AOP通过将横切关注点与核心业务逻辑分离,提高了代码的可维护性和可重用性。它的原理是通过动态代理技术,在运行时动态生成代理对象,并将切面逻辑织入到目标对象的方法中。实现AOP的方式包括静态代理、动态代理和使用AOP框架等
四、实战项目代码实现
本实战项目演示使用Autofac容器实现批量依赖注入服务和AOP拦截。
1、新建服务类库
实现接口和继承接口的服务类,主要是用于测试主项目通过autofac容器实现将其他程序集的服务批量依赖注入注册服务。
新增ITest1Services.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace study_project_commoncore.IServices
{
public interface ITest1Services
{
void Write();
}
}
新增Test1Services.cs
using study_project_commoncore.IServices;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace study_project_commoncore.Services
{
public class Test1Services : ITest1Services
{
public void Write()
{
Console.WriteLine("测试批量注册服务");
}
}
}
新增ITestServices.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace study_project_commoncore.IServices
{
public interface ITestServices
{
void TestPrint();
void TestPrint(string name,string password);
}
}
新增TestServices.cs
using study_project_commoncore.IServices;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace study_project_commoncore.Services
{
public class TestServices : ITestServices
{
public void TestPrint()
{
Console.WriteLine("测试autofac容器注入");
}
public void TestPrint(string name, string password)
{
Console.WriteLine($"测试用户注册");
}
}
}
2、新增项目
1、新建asp.net core 6 Web Api 项目
引入Autofac、Autofac.Extensions.DependencyInjection、Autofac.Extras.DynamicProxy、Castle.Core等Nuget程序包,具体的版本应视项目框架而定,以下仅可参考:
<PackageReference Include="Autofac" Version="8.0.0" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="9.0.0" />
<PackageReference Include="Autofac.Extras.DynamicProxy" Version="7.1.0" />
<PackageReference Include="Castle.Core" Version="5.1.1" />
2、新建 AutofacMoudleManager.cs
继承与Autofac.Module,重写Autofac管道Load方法,在这里通过反射和动态代理来实现批量注册注入服务和AOP拦截器。
using Autofac;
using Autofac.Extras.DynamicProxy;
using FluentValidation;
using System.Reflection;
namespace study_project_allin.Manager
{
public class AutofacMoudleManager:Autofac.Module
{
/// <summary>
/// 重写Autofac管道Load方法,在这里注册注入
/// </summary>
protected override void Load(ContainerBuilder builder)
{
// 注册日志记录拦截器
builder.RegisterType<LoggingInterceptor>();
// 注册性能监测拦截器
builder.RegisterType<PerformanceInterceptor>();
// 注册事务管理拦截器
builder.RegisterType<TransactionInterceptor>();
//通过反射机制实现批量注册服务和拦截器
builder.RegisterAssemblyTypes(Assembly.Load("study_project_commoncore"))
.Where(a => a.Name.EndsWith("Services"))
.AsImplementedInterfaces()
.InstancePerLifetimeScope()
.EnableInterfaceInterceptors()
.InterceptedBy(typeof(LoggingInterceptor),typeof(PerformanceInterceptor),typeof(TransactionInterceptor));
//.AsImplementedInterfaces():注册的服务的生命周期是默认的 Transient 生命周期
//.InstancePerLifetimeScope():每个请求(即每个 HTTP 请求)创建一个实例,并在整个请求期间重用
//.SingleInstance():在整个应用程序生命周期中只创建一个实例,并在每次解析时重用
//.InstancePerDependency():每次解析时都创建一个新的实例
//builder.RegisterAssemblyTypes(Assembly.Load("study_project_commoncore"))
// .Where(a => a.Name.EndsWith("Services"))
// .AsImplementedInterfaces()
// .SingleInstance();
base.Load(builder);
}
}
}
上述代码中通过调用builder.RegisterType<LoggingInterceptor>()
,将LoggingInterceptor、PerformanceInterceptor、TransactionInterceptor
拦截器注册到DI容器中。这个拦截器可以用来在方法执行前后进行日志记录等横切关注点的处理。还通过调用builder.RegisterAssemblyTypes(Assembly.Load("study_project_commoncore"))
,批量注册指定程序集("study_project_commoncore")中以"Services"结尾的类型,并将其实现的接口也注册到DI容器中。同时,启用接口拦截器(EnableInterfaceInterceptors)并指定使用LoggingInterceptor、PerformanceInterceptor、TransactionInterceptor
拦截器进行拦截。同时设置生命周期:通过调用.InstancePerLifetimeScope()
,设置注册的服务的生命周期为每个请求(即每个HTTP请求)创建一个实例,并在整个请求期间重用。
3、新建拦截器LoggingInterceptor.cs(实现日志记录)
using Castle.DynamicProxy;
using System;
using System.Diagnostics;
public class LoggingInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
var methodName = invocation.Method.Name;
var className = invocation.TargetType.Name;
var arguments = string.Join(", ", invocation.Arguments);
Console.WriteLine($"Before executing method: {className}.{methodName}({arguments})");
var stopwatch = Stopwatch.StartNew();
try
{
invocation.Proceed();
}
catch (Exception ex)
{
Console.WriteLine($"Exception occurred in method: {className}.{methodName}");
Console.WriteLine($"Exception details: {ex}");
throw;
}
finally
{
stopwatch.Stop();
Console.WriteLine($"After executing method: {className}.{methodName}");
Console.WriteLine($"Execution time: {stopwatch.ElapsedMilliseconds} ms");
}
}
}
在上述代码中,我们使用了 Castle.Core 库中的 IInterceptor
接口和 IInvocation
接口来实现拦截器。在 Intercept
方法中,我们首先获取被调用方法的名称、所属类名和参数列表,并输出日志信息。然后,我们使用 Stopwatch
类来计算方法的执行时间。在 try
块中,我们调用 invocation.Proceed()
来执行原始方法的逻辑。如果发生异常,我们会捕获并输出异常信息。最后,在 finally
块中,我们停止计时器,并输出方法执行完成的日志信息和执行时间。你可以将该拦截器应用到需要进行日志记录的方法或类上,以实现完整详细的日志记录功能。
4、新建PerformanceInterceptor.cs(性能监测拦截器)
using Castle.DynamicProxy;
using System.Diagnostics;
public class PerformanceInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
var methodName = invocation.Method.Name;
var className = invocation.TargetType.Name;
var stopwatch = Stopwatch.StartNew();
try
{
invocation.Proceed();
}
catch (Exception ex)
{
Console.WriteLine($"Exception occurred in method: {className}.{methodName}");
Console.WriteLine($"Exception details: {ex}");
throw;
}
finally
{
stopwatch.Stop();
Console.WriteLine($"Method: {className}.{methodName}");
Console.WriteLine($"Execution time: {stopwatch.ElapsedMilliseconds} ms");
}
}
}
5、新建TransactionInterceptor.cs(事务管理拦截器)
using Castle.DynamicProxy;
using System.Transactions;
public class TransactionInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
var methodName = invocation.Method.Name;
var className = invocation.TargetType.Name;
using (var scope = new TransactionScope())
{
try
{
Console.WriteLine($"Transaction started for method: {className}.{methodName}");
invocation.Proceed();
scope.Complete();
Console.WriteLine($"Transaction committed for method: {className}.{methodName}");
}
catch (Exception ex)
{
Console.WriteLine($"Exception occurred in method: {className}.{methodName}");
Console.WriteLine($"Exception details: {ex}");
throw;
}
}
}
}
6、修改Program.cs文件
添加以下代码,使用Autofac容器替换内置DI容器
// 添加Autofac容器:使用第三方IOC容器实现依赖注入
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
// 加载Autofac模块
builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder =>
{
containerBuilder.RegisterModule<AutofacMoudleManager>();
});
7、修改WeatherForecastController.cs代码
通过构造函数依赖注入的方式,将类库的服务注入到Controller层,调用方法测试验证
using Microsoft.AspNetCore.Mvc;
using study_project_commoncore.IServices;
namespace study_project_allin.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
#region 构造函数注入
private readonly ILogger<WeatherForecastController> _logger;
private readonly ITestServices _services;
private readonly ITest1Services _services1;
public WeatherForecastController(ILogger<WeatherForecastController> logger, ITestServices testServices, ITest1Services services1)
{
_logger = logger;
_services = testServices;
_services1=services1;
}
#endregion
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
//测试IOC和AOP-----------
_services.TestPrint();
_services.TestPrint("张三","12345678");
_services1.Write();
//----------------
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
}
3、启动调试项目
请求WeatherForecast Api接口,查看控制台输出结果:
五、总结
总结来说,Autofac是一个功能强大且易于使用的依赖注入容器,可以帮助开发人员实现组件解耦、提高代码质量和可维护性。它的原理是基于反射和动态代理技术,通过自动发现和注入对象的依赖关系来实现依赖注入。但是,选择使用Autofac还是内置DI容器取决于具体的应用场景和需求。如果应用程序需要更高级的功能和灵活性,或者已经使用了Autofac的其他功能,那么Autofac是一个很好的选择。如果应用程序较为简单,或者希望减少依赖和配置的复杂性,那么内置DI容器可能更适合。