天天看点

.Net基于Remoting远程调用实现的AOP拦截

AOP的作用就是横切关注点,然后将分离后的关注点已面的形式来呈现,这是概念性的说法,举个列子来说明吧。

假设在一个应用系统中,有一个共享的数据必须被并发同时访问,首先,将这个 数据封装在 数据对象中,称为Data Class,同时,将有多个访问类,专门用于在同一时刻访问这同一个数据对象。 为了完成上述并发访问同一资源的功能,需要引入锁Lock的概念,也就是说,某个时刻,当有一个访问类访问这个数据对象时,这个数据对象必须上锁Locked,用完后就立即解锁unLocked,再供其它访问类访问。 “锁”功能其实是这个系统的一个纵向切面,涉及许多类、许多类的方法。如图

.Net基于Remoting远程调用实现的AOP拦截

AOP拦截的实现

看到一些大神的文章利用remoting中的管道技术来实现信息拦截,这里把自己看过之后整理测试的代码给贴出来。

.Net基于Remoting远程调用实现的AOP拦截

上图展示了Remoting通过远程调用,通过消息接收器来进行的AOP拦截。具体看代码的实现,先看看如下的代码结构

.Net基于Remoting远程调用实现的AOP拦截
[AOP("AOP")]
    public class TestInstance : ContextBoundObject
    {
        public void TestMethod()
        {
            Console.WriteLine("TestMethod-Action…要被拦截的方法…");
            RxbMethod();
        }
        public void RxbMethod()
        {
            Console.WriteLine("RxbMethod-Action…要被拦截的方法…");
        }

    }
           

以上的代码是需要被拦截的类的方法。TestInstance类型继承自上下文绑定类ContextBoundObject对象,继承过后就表示TestInstance类型需要强制绑定上下文意思就是说它需要在一个特性环境的上下文中,对于没有继承ContextBoundObject类型的类型被称之为灵活对象,它们的诞生是在默认的当前上下文中。因为只有继承了ContextBoundObject才能创建新的上下文,然后当前上下文对于TestInstance的调用都是属于远程调用(在remoting里跨越了上下文边界的所有调用都应该叫远程调用,不管服务端在哪),只有这样才能利用remoting中的消息管道来进行消息拦截。

那么是在什么时候创建新的上下文的呢?在TestInstance类型定义的上面,有个AOPWriter特性类型,我们先来看下它的定义

这里为什么要说到这些内容呢?因为这样才能创建新的上下文,然后当前上下文对于MyContextObject的调用都是属于远程调用(在remoting里跨越了上下文边界的所有调用都应该叫远程调用,不管服务端在哪),只有这样才能利用remoting中的消息管道来进行消息拦截。

/// <summary>
    /// AOP拦截特性
    /// </summary>
    /*  解释:

        首先在上下文绑定对象进行实例化的时候系统默认的会调用IsContextOK()方法来判断当前执行实例化过程所在的上下文是否满足于上下文绑定对象的要求,
        这里我们实现是返回的false,意思就是当前上下文并不满足于当前特性所设置的类型所需要的,

        这是系统会去调用IContextAttribute中的GetPropertiesForNewContext()方法用于向新建上下文中添加自定义的上下文属性,
        也就是实现了IContextProperty接口类型的类型对象,在普通的运用中我们可以在自定义的上下文属性中设置一些逻辑操作,以便在新建上下文中使用
    */
    [AttributeUsage(AttributeTargets.Class)]
    public class AOPAttribute : Attribute, IContextAttribute
    {
        private string _name = string.Empty;
        public AOPAttribute() { }

        public AOPAttribute(string name)
        {
            _name = name;
        }
        //抽象化的创建AOPProperty的方法,可以放到具体的实现类中,越抽象越灵活。
        //public abstract AOPProperty CreateContextProperty();
        public AOPProperty CreateContextProperty()
        {
            return new AOPProperty(_name);
        }

        public void GetPropertiesForNewContext(IConstructionCallMessage msg)
        {
            AOPProperty contextProperty = CreateContextProperty();
            msg.ContextProperties.Add(contextProperty);
        }

        public bool IsContextOK(System.Runtime.Remoting.Contexts.Context ctx, IConstructionCallMessage msg)
        {
            return false;
        }
    }
           

AOPAttribute类型实现了IContextAttribute类型的接口。首先在上下文绑定对象进行实例化的时候系统默认的会调用IsContextOK()方法来判断当前执行实例化过程所在的上下文是否满足于上下文绑定对象的要求,这里我们在代码中的实现是返回的false,意思就是当前上下文并不满足于TestInstance类型所需要的,这是系统会去调用IContextAttribute中的GetPropertiesForNewContext()方法用于向新建上下文中添加自定义的上下文属性,也就是实现了IContextProperty接口类型的类型对象,在普通的运用中我们可以在自定义的上下文属性中设置一些逻辑操作,以便在新建上下文中使用。下面再来看看上下文属性对象的定义:

