写过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; } }
Declarative things annotations
[AttributeUsage(AttributeTargets.Method)] public class TransactionalAttribute : Attribute { }
And the work unit interface implementation class that implements the operation things, injection level scope, a request for a common unit of work instance
public interface IUnitOfWork : IDisposable { /// <summary> /// 开启事务 /// </summary> void BeginTransaction(); /// <summary> /// 提交 /// </summary> void Commit(); /// <summary> /// 事物回滚 /// </summary> void RollBack(); }
public class the UnitOfWork: IUnitOfWork { public void the BeginTransaction () { Console.WriteLine ( " open transaction " ); } public void the Commit () { Console.WriteLine ( " commit transaction " ); } public void the Dispose () { // the throw new new System.NotImplementedException (); } public void rollBack () { Console.WriteLine ( " rolling back the transaction"); } }
Interceptor
/// <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("Engine stall distress call dial " + 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 modified, add some tests key-value pairs, (Chinese distortion found if the test output, the format can be saved to utf8 appsettings.json), specific code as follows,
{ "Logging": { "LogLevel": { "Default": "Warning" } }, "AllowedHosts": "*", "oilNo": 95, "HelpNumber": "110", "description": "我要开始飙车了" }
2.效果图
It can be seen from the figure, the normal injection, normally open interceptors and transaction configuration values read correctly.
You can see from the chart, our controller, ICar and Engine are all dynamic proxy classes, the normal injection.
3. core code
A first portion, to add an extension class named SummerBootExtentions.cs, the following code
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); staticPublic } 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; } }
A second portion, add custom controller activates a class to replace mvc own activation class, named SbControllerActivator.cs, the following code
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的发扬光大尽自己的一份力。