美文网首页
C#中利用Attribute实现AOP

C#中利用Attribute实现AOP

作者: seawish | 来源:发表于2019-10-28 18:02 被阅读0次

    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对象。

    参考文献

    相关文章

      网友评论

          本文标题:C#中利用Attribute实现AOP

          本文链接:https://www.haomeiwen.com/subject/klobvctx.html