寫過spring boot以後,那種無處不在的註解讓我很是喜歡,好比屬性注入@autowire,配置值注入@value,聲明式事物@Transactional等,都很是簡潔優雅,那麼我就在想,這些在net core裏能實現麼?通過一番摸索,終於實現並整理成此文。spring
IOC方面,我的很是喜歡net core自帶的DI,由於他註冊服務簡潔優雅,3個生命週期通俗易懂,因此就沒使用autofac等其餘容器,AOP方面,使用了業內鼎鼎大名的Castle.DynamicProxy(簡稱DP),因此要在nuget中添加Castle.Core的依賴包,這裏你們可能會有疑問,利用mvc的actionFilter不就能夠實現了麼,爲何還要引用DP呢,由於呀,actionFilter只在controller層有效,普通類他就無能爲力了,而DP無所不能。json
屬性注入註解cookie
[AttributeUsage(AttributeTargets.Property)] public class AutowiredAttribute : Attribute { }
配置值注入註解mvc
[AttributeUsage(AttributeTargets.Property)] public class ValueAttribute : Attribute { public ValueAttribute(string value = "") { this.Value = value; } public string Value { get; } }
聲明式事物註解app
[AttributeUsage(AttributeTargets.Method)] public class TransactionalAttribute : Attribute { }
工做單元接口及實現類,用來實現事物操做,注入級別爲scope,一次請求公用一個工做單元實例ide
public interface IUnitOfWork : IDisposable { /// <summary> /// 開啓事務 /// </summary> void BeginTransaction(); /// <summary> /// 提交 /// </summary> void Commit(); /// <summary> /// 事物回滾 /// </summary> void RollBack(); }
public class UnitOfWork : IUnitOfWork { public void BeginTransaction() { Console.WriteLine("開啓事務"); } public void Commit() { Console.WriteLine("提交事務"); } public void Dispose() { //throw new System.NotImplementedException(); } public void RollBack() { Console.WriteLine("回滾事務"); } }
攔截器函數
/// <summary> /// 事物攔截器 /// </summary> public class TransactionalInterceptor : StandardInterceptor { private IUnitOfWork Uow { set; get; } public TransactionalInterceptor(IUnitOfWork uow) { Uow = uow; } protected override void PreProceed(IInvocation invocation) { Console.WriteLine("{0}攔截前", invocation.Method.Name); var method = invocation.TargetType.GetMethod(invocation.Method.Name); if (method != null && method.GetCustomAttribute<TransactionalAttribute>() != null) { Uow.BeginTransaction(); } } protected override void PerformProceed(IInvocation invocation) { invocation.Proceed(); } protected override void PostProceed(IInvocation invocation) { Console.WriteLine("{0}攔截後, 返回值是{1}", invocation.Method.Name, invocation.ReturnValue); var method = invocation.TargetType.GetMethod(invocation.Method.Name); if (method != null && method.GetCustomAttribute<TransactionalAttribute>() != null) { Uow.Commit(); } } }
用來測試注入效果的接口以及實現類,這裏咱們定義一輛汽車,汽車擁有一個引擎(屬性注入),它能點火啓動,點火操做帶事物,這裏爲了演示【接口-實現類】注入和【實現類】注入2種狀況,引擎就沒添加接口,只有實現類,而且AOP攔截僅有【實現類】的類時,只能攔截虛方法,因此Start和Stop函數爲虛函數。測試
/// <summary> /// 汽車引擎 /// </summary> public class Engine { [Value("HelpNumber")] public string HelpNumber { set; get; } public virtual void Start() { Console.WriteLine("發動機啓動"); Stop(); } public virtual void Stop() { Console.WriteLine("發動機熄火,撥打求救電話" + HelpNumber); } }
public interface ICar { Engine Engine { set; get; } void Fire(); }
public class Car : ICar { [Autowired] public Engine Engine { set; get; } [Value("oilNo")] public int OilNo { set; get; } [Transactional] public void Fire() { Console.WriteLine("加滿" + OilNo + "號汽油,點火"); Engine.Start(); } }
控制器HomeControllerui
public class HomeController : Controller { [Autowired] public ICar Car{ set; get; } [Value("description")] public string Description { set; get; } public IActionResult Index() { var car = Car; Console.WriteLine(Description); Car.Fire(); return View(); } }
修改appsettings.json,添加一些測試鍵值對,(若是測試時發現輸出的中文亂碼,把appsettings.json保存爲utf8格式便可),具體代碼以下,this
{ "Logging": { "LogLevel": { "Default": "Warning" } }, "AllowedHosts": "*", "oilNo": 95, "HelpNumber": "110", "description": "我要開始飆車了" }
從上圖能夠看到,正常注入,正常開啓攔截器和事務,正確讀取配置值。
從上圖能夠看到,咱們的控制器,ICar和Engine所有都是動態代理類,注入正常。
第一部分,添加一個擴展類,名叫SummerBootExtentions.cs,代碼以下
public static class SummerBootExtentions { /// <summary> /// 瞬時 /// </summary> /// <typeparam name="TService"></typeparam> /// <typeparam name="TImplementation"></typeparam> /// <param name="services"></param> /// <param name="interceptorTypes"></param> /// <returns></returns> public static IServiceCollection AddSbTransient<TService, TImplementation>(this IServiceCollection services, params Type[] interceptorTypes) { return services.AddSbService(typeof(TService), typeof(TImplementation), ServiceLifetime.Transient, interceptorTypes); } /// <summary> /// 請求級別 /// </summary> /// <typeparam name="TService"></typeparam> /// <typeparam name="TImplementation"></typeparam> /// <param name="services"></param> /// <param name="interceptorTypes"></param> /// <returns></returns> public static IServiceCollection AddSbScoped<TService, TImplementation>(this IServiceCollection services, params Type[] interceptorTypes) { return services.AddSbService(typeof(TService), typeof(TImplementation), ServiceLifetime.Scoped, interceptorTypes); } /// <summary> /// 單例 /// </summary> /// <typeparam name="TService"></typeparam> /// <typeparam name="TImplementation"></typeparam> /// <param name="services"></param> /// <param name="interceptorTypes"></param> /// <returns></returns> public static IServiceCollection AddSbSingleton<TService, TImplementation>(this IServiceCollection services, params Type[] interceptorTypes) { return services.AddSbService(typeof(TService), typeof(TImplementation), ServiceLifetime.Singleton, interceptorTypes); } public static IServiceCollection AddSbService(this IServiceCollection services, Type serviceType, Type implementationType, ServiceLifetime lifetime, params Type[] interceptorTypes) { services.Add(new ServiceDescriptor(implementationType, implementationType, lifetime)); object Factory(IServiceProvider provider) { var target = provider.GetService(implementationType); var properties = implementationType.GetTypeInfo().DeclaredProperties; foreach (PropertyInfo info in properties) { //屬性注入 if (info.GetCustomAttribute<AutowiredAttribute>() != null) { var propertyType = info.PropertyType; var impl = provider.GetService(propertyType); if (impl != null) { info.SetValue(target, impl); } } //配置值注入 if (info.GetCustomAttribute<ValueAttribute>() is ValueAttribute valueAttribute) { var value = valueAttribute.Value; if (provider.GetService(typeof(IConfiguration)) is IConfiguration configService) { var pathValue = configService.GetSection(value).Value; if (pathValue != null) { var pathV = Convert.ChangeType(pathValue, info.PropertyType); info.SetValue(target, pathV); } } } } List<IInterceptor> interceptors = interceptorTypes.ToList() .ConvertAll<IInterceptor>(interceptorType => provider.GetService(interceptorType) as IInterceptor); var proxy = new ProxyGenerator().CreateInterfaceProxyWithTarget(serviceType, target, interceptors.ToArray()); return proxy; }; var serviceDescriptor = new ServiceDescriptor(serviceType, Factory, lifetime); services.Add(serviceDescriptor); return services; } /// <summary> /// 瞬時 /// </summary> /// <typeparam name="TService"></typeparam> /// <param name="services"></param> /// <param name="interceptorTypes"></param> /// <returns></returns> public static IServiceCollection AddSbTransient<TService>(this IServiceCollection services, params Type[] interceptorTypes) { return services.AddSbService(typeof(TService), ServiceLifetime.Transient, interceptorTypes); } /// <summary> /// 請求 /// </summary> /// <typeparam name="TService"></typeparam> /// <param name="services"></param> /// <param name="interceptorTypes"></param> /// <returns></returns> public static IServiceCollection AddSbScoped<TService>(this IServiceCollection services, params Type[] interceptorTypes) { return services.AddSbService(typeof(TService), ServiceLifetime.Scoped, interceptorTypes); } /// <summary> /// 單例 /// </summary> /// <typeparam name="TService"></typeparam> /// <param name="services"></param> /// <param name="interceptorTypes"></param> /// <returns></returns> public static IServiceCollection AddSbSingleton<TService>(this IServiceCollection services, params Type[] interceptorTypes) { return services.AddSbService(typeof(TService), ServiceLifetime.Singleton, interceptorTypes); } public static IServiceCollection AddSbService(this IServiceCollection services, Type serviceType, ServiceLifetime lifetime, params Type[] interceptorTypes) { if (services == null) throw new ArgumentNullException(nameof(services)); if (serviceType == (Type)null) throw new ArgumentNullException(nameof(serviceType)); object Factory(IServiceProvider provider) { List<IInterceptor> interceptors = interceptorTypes.ToList() .ConvertAll<IInterceptor>(interceptorType => provider.GetService(interceptorType) as IInterceptor); var proxy = new ProxyGenerator().CreateClassProxy(serviceType, interceptors.ToArray()); var properties = serviceType.GetTypeInfo().DeclaredProperties; foreach (PropertyInfo info in properties) { //屬性注入 if (info.GetCustomAttribute<AutowiredAttribute>() != null) { var propertyType = info.PropertyType; var impl = provider.GetService(propertyType); if (impl != null) { info.SetValue(proxy, impl); } } //配置值注入 if (info.GetCustomAttribute<ValueAttribute>() is ValueAttribute valueAttribute) { var value = valueAttribute.Value; if (provider.GetService(typeof(IConfiguration)) is IConfiguration configService) { var pathValue = configService.GetSection(value).Value; if (pathValue != null) { var pathV = Convert.ChangeType(pathValue, info.PropertyType); info.SetValue(proxy, pathV); } } } } return proxy; }; var serviceDescriptor = new ServiceDescriptor(serviceType, Factory, lifetime); services.Add(serviceDescriptor); return services; } /// <summary> /// 添加summer boot擴展 /// </summary> /// <param name="builder"></param> /// <returns></returns> public static IMvcBuilder AddSB(this IMvcBuilder builder) { if (builder == null) throw new ArgumentNullException(nameof(builder)); ControllerFeature feature = new ControllerFeature(); builder.PartManager.PopulateFeature<ControllerFeature>(feature); foreach (Type type in feature.Controllers.Select<TypeInfo, Type>((Func<TypeInfo, Type>)(c => c.AsType()))) builder.Services.TryAddTransient(type, type); builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, SbControllerActivator>()); return builder; } }
第二部分,添加一個自定義控制器激活類,用以替換掉mvc自帶的激活類,這個類命名爲SbControllerActivator.cs,代碼以下
public class SbControllerActivator : IControllerActivator { /// <inheritdoc /> public object Create(ControllerContext actionContext) { if (actionContext == null) throw new ArgumentNullException(nameof(actionContext)); Type serviceType = actionContext.ActionDescriptor.ControllerTypeInfo.AsType(); var target = actionContext.HttpContext.RequestServices.GetRequiredService(serviceType); var properties = serviceType.GetTypeInfo().DeclaredProperties; var proxy = new ProxyGenerator().CreateClassProxyWithTarget(serviceType, target); foreach (PropertyInfo info in properties) { //屬性注入 if (info.GetCustomAttribute<AutowiredAttribute>() != null) { var propertyType = info.PropertyType; var impl = actionContext.HttpContext.RequestServices.GetService(propertyType); if (impl != null) { info.SetValue(proxy, impl); } } //配置值注入 if (info.GetCustomAttribute<ValueAttribute>() is ValueAttribute valueAttribute) { var value = valueAttribute.Value; if (actionContext.HttpContext.RequestServices.GetService(typeof(IConfiguration)) is IConfiguration configService) { var pathValue = configService.GetSection(value).Value; if (pathValue != null) { var pathV = Convert.ChangeType(pathValue, info.PropertyType); info.SetValue(proxy, pathV); } } } } return proxy; } /// <inheritdoc /> public virtual void Release(ControllerContext context, object controller) { } }
第三部分,在Startup.cs中,修改ConfigureServices方法以下
public void ConfigureServices(IServiceCollection services) { services.Configure<CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddMvc() .SetCompatibilityVersion(CompatibilityVersion.Version_2_1) .AddSB(); services.AddSbScoped<Engine>(typeof(TransactionalInterceptor)); services.AddScoped<IUnitOfWork,UnitOfWork>(); services.AddScoped(typeof(TransactionalInterceptor)); services.AddSbScoped<ICar, Car>(typeof(TransactionalInterceptor)); }
從上面代碼咱們能夠看到,在addMvc後加上了咱們替換默認控制器的AddSB方法,接管了控制器的生成。AddSbScoped<Engine>和AddSbScoped<ICar, Car>這種添加依賴注入的方式也保持了net core自帶DI的原滋原味,簡潔優雅,而且實現了動態代理,只須要在參數裏添加攔截器,就能實時攔截,這裏參數爲params Type[],能夠添加N個攔截器。
在博客園潛水了好幾年,見證了net core從1.0到快出3.0,這也是第一次嘗試着寫博客,爲net core的發揚光大盡本身的一份力。