NET Framework的AOP实施方法1 ContextBoundObject

ops/2024/11/2 15:27:32/

NET Core的AOP实施方法1 DispatchProxy
NET Framework的AOP实施方法1 ContextBoundObject
NET Framework的AOP实施方法2 RealProxy

源码见Github

ContextBoundObject

NET Framework需要实现AOP,可以借助于System.Runtime.Remoting.Contexts命名空间中的ContextBoundObject类。

ContextBoundObject 是 .NET Framework 中的一个抽象类,它继承自 MarshalByRefObject,用于定义所有上下文绑定类的基类。上下文绑定对象是指那些驻留在特定上下文中并受上下文规则约束的对象。上下文是一组属性或使用规则,用于定义对象集合所在的环境,当对象进入或离开上下文时,会强制实施这些规则。

上下文绑定的对象只能在创建它的上下文中正常运行,而其他对象访问它时,必须通过透明代理(Transparent Proxy)来操作。这与上下文灵活对象(context-agile)不同,后者可以存在于任意上下文中,并且不需要特定的操作就可以被创建和管理。

ContextBoundObject 类的一个重要用途是实现自动同步。通过将 SynchronizationAttribute 应用于 ContextBoundObject 的子类,可以确保该类的实例在同一时刻只能被一个线程访问,从而实现线程安全。这种机制是通过在每个方法或属性的每次调用时自动加锁来实现的,锁的作用域被称为同步上下文。

然而,需要注意的是,当前版本的公共语言运行时(CLR)不支持具有泛型方法的泛型 ContextBoundObject 类型或非泛型 ContextBoundObject 类型。尝试创建此类的实例会导致 TypeLoadException 异常。

在实际应用中,ContextBoundObject 可以用来实现诸如事务处理、同步访问等跨多个对象的操作,同时保持代码的简洁性和易于管理。不过,由于 .NET 的发展,一些过去使用 ContextBoundObject 实现的功能可能已经有了更现代的替代方案。

适用场景

NET Framework

使用步骤

引用 System.Runtime.Remoting 命名空间。
定义一个要进行同步上下文的类,并继承自 ContextBoundObject 类。该类执行方法就会被拦截

class Data : ContextBoundObject
{public void DoWork(){Console.WriteLine("正在处理。。。。。");}
}

AopHandler.cs:定义在同步上下文时执行的逻辑,所有被拦截的内容都会进入到该类中

using System.Runtime.Remoting.Messaging;public sealed class AopHandler : IMessageSink
{//下一个接收器private IMessageSink nextSink;public IMessageSink NextSink{get { return nextSink; }}public AopHandler(IMessageSink nextSink){this.nextSink = nextSink;}//同步处理方法public IMessage SyncProcessMessage(IMessage msg){IMessage retMsg = null;//方法调用消息接口IMethodCallMessage call = msg as IMethodCallMessage;if (call == null){retMsg = nextSink.SyncProcessMessage(msg);return retMsg;}retMsg = BeforeAndAfterDoWork(msg, call);return retMsg;}public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink){return null;}private IMessage BeforeAndAfterDoWork(IMessage msg, IMethodCallMessage methodCall){IMessage retMsg;var attrs = Attribute.GetCustomAttributes(methodCall.MethodBase);return WorkersManager.DoWork(attrs, nextSink, msg);}
}

WorkersManager.cs:定义执行逻辑管理类,用于当被拦截的逻辑不止一个的时候,这样可以把拦截逻辑分开设计

