写过spring boot之后,那种无处不在的注解让我非常喜欢,比如属性注入@autowire,配置值注入@value,声明式事物@Transactional等,都非常简洁优雅,那么我就在想,这些在net core里能实现么?经过一番摸索,终于实现并整理成此文。

IOC方面,个人非常喜欢net core自带的DI,因为他注册服务简洁优雅,3个生命周期通俗易懂,所以就没使用autofac等其他容器,AOP方面,使用了业内鼎鼎大名的Castle.DynamicProxy(简称DP),所以要在nuget中添加Castle.Core的依赖包,这里大家可能会有疑问,利用mvc的actionFilter不就可以实现了么,为什么还要引用DP呢,因为呀,actionFilter只在controller层有效,普通类他就无能为力了,而DP无所不能。

1.定义注解和需要用到的类

属性注入注解

  [AttributeUsage(AttributeTargets.Property)]
public class AutowiredAttribute : Attribute
{
}

配置值注入注解

   [AttributeUsage(AttributeTargets.Property)]
public class ValueAttribute : Attribute
{
public ValueAttribute(string value = "")
{
this.Value = value;
} public string Value { get; }
}

声明式事物注解

   [AttributeUsage(AttributeTargets.Method)]
public class TransactionalAttribute : Attribute
{
}

工作单元接口及实现类,用来实现事物操作,注入级别为scope,一次请求公用一个工作单元实例

    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();
}
}

控制器HomeController

 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格式即可),具体代码如下,

{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"oilNo": ,
"HelpNumber": "",
"description": "我要开始飙车了"
}

2.效果图

从上图可以看到,正常注入,正常开启拦截器和事务,正确读取配置值。

从上图可以看到,我们的控制器,ICar和Engine全部都是动态代理类,注入正常。

3.核心代码

第一部分,添加一个扩展类,名叫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个拦截器。

4.写在最后

在博客园潜水了好几年,见证了net core从1.0到快出3.0,这也是第一次尝试着写博客,为net core的发扬光大尽自己的一份力。

     

net core天马行空系列:原生DI+AOP实现spring boot注解式编程的更多相关文章

  1. net core天马行空系列: 泛型仓储和声明式事物实现最优雅的crud操作

    系列目录 1.net core天马行空系列:原生DI+AOP实现spring boot注解式编程 哈哈哈哈,大家好,我就是那个高产似母猪的三合,长久以来,我一直在思考,如何才能实现高效而简洁的仓储模式 ...

  2. net core天马行空系列: 一个接口多个实现类,利用mixin技术通过自定义服务名,实现精准属性注入

    系列目录 1.net core天马行空系列:原生DI+AOP实现spring boot注解式编程 2.net core天马行空系列: 泛型仓储和声明式事物实现最优雅的crud操作 哈哈哈哈,大家好,我 ...

  3. net core天马行空系列:SummerBoot,将SpringBoot的先进理念与C#的简洁优雅合二为一

    系列目录 1.net core天马行空系列:原生DI+AOP实现spring boot注解式编程 2.net core天马行空系列: 泛型仓储和声明式事物实现最优雅的crud操作 3.net core ...

  4. net core天马行空系列:移植Feign,结合Polly,实现回退,熔断,重试,超时,做最好用的声明式http服务调用端

    系列目录 1.net core天马行空系列:原生DI+AOP实现spring boot注解式编程 2.net core天马行空系列: 泛型仓储和声明式事物实现最优雅的crud操作 3.net core ...

  5. Spring Boot系列(三):Spring Boot整合Mybatis源码解析

    一.Mybatis回顾 1.MyBatis介绍 Mybatis是一个半ORM框架,它使用简单的 XML 或注解用于配置和原始映射,将接口和Java的POJOs(普通的Java 对象)映射成数据库中的记 ...

  6. Spring Boot系列(四):Spring Boot源码解析

    一.自动装配原理 之前博文已经讲过,@SpringBootApplication继承了@EnableAutoConfiguration,该注解导入了AutoConfigurationImport Se ...

  7. ASP.NET CORE 学习之原生DI实现批量注册

    以前使用Autofac的时候,只需一句AsImplementInterfaces()就可以很轻松实现批量注册功能.而asp.net core内置的DI框架没有现成的批量注册方法,考虑到替换Autofa ...

  8. ASP.NET Core 3.0 原生DI拓展实现IocManager

    昨天.NET Core 3.0 正式发布,创建一个项目运行后发现:原来使用的Autofac在ConfigureServices返回IServiceProvider的这种写法已经不再支持.当然Autof ...

  9. 响应式编程系列(一):什么是响应式编程?reactor入门

    响应式编程 系列文章目录 (一)什么是响应式编程?reactor入门 (二)Flux入门学习:流的概念,特性和基本操作 (三)Flux深入学习:流的高级特性和进阶用法 (四)reactor-core响 ...

随机推荐

  1. [小米OJ] 9. 移除 K 位得到最小值

    思路: 重复k次: 1.找到并且删除第一个 num[i] > num[i+1] 的第i位数字. 2.若删除过程中,序列变成递增序列,则直接删除最后一位. 注意除去字符串头的0 def solut ...

  2. [PTA] 数据结构与算法题目集 6-12 二叉搜索树的操作集

    唯一比较需要思考的删除操作: 被删除节点有三种情况: 1.叶节点,直接删除 2.只有一个子节点,将子节点替换为该节点,删除该节点. 3.有两个子节点,从右分支中找到最小节点,将其值赋给被删除节点的位置 ...

  3. PHP对接口执行效率慢的优化

    PHP对接口执行效率慢的优化 PHP对接口执行效率慢的优化 造成执行效率低的原因可以由很多方面找原因 从代码层面,代码质量低,执行效率也会有很大影响的. 从硬件方面,服务器配置低,服务器配置是基础,这 ...

  4. spark 源码分析之六--Spark RPC剖析之Dispatcher和Inbox、Outbox剖析

    在上篇 spark 源码分析之五 -- Spark内置RPC机制剖析之一创建NettyRPCEnv 中,涉及到了Diapatcher 内容,未做过多的剖析.本篇来剖析一下它的工作原理. Dispatc ...

  5. 手撸PHP数据库连接

    最近这个月过得确实有点狼狈....不停地复习,看书..终于到今天为止考完了2科了.能让我好好地写写博客了..前段时间的PHP课设我多学了点东西,在我们一般老师讲的php连接数据库方面做了一些优化.前段 ...

  6. 转发后找不到css

    当在jsp中引入css时,如果其相对路径相对于当前jsp文件的,而在一个和这个jsp的路径不一样的servlet中forward这个jsp时,就会发现这个css样式根本没有起作用. 这是因为在serv ...

  7. Jmeter之CSS选择器/JQuery选择器关联

    选择器: CSS选择器或JQuery选择器是Jmeter支持的两种语法,下面对其两种语法进行简单介绍 CSS选择器 JQuery选择器 Chrome - 复制CSS选择器 Google Chrome在 ...

  8. tomcat 启动是 jdbc警告

    the web application [ROOT] registered the JDBC driver [com.mysql.jdbc.Driver] but failed to unregist ...

  9. 【JDK】JDK源码分析-LinkedList

    概述 相较于 ArrayList,LinkedList 在平时使用少一些. LinkedList 内部是一个双向链表,并且实现了 List 接口和 Deque 接口,因此它也具有 List 的操作以及 ...

  10. Django settings.py 配置文件详解

    settings.py 配置文件 import os BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) #引 ...