/// <summary>
    /// AOP远程调用上下文实体
    /// </summary>
    /* 解释:

    (1)上下文属性对象添加了个WriterMessage()方法,也就是上面所说的在上下文中的所有对象都可获取上下文属性中提供的逻辑操作。
        Name属性表示上下文属性名称,这个要是唯一的,在获取上下文属性就是根据这个Name属性值来获取的

     (2)这里上下文属性还实现了IContributeServerContextSink类型,
        上下文属性的实现中GetServerContextSink()将自定义的消息接收器添加到了新建上下文的消息接收器链的前端,
        这是一点非常重要我们AOP的实现主要依赖于自定义消息接收器中对于调用函数信息的拦截。
    */
    public class AOPProperty : IContextProperty, IContributeServerContextSink
    {
        private string _name = string.Empty;
        public AOPProperty() { }
        public AOPProperty(string name)
        {
            _name = name;
        }
        public void Freeze(Context newContext)
        {

        }

        public bool IsNewContextOK(Context newCtx)
        {
            return true;
        }


        public string Name
        {
            set { _name = value; }
            get { return _name; }
        }

        public IMessageSink GetServerContextSink(IMessageSink nextSink)
        {
            AOPSink aopSink = CreateAOPSink(nextSink) as AOPSink;
            aopSink.AOPPropertyName = Name;
            return aopSink;
        }

        protected IMessageSink CreateAOPSink(IMessageSink nextSink)
        {
            return new AOPSink(nextSink);
        }
    }
           

上下文属性对象添加了个WriterMessage()方法,也就是上面所说的在上下文中的所有对象都可获取上下文属性中提供的逻辑操作。Name属性表示上下文属性名称,这个要是唯一的,在获取上下文属性就是根据这个Name属性值来获取的。在上下文属性的实现中GetServerContextSink()将自定义的消息接收器添加到了新建上下文的消息接收器链的前端,这是一点非常重要我们AOP的实现主要依赖于自定义消息接收器中对于调用函数信息的拦截。

.Net基于Remoting远程调用实现的AOP拦截

下面我们再来看看消息接收器的定义:

/// <summary>
    /// AOP监听器
    /// </summary>
    /*  解释:


        在实例化绑定上下文对象的时候或者是调用定上下文对象的方法的时候都会调用SyncProcessMessage()方法,
        在SyncProcessMessage()方法中我们根据IMessage消息对象来获取当前远程对象执行方法的名称(对应MyContextObject的对象的函数名称),
        随之获取当前上下文属性,利用上下文属性中的逻辑操作来进行拦截后的操作。
    */
    public class AOPSink : IMessageSink
    {
        private IMessageSink _NextSink;


        private string _AOPPropertyName;


        public string AOPPropertyName
        {
            get { return _AOPPropertyName; }
            set { _AOPPropertyName = value; }
        }


        public AOPSink(IMessageSink nextsink)
        {
            _NextSink = nextsink;
        }


        public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
        {
            return null;
        }


        public IMessageSink NextSink
        {
            get { return _NextSink; }
        }


        //在实例化绑定上下文对象的时候或者是调用定上下文对象的方法的时候都会调用SyncProcessMessage()方法
        public IMessage SyncProcessMessage(IMessage msg)
        {
            bool check = true;
            IMethodCallMessage callmsg = msg as IMethodCallMessage;
            if (callmsg == null)
            {
                return null;
            }
            IMessage resultMsg = msg;
            check = ConfigRelationExecution.ActionConfigBySinkName(_AOPPropertyName, callmsg.MethodName, InterceptAchieveType.Before, callmsg);
            if (check)
            {
                //执行被拦截方法
                resultMsg = _NextSink.SyncProcessMessage(msg);//需要被拦截的方法
                check = ConfigRelationExecution.ActionConfigBySinkName(_AOPPropertyName, callmsg.MethodName, InterceptAchieveType.After, resultMsg);
            }
            else
            {
                //不执行被拦截方法
                resultMsg = new ReturnMessage(callmsg.InArgs, null, 0, null, callmsg);
            }
            return resultMsg;
        }
    }

           

对于消息接收器,是实现IMessageSink了,在代码的定义可以看到哟给NextSink的属性,用以链接在管道中的下个消息接收器并且已这样的形式形成消息接收器链,SyncProcessMessage()方法就可以理解为是调用的远程对象所执行的函数。