public class WorkersManager
{public static IMessage DoWork(Attribute[] attributes,IMessageSink messageSink,IMessage message){//循环遍历所有属性,如果是WorkerAttribute的子类,则执行DoWorkBefore和DoWorkAfter方法foreach (var item in attributes){if (item.GetType().IsSubclassOf(typeof(WorkerAttribute))){(item as WorkerAttribute).DoWorkBefore();}}var ret = messageSink.SyncProcessMessage(message);foreach (var item in attributes){if (item.GetType().IsSubclassOf(typeof(WorkerAttribute))){(item as WorkerAttribute).DoWorkAfter();}}return ret;}
}

InterceptorAttribute.cs:定义拦截器属性,标记了该特性的类
在执行时拦截
同时抽象出代理逻辑接口,便于后面简化拦截器的实现

using System.Runtime.Remoting.Contexts;
using System.Runtime.Remoting.Messaging;[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class InterceptorAttribute : ContextAttribute, IContributeObjectSink
{public InterceptorAttribute(): base("Interceptor") { }//实现IContributeObjectSink接口当中的消息接收器接口public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink next){return new AopHandler(next);}
}public abstract class WorkerAttribute : Attribute
{public abstract IDualWorkerService GetWorker();public abstract void DoWorkBefore();public abstract void DoWorkAfter();
}public interface IDualWorkerService : IBeforeWorkerService, IAfterWorkerService { }public interface IBeforeWorkerService
{void Before(object obj);
}public interface IAfterWorkerService
{void After(object obj);
}

创建日志拦截器,记录方法进入和退出日志,可以创建多个,只需要继承IDualWorkerService接口,WorkersManager中会遍历执行每一个拦截器

public class LoggingWorker : IDualWorkerService
{public void Before(object obj){Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")} {obj as string} Before");}public void After(object obj){Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")} {obj as string} After");}
}

使用案例

Console.WriteLine("Hello, World!");Data data = new Data();
data.DoWork();Console.ReadKey();[Interceptor]
class Data : ContextBoundObject
{[LoggingInterceptor("点击了按钮。。。。")]public void DoWork(){Console.WriteLine("正在处理。。。。。");throw new Exception("出错了!");}
}

http://www.ppmy.cn/ops/130456.html

相关文章

动态ip如何自动更换ip

在探讨如何自动更换动态IP地址时,我们首先需要理解动态IP的基本概念。IP地址,即互联网协议地址,分配给每台连接到互联网的设备的唯一标识符。与传统静态IP地址不同,动态IP地址是由网络服务提供商(ISP)动态分…

linux命令行的艺术

文章目录 前言基础日常使用文件及数据处理系统调试单行脚本冷门但有用仅限 OS X 系统仅限 Windows 系统在 Windows 下获取 Unix 工具实用 Windows 命令行工具Cygwin 技巧 更多资源免责声明 熟练使用命令行是一种常常被忽视,或被认为难以掌握的技能,但实际…

【Unity实战笔记】第二十二 · 基于SMB的角色控制中遇到的一些问题(斜坡移动鬼畜、落地卡顿、角色突进、头发动画失效等)

【Unity实战笔记】第二一 基于状态模式的角色控制——以UnityChan为例【Unity学习笔记】第十一 动画基础(Animation、状态机、root motion、bake into pose、blendTree、大量案例) 注: 本文紧接上一篇 Unity实战笔记 第二一,补…

硅谷甄选(三)登录注册

今天跑了步很舒服 一.登录模块 1.1登录路由静态组件 src\views\login\index.vue <template><div class"login_container"><el-row><el-col :span"12" :xs"0"></el-col><el-col :span"12" :xs&quo…

【学习】ZLMediaKit试用

服务端准备 下载ZLMediaKit压缩包&#xff0c;解压 /linux/Release路径下启用MediaServer ./MediaServer -d &/linux/Release路径下config.ini更改配置 也可以将进入web控制台 rtmp默认端口1935, rtsp默认端口554,http默认端口80, SSL默认端口443 进入web控制台 http…

js中什么是闭包,它和柯里化函数有什么关系

在JavaScript中&#xff0c;闭包是一个非常重要的概念&#xff0c;它指的是一个函数和它声明时所处的词法环境的组合。这意味着该函数可以访问并操作它被创建时作用域中的变量&#xff0c;即使它在那个作用域之外被调用。 闭包的定义和特点 访问外部变量&#xff1a;闭包允许…

tornado,flaskd这两个框架主要是干什么的

Tornado是一个Python的Web框架&#xff0c;主要用于构建高性能的异步Web应用程序。它基于非阻塞的网络I/O模型&#xff0c;可以处理大量并发连接&#xff0c;适用于需要处理实时性要求较高的应用场景&#xff0c;如实时聊天、实时数据推送等。 Flask是另一个Python的Web框架&a…

git入门教程12:git命令与技巧

一、Git高级命令 Git Rebase 功能&#xff1a;清理提交历史记录&#xff0c;使其更清晰和线性。在多人合作中&#xff0c;可以使用rebase合并功能分支的更改到主分支。交互式Rebase&#xff1a;使用git rebase -i HEAD~n&#xff08;n为你想重新排序、编辑或合并的提交数量&…