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方法结束

展开阅读全文

没有更多推荐了,返回首页