</pre><p></p><p style="text-indent: 28px;"><span style="font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 23px;"><span style="font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 23px;">接下来在看看拦截器接口的定义,也就是定义了一个被拦截方法的前和后的执行的接口:</span></span></p><p style="text-indent: 28px;"><span style="font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 23px;"><span style="font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 23px;"></span></span></p><pre name="code" class="csharp"> /// <summary>
    /// 被拦截方法的前后的额外处理逻辑接口(即要实现扩展的地方)
    /// </summary>
    public interface IAOPInterceptAchieve
    {
        bool BeforeAchieve(IMethodCallMessage callMsg);
        bool AfterAchieve(IMethodReturnMessage returnMsg);
    }
           

通过以上的这些,Remoting远程调用的方式基本实现,下面为了更灵活拦截不同的方法,和在不同的位置拦截,新增了一个Config(拦截配置信息)和ConfigRelationExecution(拦截配置信息解析执行)的类。代码如下:

/// <summary>
    /// 配置拦截位置和要拦截的方法的信息
    /// </summary>
    public class Config
    {
        private Dictionary<string, InterceptAchieveType> _DictMethodAndAchieveType = new Dictionary<string, InterceptAchieveType>();//每个方法的拦截位置
        private Dictionary<string, List<IAOPInterceptAchieve>> _DictMethodAndAchieve = new Dictionary<string, List<IAOPInterceptAchieve>>();//每个拦截方法的拦截实现类

        /// <summary>
        /// 拦截方法和位置的字典
        /// key :被拦截的方法名
        /// value: 被拦截的位置
        /// </summary>
        public Dictionary<string, InterceptAchieveType> DictMethodAndAchieveType
        {
            get { return _DictMethodAndAchieveType; }
        }


        /// <summary>
        /// 拦截方法和拦截实现类的字典
        /// key :被拦截的方法名
        /// value: 被拦截的位置
        /// </summary>
        public Dictionary<string, List<IAOPInterceptAchieve>> DictMethodAndAchieve
        {
            get { return _DictMethodAndAchieve; }
        }

        /// <summary>
        /// 给不同的被拦截方法设置各自的拦截位置
        /// </summary>
        /// <param name="methodName">需要被拦截的方法</param>
        /// <param name="interceptAchieveType">拦截位置</param>
        public void SetMethodAndActhieveType(string methodName, InterceptAchieveType interceptAchieveType)
        {
            if (string.IsNullOrEmpty(methodName))
            {
                throw new ArgumentNullException("AOP的被拦截方法名不可为空");
            }
            _DictMethodAndAchieveType.Add(methodName, interceptAchieveType);
        }

        /// <summary>
        /// 设置拦截方法接口(IAOPInterceptAchieve)实现类
        /// </summary>
        /// <param name="aopInterceptAchieve"></param>
        public void AddAOPInterceptAchieve(string methodName, List<IAOPInterceptAchieve> aopInterceptAchieve)
        {
            if (aopInterceptAchieve == null)
            {
                throw new ArgumentNullException("IAOPInterceptAchieve接口的实现类不可为null");
            }
            _DictMethodAndAchieve.Add(methodName,aopInterceptAchieve);
        }

        /// <summary>
        /// 设置拦截方法接口(IAOPInterceptAchieve)实现类
        /// </summary>
        /// <param name="aopInterceptAchieve"></param>
        public void RemoveAOPInterceptAchieve(string methodName)
        {
            _DictMethodAndAchieve.Remove(methodName);
        }
    }
           
