一、前言
1、简而言之
Asp.Net Core Mvc,我也用了很长一段时间了,它现在的编程模型和方式还是特别棒的,都是组件开发,什么都可以替换,当然了,您别抬杠,有些还是不能替换的。自从我们进入了跨平台开发的时代,IOC容器也成了一个不可或缺的东西了。微软为我们提供了一个默认实现,那就是 IServiceCollection
,当时我们可以替换掉它,今天我就试试,替换一下,把我的一些经验也写出来,以防以后忘记,不知道去哪里找了。
当然了,这个不是很难,也希望高手不要见笑,对于我来说,好记性不如烂笔头,好的东西我就记录下来,有使用的地方,可以直接来找。
2、开发环境。
我的开发环境没有发生变化,具体如下:
- 操作系统:Windows10 Professional
- 开发平台:Asp.Net Core Mvc 6.0
- 开发语言:C#
- 开发工具:Visual Studio 2022
二、操作步骤
1、第一,我们当然要新建一个 Asp.Net Core MVC 的项目,项目都没有,其他的都是胡扯了,我项目的名称是:PatrickLiu.Autofac.MvcConcordance
。
2、我们为我们的项目增加相应的程序包。分别是:Autofac、Autofac.Extensions.DependencyInjection、Autofac.Extras.DynamicProxy,Castle.Core
1】、Autofac
提供最基础、最核心的功能。
2】、Autofac.Extensions.DependencyInjection
提供和 Asp.Net Core MVC 集成的接口。
3】、Autofac.Extras.DynamicProxy
提供对AOP的支持,通过动态代理实现。
4】、Castle.Core
实现针对 Core 版本的支持,也是支持 AOP 的必需组件。
3、这部分是重点,在 Program 程序中配置。具体代码在里面,很简单,就不多说了。
using Autofac;
using Autofac.Extensions.DependencyInjection;
using Autofac.Extras.DynamicProxy;
using Castle.DynamicProxy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using PatrickLiu.Autofac.Contracts;
using PatrickLiu.Autofac.Extensions;
using PatrickLiu.Autofac.Extensions.Aops;
using PatrickLiu.Autofac.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
#region 整合 Autofac
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(builder =>
{
builder.RegisterType<ChiesePerson>().As<IPerson>();
builder.RegisterType<SinglePerson>();
#region 服务类型支持属性注入,红色表示是对属性注入的支持,哪个类型需要属性注入,在注册的时候使用 PropertiesAutowired()方法,里面参数是属性选择器。
25
builder.RegisterType<PropertyPerson>().As<IPropertyPerson>().PropertiesAutowired(new CustomPropertySelector());
builder.RegisterType<PropertyTwoPerson>().As<IPropertyTwoPerson>();
builder.RegisterType<PropertyThreePerson>().As<IPropertyThreePerson>();
builder.RegisterType<SinglePerson>();
#endregion
#region AOP支持,红色标注的是关键实现。
builder.RegisterType<AOPPerson>().As<IAOPPerson>().EnableInterfaceInterceptors();
builder.RegisterType<AOPClassPerson>().As<IAOPClassPerson>().EnableClassInterceptors(new ProxyGenerationOptions()
{
Selector = new CustomInterceptorSelector()
});
builder.RegisterType<AOPCachePerson>().As<IAOPCachePerson>().EnableClassInterceptors();
builder.RegisterType<CustomClassInterceptor>();
builder.RegisterType<CustomInterfaceInterceptor>();
builder.RegisterType<CustomCacheInterceptor>();
#endregion
#region 单接口多实例
builder.RegisterType<MultiPerson>().Keyed<IMultiPerson>("MultiPerson");
builder.RegisterType<MultiTwoPerson>().Keyed<IMultiPerson>("MultiTwoPerson");
builder.RegisterType<MultiThreePerson>().Keyed<IMultiPerson>("MultiThreePerson");
#endregion
#region 让控制器支持属性注入
var controllerBaseType = typeof(ControllerBase);
builder.RegisterAssemblyTypes(typeof(Program).Assembly)
.Where(t => controllerBaseType.IsAssignableFrom(t) && controllerBaseType != t)
.PropertiesAutowired(new CustomPropertySelector());
builder.RegisterType<ServiceBasedControllerActivator>().As<IControllerActivator>();
#endregion
});
#region 支持 Autofac 属性注入,该方法可以使用,也可以不使用。作用是我们的控制器要使用 Autofac 容器来创建,替换原始的 Controller 激活器。
//builder.Services.AddTransient<IControllerActivator, ServiceBasedControllerActivator>();
//builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
#endregion
#endregion
var app = builder.Build();
app.UseStaticFiles();
app.UseRouting();
app.MapControllerRoute("default", "{controller=AOP}/{action=index}/{id?}");
app.Run();
4、Autofac 支持属性注入,默认是所有属性的类型如果是注册的服务类型,就会全部赋值,但是,我们也可以实现 IPropertySelector
接口,自定义哪个属性需要注入。
using Autofac.Core;
using System.Reflection;
namespace PatrickLiu.Autofac.Extensions
{
public sealed class CustomPropertySelector : IPropertySelector
{
public bool InjectProperty(PropertyInfo propertyInfo, object instance)
{
return propertyInfo.IsDefined(typeof(CustomPropertySelectorAttribute), false);
}
}
}
有了选择器,我们也需要定义特性(Attribute
),标注属性(Property
)就可以。
namespace PatrickLiu.Autofac.Extensions
{
/// <summary>
///
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public sealed class CustomPropertySelectorAttribute : Attribute
{
}
}
有了这两个,我们在把我们自定义的属性选择器 CustomPropertySelector
作为参数,传递给 PropertiesAutowired(new CustomPropertySelector())
方法,就完成操作了。
using Microsoft.AspNetCore.Mvc;
using PatrickLiu.Autofac.Contracts;
using PatrickLiu.Autofac.Extensions;
using PatrickLiu.Autofac.Models;
namespace PatrickLiu.Autofac.MvcConcordance.Controllers
{
/// <summary>
///
/// </summary>
public class PropertyInjectionController : Controller
{
private readonly IPropertyPerson _propertyPerson;
/// <summary>
///
/// </summary>
/// <param name="propertyPerson"></param>
public PropertyInjectionController(IPropertyPerson propertyPerson)
{
_propertyPerson = propertyPerson;
}
/// <summary>
/// 这里就是控制器的属性,需要自动初始化。
/// </summary>
[CustomPropertySelector]
public SinglePerson? SinglePerson { get; set; }
/// <summary>
///
/// </summary>
/// <returns></returns>
public IActionResult Index()
{
_propertyPerson.Process();
return View();
}
}
}
5、Autofac
支持两种类型 AOP,分别是:EnableClassInterceptors
和 EnableInterfaceInterceptors
,类拦截器必须使用 [Intercept(typeof(CustomClassInterceptor))]
标注需要实现 AOP 的实现类上,如果是接口拦截器,就必须将 [Intercept(typeof(CustomInterfaceInterceptor))]
标注在需要实现 AOP 的接口类型上。
1】、EnableClassInterceptors
:类拦截器,它的方法必须是 virtual
虚方法,才可以支持 AOP。
2】、EnableInterfaceInterceptors
:接口拦截器,没有相关限制,该接口的实现类的方法都会实现 AOP。
这就是接口拦截器。
using Autofac.Extras.DynamicProxy;
using PatrickLiu.Autofac.Extensions.Aops;
namespace PatrickLiu.Autofac.Contracts
{
/// <summary>
///
/// </summary>
[Intercept(typeof(CustomInterfaceInterceptor))]
public interface IAOPPerson
{
/// <summary>
///
/// </summary>
void Process();
/// <summary>
///
/// </summary>
void ProcessTwo();
/// <summary>
///
/// </summary>
void ProcessThree();
}
}
以下是类拦截器。
using Autofac.Extras.DynamicProxy;
using PatrickLiu.Autofac.Contracts;
using PatrickLiu.Autofac.Extensions.Aops;
namespace PatrickLiu.Autofac.Models
{
/// <summary>
///
/// </summary>
[Intercept(typeof(CustomClassInterceptor))]
public class AOPClassPerson : IAOPClassPerson
{
/// <summary>
///
/// </summary>
public virtual void ProcessAOP()
{
Console.WriteLine("AOPClassPerson.ProcessAOP()");
}
/// <summary>
///
/// </summary>
public void Process()
{
Console.WriteLine("AOPClassPerson.Process()");
}
}
}
6、我们要自定义我们的拦截器,然后再标注的类型标注 [Intercept(typeof(自定义拦截器))]
,就可以使用,我分别定义了两个拦截器,一个用于类,一个用于接口,其实没有本质区别,实现的接口都是一样的,该接口就是:IInterceptor
。
using Castle.DynamicProxy;
namespace PatrickLiu.Autofac.Extensions.Aops
{
/// <summary>
/// 类级别的拦截器,标注在要实现AOP功能的类型上。
/// </summary>
public sealed class CustomClassInterceptor : IInterceptor
{
/// <summary>
///
/// </summary>
/// <param name="invocation"></param>
public void Intercept(IInvocation invocation)
{
{
Console.WriteLine("CustomClassInterceptor.Before");
}
invocation.Proceed();
{
Console.WriteLine("CustomClassInterceptor.After");
}
}
}
}
using Castle.DynamicProxy;
namespace PatrickLiu.Autofac.Extensions.Aops
{
/// <summary>
/// 接口级别的拦截器,标注在要实现AOP功能的接口类型上。
/// </summary>
public sealed class CustomInterfaceInterceptor : IInterceptor
{
/// <summary>
///
/// </summary>
/// <param name="invocation"></param>
public void Intercept(IInvocation invocation)
{
{
Console.WriteLine("CustomInterfaceInterceptor.Before");
}
invocation.Proceed();
{
Console.WriteLine("CustomInterfaceInterceptor.After");
}
}
}
}
7、当然,我们也可以不标注 [Intercept(typeof(自定义拦截器))]
,我们可以实现 IInterceptorSelector
接口,实现自定义使用哪些拦截器。代码如下:
using Castle.DynamicProxy;
using System.Reflection;
namespace PatrickLiu.Autofac.Extensions.Aops
{
/// <summary>
///
/// </summary>
public class CustomInterceptorSelector : IInterceptorSelector
{
/// <summary>
///
/// </summary>
/// <param name="type"></param>
/// <param name="method"></param>
/// <param name="interceptors">如果类型有标注拦截器,这里会获取所有拦截器。</param>
/// <returns></returns>
public IInterceptor[] SelectInterceptors(Type type, MethodInfo method, IInterceptor[] interceptors)
{
IList<IInterceptor> interceptorsList = new List<IInterceptor>();
interceptorsList.Add(new CustomInterfaceInterceptor());
//在这个方法里面,我们可以过滤拦截器,想是哪个起作用哪个就起作用。返回的拦截器,就是起作用的拦截器。
return interceptorsList.ToArray();
}
}
}
我们可以选择拦截器,也需要在Program
里体现,builder.RegisterType<AOPClassPerson>().As<IAOPClassPerson>().EnableClassInterceptors(new ProxyGenerationOptions() { Selector = new CustomInterceptorSelector() });
8、Autofac 默认支持构造函数注入,如果有多个构造函数,如果构造函数的参数都是需要注入的服务类型,默认选择依赖注册服务参数最多的构造函数会被执行。当然我们也可以选择指定的构造函数来初始化类型实例,该方法就是 .UsingConstructor(typeof(参数类型))
。
builder.RegisterType<ChiesePerson>().As<IPerson>().UsingConstructor(typeof(int));
using Autofac;
using Microsoft.AspNetCore.Mvc;
using PatrickLiu.Autofac.Contracts;
using PatrickLiu.Autofac.Models;
namespace PatrickLiu.Autofac.MvcConcordance.Controllers
{
/// <summary>
///
/// </summary>
public class HomeController : Controller
{
private readonly IPerson _person;
private readonly IServiceProvider _serviceProvider;
private readonly IComponentContext _componentContext;
/// <summary>
///
/// </summary>
/// <param name="person"></param>
/// <param name="serviceProvider">服务提供器,类型是 AutofacServiceProvider,获取类型。</param>
/// <param name="componentContext">autofac 的上下文对象,可以获取容器中的服务。</param>
public HomeController(IPerson person, IServiceProvider serviceProvider, IComponentContext componentContext)
{
_person = person;
_serviceProvider = serviceProvider;
_componentContext = componentContext;
}
public IActionResult Index()
{
_person.Eat("炸酱面");
var single=_serviceProvider.GetService<SinglePerson>();
single!.Eat("残羹冷炙");
var singleTwo=_componentContext.Resolve<SinglePerson>();
singleTwo.Eat("残羹剩饭");
return View();
}
}
}
三、结束语
平台本身提供了自己的容器实现,当然,我们也可以替换它,使用其他的 IOC容器。不学不知道,一学吓一跳,东西还有很多不知道了。平凡的我,只能继续努力,苍天不负有心人,努力就会有回报。不忘初心,继续努力吧。
天下国家,可均也;爵禄,可辞也;白刃,可蹈也;中庸不可能也