• C# Attribute 实现简单的 AOP 处理的例子(转)


    工作中遇到这样的情况,项目开发之前,没提过什么日志的东西。

    项目开发的差不多了,说需要记录日志文件。

    比如客户端调用服务器的时候,都什么时间,谁,调用了什么之类的。

     

    目前还不确定,这个日志到底要怎么记录,以及都记录哪些信息。

    上网查询了一下 AOP 的东西,实现办法还真不少。

    这里就写个简单的,使用 Attribute 实现的例子。

     

    首先是个 目标类,也就是针对这个类的方法,来做AOP的操作。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    namespace A0075_AOPAttributes.Sample
    {
        public class ObjectClass
        {
            public void Test1()
            {
                Console.WriteLine("Test");
            }
            public void Test2(string para)
            {
                Console.WriteLine("Test:" + para);
            }
            public void Test3(int para)
            {
                Console.WriteLine("Test:" + para);
            }
        }
    }

    出于演示的目的,这个类只有3个方法。只输出点基本的信息。

    由于实际的项目,那个类有几十个方法,每个方法,都复制/粘贴一段 Log 代码上去,也不现实。

    下面是 Attribute的代码

     

    using System;
    using System.Text;
    using System.Runtime.Remoting.Messaging;

    namespace A0075_AOPAttributes.Sample
    {

        public class AutoLogSink : IMessageSink
        {

            /// <summary>
            /// 保存下一个接收器
            /// </summary>
            private IMessageSink nextSink;

            /// <summary>
            /// 在构造器中初始化下一个接收器
            /// </summary>
            /// <param name="next"></param>
            public AutoLogSink(IMessageSink next)
            {
                nextSink = next;
            }

            /// <summary>
            /// 必须实现的IMessageSink接口属性
            /// </summary>
            public IMessageSink NextSink
            {
                get
                {
                    return nextSink;
                }
            }

            /// <summary>
            /// 实现IMessageSink的接口方法,当消息传递的时候,该方法被调用
            /// </summary>
            /// <param name="msg"></param>
            /// <returns></returns>
            public IMessage SyncProcessMessage(IMessage msg)
            {
                //拦截消息,做前处理
                Preprocess(msg);
                //传递消息给下一个接收器
                IMessage retMsg = nextSink.SyncProcessMessage(msg);
                //调用返回时进行拦截,并进行后处理
                Postprocess(msg, retMsg);
                return retMsg;
            }


            /// <summary>
            /// IMessageSink接口方法,用于异步处理,我们不实现异步处理,所以简单返回null,
            /// 不管是同步还是异步,这个方法都需要定义
            /// </summary>
            /// <param name="msg"></param>
            /// <param name="replySink"></param>
            /// <returns></returns>
            public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
            {
                return null;
            }

            /// <summary>
            /// 前处理.
            /// </summary>
            /// <param name="msg"></param>
            private void Preprocess(IMessage msg)
            {
                //检查是否是方法调用,我们只拦截Order的Submit方法。
                IMethodCallMessage call = msg as IMethodCallMessage;
                if (call == null)
                    return;
                StringBuilder buff = new StringBuilder();
                buff.AppendFormat("$LOG$:开始调用{0}方法。参数数量:{1}", call.MethodName, call.InArgCount);
                buff.AppendLine();
                for (int i = 0; i < call.InArgCount; i++)
                {
                    buff.AppendFormat("  参数[{0}] : {1}", i + 1, call.InArgs[i]);
                    buff.AppendLine();
                }
                Console.Write(buff.ToString());
            }

            /// <summary>
            /// 后处理
            /// </summary>
            /// <param name="msg"></param>
            /// <param name="retMsg"></param>
            private void Postprocess(IMessage msg, IMessage retMsg)
            {
                IMethodCallMessage call = msg as IMethodCallMessage;
                if (call == null)
                    return;
                StringBuilder buff = new StringBuilder();
                buff.AppendFormat("$LOG$:调用{0}方法结束", call.MethodName);
                buff.AppendLine();
                Console.Write(buff.ToString());
            }
        }

     

        public class AutoLogProperty : IContextProperty, IContributeObjectSink
        {
            public AutoLogProperty()
            {
            }

            /// <summary>
            /// IContributeObjectSink的接口方法,实例化消息接收器
            /// </summary>
            /// <param name="obj"></param>
            /// <param name="next"></param>
            /// <returns></returns>
            public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink next)
            {
                return new AutoLogSink(next);
            }

            /// <summary>
            /// IContextProperty接口方法,如果该方法返回ture,在新的上下文环境中激活对象
            /// </summary>
            /// <param name="newCtx"></param>
            /// <returns></returns>
            public bool IsNewContextOK(Context newCtx)
            {
                return true;
            }

            /// <summary>
            /// IContextProperty接口方法,提供高级使用
            /// </summary>
            /// <param name="newCtx"></param>
            public void Freeze(Context newCtx)
            {
            }

            /// <summary>
            /// IContextProperty接口属性
            /// </summary>
            public string Name
            {
                get { return "AutoLog"; }
            }
        }

     

        [AttributeUsage(AttributeTargets.Class)]
        public class AutoLogAttribute : ContextAttribute
        {
            public AutoLogAttribute() : base("AutoLog")
            {
            }

            /// <summary>
            /// 覆盖 ContextAttribute方法,创建一个上下文环境属性
            /// </summary>
            /// <param name="ctorMsg"></param>
            public override void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg)
            {
                ctorMsg.ContextProperties.Add(new AutoLogProperty());
            }
        }


    }

    下面是 使用 Attribute 实现简单的 AOP 目标代码 [粗体的地方,为在原代码的基础上增加的代码]

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    namespace A0075_AOPAttributes.Sample
    {
        [AutoLog]
        public class ObjectClassAOP: ContextBoundObject
        {
            public void Test1()
            {
                Console.WriteLine("Test");
            }
            public void Test2(string para)
            {
                Console.WriteLine("Test:" + para);
            }
            public void Test3(int para)
            {
                Console.WriteLine("Test:" + para);
            }
        }
    }

     

    测试运行代码

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    using A0075_AOPAttributes.Sample;

    namespace A0075_AOPAttributes
    {
        class Program
        {
            static void Main(string[] args)
            {

                ObjectClass obj = new ObjectClass();
                obj.Test1();
                obj.Test2("Test");
                obj.Test3(1024);

                ObjectClassAOP objAop = new ObjectClassAOP();
                objAop.Test1();
                objAop.Test2("Test");
                objAop.Test3(1024);

                Console.ReadLine();
            }
        }
    }

    运行结果:

    Test
    Test:Test
    Test:1024
    $LOG$:开始调用Test1方法。参数数量:0
    Test
    $LOG$:调用Test1方法结束
    $LOG$:开始调用Test2方法。参数数量:1
      参数[1] : Test
    Test:Test
    $LOG$:调用Test2方法结束
    $LOG$:开始调用Test3方法。参数数量:1
      参数[1] : 1024
    Test:1024
    $LOG$:调用Test3方法结束

  • 相关阅读:
    数据结构10——最短路径
    获取JVM转储文件的Java工具类
    如何测试这个方法--性能篇
    如何测试这个方法--功能篇
    使用WireMock进行更好的集成测试
    性能测试框架第二版
    如何使用Selenium来计算自动化测试的投资回报率?
    模糊断言
    如何从测试自动化中实现价值
    如何获取JVM堆转储文件
  • 原文地址:https://www.cnblogs.com/zjoch/p/2117656.html
Copyright © 2020-2023  润新知