工作中遇到这样的情况,项目开发之前,没提过什么日志的东西。
项目开发的差不多了,说需要记录日志文件。
比如客户端调用服务器的时候,都什么时间,谁,调用了什么之类的。
目前还不确定,这个日志到底要怎么记录,以及都记录哪些信息。
上网查询了一下 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方法结束