C#中利用Attribute实现AOP

2019-10-28  本文已影响0人  seawish

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获取失败

解决

  1. 该问题为被拦截的类找不到问题,需要保证AOPhandle类能够访问到该类,如果属于不同的dll中,则需要引用。
  2. 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对象。

参考文献

上一篇下一篇

猜你喜欢

热点阅读