/// <summary>
    /// 配置信息解析引擎
    /// </summary>
    public class ConfigRelationExecution
    {
        private static Dictionary<string, Config> _ConfigRelation;

        /// <summary>
        /// 设置拦截器和其对应的Config的Dictionary
        /// key:拦截器的标识符
        /// value: 拦截对象的配置信息
        /// </summary>
        public static Dictionary<string, Config> ConfigRelation
        {
            get
            {
                if (_ConfigRelation == null)
                {
                    _ConfigRelation = new Dictionary<string, Config>();
                }
                return _ConfigRelation;
            }
        }

        /// <summary>
        /// 解析Config配置信息,并调用各自方法的拦截处理
        /// </summary>
        /// <param name="sinkname">上下文属性名</param>
        /// <param name="methodname">方法名</param>
        /// <param name="interceptAchieveType">拦截类型</param>
        /// <param name="msg">IMessage对象</param>
        public static bool ActionConfigBySinkName(string sinkname, string methodname, InterceptAchieveType interceptAchieveType, IMessage msg)
        {
            bool check = true;
            //如果当前的拦截器包含了拦截器的配置对象
            if (_ConfigRelation.ContainsKey(sinkname))
            {
                Config config = _ConfigRelation[sinkname];
                //如果拦截器实现对象不为空 并且 拦截字典中包括当前的方法,则执行拦截操作
                if (config.DictMethodAndAchieve.ContainsKey(methodname) ==true && config.DictMethodAndAchieveType.ContainsKey(methodname) == true)
                {
                    //如果InterceptAchieveType.All,则Before和After都执行
                    if (config.DictMethodAndAchieveType[methodname] == InterceptAchieveType.All)
                    {
                        List<IAOPInterceptAchieve> IAOP = config.DictMethodAndAchieve[methodname];
                        foreach (var item in IAOP)
                        {
                            check = InterceptAchieve(interceptAchieveType, item, msg);
                            if (check == false)
                            {
                                break;    
                            }
                        }
 
                    }
                    //不是InterceptAchieveType.All时,则只拦截拦截字典中的该方法的拦截位置
                    else if (config.DictMethodAndAchieveType[methodname] == interceptAchieveType)
                    {
                        List<IAOPInterceptAchieve> IAOP = config.DictMethodAndAchieve[methodname];
                        foreach (var item in IAOP)
                        {
                            check = InterceptAchieve(config.DictMethodAndAchieveType[methodname], item, msg);
                            if (check == false)
                            {
                                break;
                            }
                        }
                    }
                }
            }
            return check;
        }

        //执行实现IAOPInterceptAchieve接口的实现类的具体拦截逻辑
        /// <summary>
        /// 执行实现IAOPInterceptAchieve接口的实现类的具体拦截逻辑
        /// </summary>
        /// <param name="interceptAchieveType">拦截位置</param>
        /// <param name="aopInterceptAchieve">拦截器实现类对象</param>
        /// <param name="msg">IMessage对象</param>
        private static bool InterceptAchieve(InterceptAchieveType interceptAchieveType, IAOPInterceptAchieve aopInterceptAchieve, IMessage msg)
        {
            bool check = true;
            if (interceptAchieveType == InterceptAchieveType.Before)
            {
                IAOPInterceptAchieve Achieve = aopInterceptAchieve as IAOPInterceptAchieve;
                if (Achieve != null)
                {
                    check = Achieve.BeforeAchieve((IMethodCallMessage)msg);
                }
            }
            else
            {
                IAOPInterceptAchieve Achieve = aopInterceptAchieve as IAOPInterceptAchieve;
                if (Achieve != null)
                {
                    check = Achieve.AfterAchieve((IMethodReturnMessage)msg);
                }
            }
            return check;
        }
    }

    /// <summary>
    /// 拦截位置的枚举
    /// </summary>
    public enum InterceptAchieveType
    {
        Before,//方法前拦截
        After,//方法后拦截
        All//方法前和方法后都拦截
    }
           

至此,所有准备工作已完成,下面看看测试代码:

首先新增实现了拦截接口的类型:

/// <summary>
    /// 拦截器角色(也就是需要扩展的地方)
    /// 实现的是IAOPInterceptAchieve这个扩展接口
    /// </summary>
    public class LogWriterAchieve : IAOPInterceptAchieve
    {
        public bool BeforeAchieve(IMethodCallMessage callMsg)
        {
            if (callMsg == null)
            {
                return false;
            }
            Console.WriteLine(callMsg.MethodName + "—Before日志拦截—" + this.GetType().Name);
            return true;
        }

        public bool AfterAchieve(IMethodReturnMessage returnMsg)
        {
            if (returnMsg == null)
            {
                return false;
            }
            Console.WriteLine(returnMsg.MethodName + "—After日志拦截—" + this.GetType().Name);
            return true;
        }
    }
    public class AOPDataValidateAchieve : IAOPInterceptAchieve
    {
        public bool BeforeAchieve(IMethodCallMessage callMsg)
        {
            if (callMsg == null)
            {
                return false;
            }
            Console.WriteLine(callMsg.MethodName + "—Before数据拦截—" + this.GetType().Name);
            return true;
        }

        public bool AfterAchieve(IMethodReturnMessage returnMsg)
        {
            if (returnMsg == null)
            {
                return false;
            }
            Console.WriteLine(returnMsg.MethodName + "—After数据拦截—" + this.GetType().Name);
            return true;
        }
    }
           

其次是调用过程代码:

//添加拦截方法和拦截位置
            Config config1 = new Config();
            config1.SetMethodAndActhieveType("TestMethod", InterceptAchieveType.All);

            //配置拦截实现对象
            List<IAOPInterceptAchieve> listAOP = new List<IAOPInterceptAchieve>();
            listAOP.Add(new LogWriterAchieve());
            listAOP.Add(new AOPDataValidateAchieve());
            config1.AddAOPInterceptAchieve("TestMethod", listAOP);

            //向配置节点中添加拦截上下文特性(name唯一)和配置
            ConfigRelationExecution.ConfigRelation.Add("AOP", config1);

            TestInstance testInstance = new TestInstance();
            //testInstance.TestMethod();
            Console.WriteLine("\n");
            testInstance.TestMethod();
           

运行结果如下:

.Net基于Remoting远程调用实现的AOP拦截