zoukankan      html  css  js  c++  java
  • net core天马行空系列:原生DI+AOP实现spring boot注解式编程

           写过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": 95,
      "HelpNumber": "110",
      "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;
            }
        }
    View Code

    第二部分,添加一个自定义控制器激活类,用以替换掉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)
            {
            }
        }
    View Code

    第三部分,在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的发扬光大尽自己的一份力。

         

  • 相关阅读:
    【转】修改mysql数据库的用户名和密码
    oracle 11g密码过期问题解决方法
    配置网络YUM源
    RedHat 7.0更新升级openSSH7.4p1
    Linux下端口被占用解决
    js function 中的返回值
    代码笔记1
    element模态框dialog中的select组件中选中无反应无显示
    vue如何使用rules对表单字段进行校验
    关于JSON.parse(JSON.stringify(obj))实现深拷贝应该注意的坑
  • 原文地址:https://www.cnblogs.com/hezp/p/11346120.html
Copyright © 2011-2022 走看看