• Lind.DDD.Aspects通过Plugins实现方法的动态拦截~Lind里的AOP


    回到目录

    .Net MVC之所以发展的如些之好,一个很重要原因就是它公开了一组AOP的过滤器,即使用这些过滤器可以方便的拦截controller里的action,并注入我们自己的代码逻辑,向全局的异常记录,用户授权,Url授权,操作行为记录等,这一大批Lind的基本组件都是实现MVC和API的过滤实现的,使用这些过滤让我们不用去像HttpModule和HttpHandler那样,还要在Config里配置注入点,让程序员在开发方式上感觉很舒服,维护成功很低!

    本文主要内容点

    1. Lind.DDD里的方法拦截器
    2. 动态注入需要Lind.DDD.Plugins的支持
    3. 零配置的方法拦截
    4. 一个日志拦截器
    5. 正在构建一个缓存拦截器

    目录结构

    Lind.DDD里的方法拦截器

    Lind.DDD.Aspects这个拦截器起源自ABP框架,但不知道为什么,ABP对这个拦截器并没有完全实现,所以今天大叔又实现了一下,解决了相关BUG, 对方法拦截上,在动态代理工厂里对方法拦截上下文添加了一些必要的参数,因为大叔认为,你只提供一个“方法名称”参数,太过简单了,哈哈。

        /// <summary>
        /// 方法相关信息
        /// </summary>
        public class MethodMetadata
        {
            /// <summary>
            /// 上下文
            /// </summary>
            private MethodInfo _context;
            /// <summary>
            /// 方法名
            /// </summary>
            private string _methodName;
    
            public MethodMetadata(string methodName, MethodInfo context = null)
            {
                _methodName = methodName;
                _context = context;
            }
            /// <summary>
            /// 方法名称
            /// </summary>
            public virtual string MethodName
            {
                get { return _methodName; }
                set { _methodName = value; }
            }
            /// <summary>
            /// 方法上下文
            /// </summary>
            public virtual string Context
            {
                get { return _context; }
                set { _context = value; }
            }
        }

    一个简单的日志拦截器的实现,它在方法执行前去拦截

       /// <summary>
        /// 方法执行前拦截,并记录日志
        /// </summary>
        public class LoggerAspectAttribute : BeforeAspectAttribute
        {
            public override void Action(InvokeContext context)
            {
                Console.WriteLine("logger start!" + context.Method.MethodName);
                Lind.DDD.Logger.LoggerFactory.Instance.Logger_Info(context.Method.MethodName + "这个方法开始执行");
            }
        }

    而在程序中,这个特性Attribute如何被动态代理拦截呢,事件上,如果你直接写代码也是可以的,就是使用Aspect提供的ProxyFactory工厂来进行生产,但大叔认为,这样的代码耦合度太高,而且对于现有的代码还需要进行修改,最重要一点,这种代码总感觉有种坏味道!

         static void Main(string[] args)
            {
                ProxyFactory.CreateProxy<ITest>(typeof(LoggerAspectTest)).Do();
                Console.Read();
            }

    所以就有了下面大叔的封装,用到了Lind.DDD.Plugins这个插件模式,将所有的拦截器都先进行注册,然后在生产对象时为它动态添加对应的ProxyFactory对象,请大家接着向下看,动态注入需要Lind.DDD.Plugins的支持这部分讲解。

    动态注入需要Lind.DDD.Plugins的支持

    上面的拦截器只是简单的实现,简单的调用,而不具有一般性,即你需要自己维护需要“拦截的代码”,而大叔在进行使用中感觉很不爽,于是想起了Plugins,让插件为我们实现这种注入,就像MVC的Filter一样,在框架本身去实现方法拦截的功能!大叔认为这样才是最好的!

    1 所有拦截器都继承IAspectProxy表示接口,而它自己则是继承IPlugins的

       /// <summary>
        /// 支持AOP拦截的接口,它被认为是一种插件动态注入到系统中
        /// </summary>
        public interface IAspectProxy : Lind.DDD.Plugins.IPlugins { }

    2 在PluginManager的Resolve方法中,添加动态的ProxyFactory实现,让实现了IAspectProxy的类型,自动进行拦截器的实现

          /// <summary>
            /// 从插件容器里返回对象
            /// </summary>
            /// <param name="serviceName">对象全名</param>
            /// <param name="serviceType">接口类型</param>
            /// <returns></returns>
            public static object Resolve(string serviceName, Type serviceType)
            {
                var obj = _container.ResolveNamed(serviceName, serviceType);
                if (typeof(Lind.DDD.Aspects.IAspectProxy).IsAssignableFrom(serviceType))
                {
                    obj = ProxyFactory.CreateProxy(serviceType, obj.GetType());
                }
                return obj;
            }

    OK,有了上面的代码,我们的方法拦截就成了一种插件了,在使用的时间之前的插件的使用方法相同,当然底层还是使用autofac来实现的Ioc容器。

     var old = Lind.DDD.Plugins.PluginManager.Resolve<IAopHelloTest2>("Lind.DDD.UnitTest.AopHello");
     old.Hello("zz", 1);

    一个日志拦截器

    日志记录是一些业务复杂方法必备的,如一些订单方法,用户提现方法都会添加相关的日志,而如果希望动态添加日志,而不在代码段中去添加,则可以设计一个日志拦截器,当然你可以在方法执行前去控制,也可以在方法执行后去控制!

      /// <summary>
        /// 方法执行前拦截,并记录日志
        /// </summary>
        public class LoggerAspectAttribute : BeforeAspectAttribute
        {
            public override void Action(InvokeContext context)
            {
                Console.WriteLine("logger start!" + context.Method.MethodName);
                Lind.DDD.Logger.LoggerFactory.Instance.Logger_Info(context.Method.MethodName + "这个方法开始执行");
            }
        }
    
        /// <summary>
        /// 方法执行完成后拦截,并记录日志
        /// </summary>
        public class LoggerEndAspectAttribute : AfterAspectAttribute
        {
            public override void Action(InvokeContext context)
            {
                Console.WriteLine("logger start!" + context.Method.MethodName);
                Lind.DDD.Logger.LoggerFactory.Instance.Logger_Info(context.Method.MethodName + "这个方法开始执行");
            }
        }

    目录方法需要添加这种日志的行为,只要在方法上添加对应的特性即可,(方法不需要为虚方法)而不需要修改方法代码体,如下面的代码

       /// <summary>
        /// AOP调用方式
        /// </summary>
        public class LoggerAspectTest : ITest
        {
            [LoggerAspectAttribute]
            public void Do()
            {
                //我做事情
                Console.WriteLine("我做事情");
            }
        }

    正在构建一个缓存拦截器

    目前,大叔正在构建一个缓存的拦截器,主要是实现对方法返回值的缓存,而不需要将这种缓存判断的逻辑写在每个方法体内,大叔认为,这种面向切面的AOP的设计,才是大势所趋,敬请大家期待!

       /// <summary>
        /// 缓存拦截器
        /// </summary>
        public class CachingAspectAttribute : BeforeAspectAttribute
        {
            CachingMethod cachingMethod;
            public CachingAspectAttribute(CachingMethod cachingMethod)
            {
                this.cachingMethod = cachingMethod;
            }
    
            public override void Action(InvokeContext context)
            {
                var method = context.Method;
                string prefix = "Lind";
                var baseInterfaces = context.GetType().GetInterfaces();
                if (baseInterfaces != null && baseInterfaces.Any())
                {
                    foreach (var item in baseInterfaces)
                    {
                        prefix += item.ToString() + "_";
                    }
                }
    
                //键名,在put和get时使用
                var key = prefix + method.MethodName;
                Console.WriteLine(key);
                switch (cachingMethod)
                {
                    case CachingMethod.Remove:
                        //……
                        break;
                    case CachingMethod.Get:
                        //……
                        break;
                    case CachingMethod.Put:
                        //……
                        break;
                    default:
                        throw new InvalidOperationException("无效的缓存方式。");
    
                }
            }
        }

    我们对支持的追求将不会停止,希望广大青年都可以踏一心来,去认真的研究一个技术,事实上,对一个技术研究透了,大叔认为就足够了!

    此致

    敬礼

    回到目录

  • 相关阅读:
    ●BZOJ 2752 [HAOI2012]高速公路(road)
    ●UVA 11021 tunnello
    ●POJ 2794 Double Patience
    【51Nod1555】布丁怪
    【LG1600】[NOIP2016]天天爱跑步
    【LG5171】Earthquake
    【LG4437】[HNOI/AHOI2018]排列
    【CF1097F】Alex and a TV Show
    【51Nod 1769】Clarke and math2
    【LG5330】[SNOI2019]数论
  • 原文地址:https://www.cnblogs.com/lori/p/6139345.html
Copyright © 2020-2023  润新知