C#的AOP实现主要是参考了这篇博客,并对实现过程中遇到的问题进行分析和修改)。
AOP实现流程
类拦截
AOPContextAttribute.cs
定义一个AOP上下文特性,用于标注需要支持AOP方法的类,通过 ContextAttribute, IContributeObjectSink 来获取类的上下文环境,这是通过 Attribute 拦截参数和获取返回值的前提。
/// <summary>
/// 定义一个AOP上下文特性,用于标注需要支持AOP方法的类,
/// 通过 ContextAttribute, IContributeObjectSink 来获取类的上下文环境,
/// 这是通过 Attribute 拦截参数和获取返回值的前提。
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public sealed class AOPContextAttribute : ContextAttribute, IContributeObjectSink
{
public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink nextSink)
{
return new AOPHandler(nextSink);
}
public AOPContextAttribute() : base("AOPContext")
{
}
}
AOPContext.cs
实现一个继承自 ContextBoundObject 的类,并标注 [AOPContext] 特性,两者配合,使得这个类下的方法可以被成功拦截。需要支持AOP的类,继承这个类即可。
[AOPContext]
public class AOPContext : ContextBoundObject
{
}
方法处理
AOPBeforeAttribute.cs
用于标注需要拦截参数的方法,和指出对应的处理函数。
// <summary>
/// 用于标注需要拦截参数的方法,和指出对应的处理函数。
/// @author: seawish.zheng
/// </summary>
public class AOPBeforeAttribute : Attribute
{
public string FullClassName;
public string StaticMethodName;
public AOPBeforeAttribute(string fullClassName, string methodName)
{
FullClassName = fullClassName;
StaticMethodName = methodName;
}
}
AOPAfterAttribute
/// <summary>
/// 用于标注需要获取返回值的方法,和指出对应的处理函数。
/// @author: seawish.zheng
/// </summary>
public class AOPAfterAttribute : Attribute
{
public string FullClassName;
public string StaticMethodName;
public AOPAfterAttribute(string fullClassName, string methodName)
{
FullClassName = fullClassName;
StaticMethodName = methodName;
}
}
ReflectionUtil
/// <summary>
/// 获取方法特性
/// </summary>
class ReflectionUtil
{
public static T GetAttribute<T>(MethodInfo method) where T : Attribute
{
var attrs = method.GetCustomAttributes(typeof(T), false);
if (attrs.Length != 0)
{
var attribute = attrs[0] as T;
if (attribute != null)
{
return attribute;
}
}
return null;
}
}
handler类
/// <summary>
/// 这个类用于实现 具体的参数拦截和返回值获取操作。
/// todo: ?????目前拦截的是@AopBeforeAttribute标注的方法的上一个方法,奇怪。
/// </summary>
public class AOPHandler : IMessageSink
{
/// <summary>
/// 下一个接收器
/// </summary>
private readonly IMessageSink _nextSink;
public AOPHandler(IMessageSink nextSink)
{
_nextSink = nextSink;
}
public IMessageSink NextSink
{
get { return _nextSink; }
}
/// <summary>
/// 同步处理方法
/// </summary>
/// <param name="msg"></param>
/// <returns></returns>
public IMessage SyncProcessMessage(IMessage msg)
{
IMessage message = null;
var callMessage = msg as IMethodCallMessage;
if (callMessage != null)
{
// Before
var before = ReflectionUtil.GetAttribute<AOPBeforeAttribute>(callMessage.MethodBase as MethodInfo);
if (before != null)
{
PreProceed(msg, before);
}
// Invoke
message = _nextSink.SyncProcessMessage(msg);
// After
var after = ReflectionUtil.GetAttribute<AOPAfterAttribute>(callMessage.MethodBase as MethodInfo);
if (after != null)
{
PostProceed(message, after);
}
}
else
{
message = _nextSink.SyncProcessMessage(msg);
}
return message;
}
/// <summary>
/// 异步处理方法
/// </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>
/// <param name="before"></param>
public void PreProceed(IMessage msg, AOPBeforeAttribute before)
{
var message = msg as IMethodMessage;
var type = Assembly.GetCallingAssembly().GetType(before.FullClassName);
var param = message.Args;
type.InvokeMember(before.StaticMethodName, BindingFlags.InvokeMethod, null, null, param);
Console.WriteLine("test");
}
/// <summary>
/// 方法执行后
/// </summary>
/// <param name="msg"></param>
/// <param name="after"></param>
public void PostProceed(IMessage msg, AOPAfterAttribute after)
{
var message = msg as IMethodReturnMessage;
var type = Assembly.GetCallingAssembly().GetType(after.FullClassName);
var param = message.ReturnValue;
type.InvokeMember(after.StaticMethodName, BindingFlags.InvokeMethod, null, null, new[] { param });
Console.WriteLine("test");
}
}
aop示例
AopSample
TestMethod1为将被拦截处理的方法,before方法和after方法可抽取到其他类中。
/// <summary>
/// 业务实现类,主要是配置Before和After方法的实现。
/// </summary>
namespace Dji.MES.WebAPI
{
public class Model1
{
public int a
{
get; set;
}
public int b
{
get;
set;
}
}
public class AopSample : AOPContext
{
/// <summary>
/// AOPBefore("Dji.MES.Base.AopSample", "Before")
/// </summary>
/// <returns></returns>
[AOPBefore("Dji.MES.WebAPI.AopSample", "Before")]
[AOPAfter("Dji.MES.WebAPI.AopSample", "After")]
public int TestMethod1(Model1 model1)
{
Console.WriteLine("Process Test 1 :" + model1.a + "\t" + model1.b);
return model1.a + model1.b;
}
/// <summary>
/// 方法执行前,对参数进行预处理
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
public static void Before(Model1 model1)
{
model1.a = 200;
model1.b = 400;
}
/// <summary>
/// 方法执行后,对返回值进行处理
/// </summary>
/// <param name="result"></param>
public static void After(int result)
{
Console.WriteLine("End :" + result);
}
}
}
问题解决
aop拦截方法
被拦截的类需要继承AOPContext,并且该类中调用的第一个方法会被拦截,如果存在嵌套方法,不会拦截到第二个方法。
Attribute的type获取失败
解决
- 该问题为被拦截的类找不到问题,需要保证AOPhandle类能够访问到该类,如果属于不同的dll中,则需要引用。
- AOPBefore和AOPAfter的参数错误,需使用全限定名,并且是包含before和after静态方法的类。
改为全限定名。
/// <summary>
/// AOPBefore("Dji.MES.Base.AopSample", "Before")
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
[AOPBefore("Dji.MES.WebAPI.AopSample", "Before")]
[AOPAfter("Dji.MES.WebAPI.AopSample", "After")]
public int TestMethod1(int a, int b)
{
Console.WriteLine("Process Test 1 :" + a + "\t" + b);
return a + b;
}
值参数未改变
目前的实现无法改值参,可以考虑将数值类型封装到model中,传model对象。