C# AOP学习笔记
一、aop概念
官方解释:aop(aspect-oriented programming,面向切面编程),它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。它是一种新的方法论,是对传统oop编程的一种补充。oop是关注将需求功能划分为不同的并且相对独立、封装良好的类,并让它们有着属于自己的行为,依靠继承和多态等来定义彼此的关系;aop是希望能够将通用需求功能从不相关的类当中分离出来,能够使得很多类共享一个行为,一旦发生变化,不必修改很多类,而只需要修改这个行为即可。aop是使用切面(aspect)将横切关注点模块化,oop是使用类将状态和行为模块化。在oop的世界中,程序都是通过类和接口组织的,使用它们实现程序的核心业务逻辑是十分合适,但是对于实现横切关注点(跨越应用程序多个模块的功能需求)则十分吃力,比如日志记录、权限验证、异常拦截等。
个人理解:aop就是将公用功能提取出来,如果以后公用功能的需求发生变化,只需要改动公用模块的代码即可,多个调用的地方则不需要改动。所谓面向切面,就是只关注通用功能,而不关注业务逻辑。它实现的方式一般是通过拦截,比如,项目中一般都有权限验证的功能,进入每个页面前都会验证当前登录用户是否有权限查看该界面。我们不可能说在每个页面的初始化方法里面都去写这段验证的代码,这个时候我们的aop就派上用场了。aop的机制是预先定义一组特性,使它具有拦截方法的功能,可以让你在执行方法之前和之后做你想做的业务,而我们使用的时候只需要在对应的方法或者类定义上面加上某一个特性就好了。
二、aop优势
1)将通用功能从业务逻辑中抽离出来,可以省略大量的重复代码,有利于代码的操作和维护。
2)在软件设计时,抽出通用功能(切面),有利于软件设计的模块化,降低软件架构的复杂度。也就是说通用的功能都是一个个单独的模块,在项目的主业务里面是看不到这些通用功能的设计代码的。
三、aop应用
3.1、静态代理方式
3.1.1、使用装饰器模式实现静态代理
1)新建一个类:decoratoraop.cs
/// <summary> /// 使用装饰器模式实现静态代理 /// </summary> public class decoratoraop { /// <summary> /// 用户类 /// </summary> public class user { public int id { get; set; } public string name { get; set; } public string password { get; set; } } /// <summary> /// 用户注册接口 /// </summary> public interface iuserprocessor { void reguser(user user); } /// <summary> /// 用户注册接口实现类 /// </summary> public class userprocessor : iuserprocessor { public void reguser(user user) { console.writeline($"用户注册成功。name:{user.name} password:{user.password}"); } } /// <summary> /// 装饰器模式实现aop功能 /// </summary> public class userprocessordecorator : iuserprocessor { private iuserprocessor userprocessor { get; set; } public userprocessordecorator(iuserprocessor userprocessor) { userprocessor = userprocessor; } public void reguser(user user) { preproceed(user); userprocessor.reguser(user); postproceed(user); } public void preproceed(user user) { console.writeline("方法执行前"); } public void postproceed(user user) { console.writeline("方法执行后"); } } /// <summary> /// 运行测试 /// </summary> public static void show() { user user = new user() { name = "hello", password = "world" }; iuserprocessor processor = new userprocessordecorator(new userprocessor()); processor.reguser(user); } }
2)调用:
static void main(string[] args) { #region 使用装饰器模式实现静态代理 decoratoraop.show(); console.read(); #endregion }
3)运行结果如下:
上面代码是模拟用户注册的例子:注册信息提交前,需要做一些准备工作,比如数据有效性校验等;注册信息提交后,还需要做日志记录等。从上面的代码可以看出,我们通过静态植入的方式,手动地在执行方法前和执行方法后让它做一些我们需要的功能。
3.1.2、使用代理模式实现静态代理
1)新建一个类:proxyaop.cs
/// <summary> /// 使用代理模式实现静态代理 /// </summary> public class proxyaop { /// <summary> /// 用户类 /// </summary> public class user { public int id { get; set; } public string name { get; set; } public string password { get; set; } } /// <summary> /// 用户注册接口 /// </summary> public interface iuserprocessor { void reguser(user user); } /// <summary> /// 用户注册接口实现类 /// </summary> public class userprocessor : iuserprocessor { public void reguser(user user) { console.writeline($"用户注册成功。name:{user.name} password:{user.password}"); } } /// <summary> /// 代理模式实现aop功能 /// </summary> public class userprocessorproxy : iuserprocessor { private iuserprocessor userprocessor = new userprocessor(); public void reguser(user user) { preproceed(user); userprocessor.reguser(user); postproceed(user); } private void preproceed(user user) { console.writeline("方法执行前"); } private void postproceed(user user) { console.writeline("方法执行后"); } } public static void show() { user user = new user() { name = "hello", password = "world" }; iuserprocessor processor = new userprocessorproxy(); processor.reguser(user); } }
2)调用:
static void main(string[] args) { #region 使用代理模式实现静态代理 proxyaop.show(); console.read(); #endregion }
3)运行结果如下:
3.2、动态代理方式
3.2.1、使用.net remoting/realproxy实现动态代理
1)新建一个类:realproxyaop.cs
/// <summary> /// 使用.net remoting/realproxy实现动态代理 /// client - transparentproxy - realproxy - target object /// 局限在业务类必须是继承自marshalbyrefobject类型 /// </summary> public class realproxyaop { /// <summary> /// 用户类 /// </summary> public class user { public int id { get; set; } public string name { get; set; } public string password { get; set; } } /// <summary> /// 用户注册接口 /// </summary> public interface iuserprocessor { void reguser(user user); } /// <summary> /// 用户注册接口实现类 /// 必须继承自marshalbyrefobject父类,否则无法生成。 /// </summary> public class userprocessor : marshalbyrefobject, iuserprocessor { public void reguser(user user) { console.writeline($"用户注册成功。name:{user.name} password:{user.password}"); } } /// <summary> /// 真实代理:提供代理的基本功能 /// </summary> public class myrealproxy<t> : realproxy { private t _target; public myrealproxy(t target) : base(typeof(t)) { _target = target; } public override imessage invoke(imessage msg) { preproceed(msg); imethodcallmessage callmessage = (imethodcallmessage)msg; object returnvalue = callmessage.methodbase.invoke(_target, callmessage.args); postproceed(msg); return new returnmessage(returnvalue, new object[0], 0, null, callmessage); } public void preproceed(imessage msg) { console.writeline("方法执行前"); } public void postproceed(imessage msg) { console.writeline("方法执行后"); } } /// <summary> /// 透明代理:提供实际对象驻留在客户端空间中的假象 /// </summary> public static class transparentproxy { public static t create<t>() { t instance = activator.createinstance<t>(); myrealproxy<t> realproxy = new myrealproxy<t>(instance); t transparentproxy = (t)realproxy.gettransparentproxy(); return transparentproxy; } } /// <summary> /// 运行测试 /// </summary> public static void show() { user user = new user() { name = "hello", password = "world" }; userprocessor processor = transparentproxy.create<userprocessor>(); processor.reguser(user); } }
2)调用:
static void main(string[] args) { #region 使用.net remoting/realproxy实现动态代理 realproxyaop.show(); console.read(); #endregion }
3)运行结果如下:
3.2.2、使用castle\dynamicproxy实现动态代理
1)在nuget中安装castle.core。
2)新建一个类:castleproxyaop.cs
/// <summary> /// 使用castle\dynamicproxy实现动态代理 /// 方法必须是虚方法 /// </summary> public class castleproxyaop { /// <summary> /// 用户类 /// </summary> public class user { public int id { get; set; } public string name { get; set; } public string password { get; set; } } /// <summary> /// 用户注册接口 /// </summary> public interface iuserprocessor { void reguser(user user); } /// <summary> /// 用户注册接口实现类 /// </summary> public class userprocessor : iuserprocessor { /// <summary> /// 必须带上virtual,否则无效。 /// </summary> /// <param name="user"></param> public virtual void reguser(user user) { console.writeline($"用户注册成功。name:{user.name} password:{user.password}"); } } /// <summary> /// 拦截器 /// </summary> public class myinterceptor : iinterceptor { public void intercept(iinvocation invocation) { preproceed(invocation); invocation.proceed(); postproceed(invocation); } public void preproceed(iinvocation invocation) { console.writeline("方法执行前"); } public void postproceed(iinvocation invocation) { console.writeline("方法执行后"); } } /// <summary> /// 运行测试 /// </summary> public static void show() { user user = new user() { name = "hello", password = "world" }; proxygenerator generator = new proxygenerator(); myinterceptor interceptor = new myinterceptor(); userprocessor userprocessor = generator.createclassproxy<userprocessor>(interceptor); userprocessor.reguser(user); } }
3)调用:
static void main(string[] args) { #region 使用castle\dynamicproxy实现动态代理 castleproxyaop.show(); console.read(); #endregion }
4)运行结果如下:
3.2.3、使用entlib\piab unity实现aop(非配置)
1)在nuget中安装unity及unity.interception。
2)新建一个类:unityaop.cs
/// <summary> /// 使用entlib\piab unity实现动态代理(非配置) /// </summary> public class unityaop { #region 业务 /// <summary> /// 用户类 /// </summary> public class user { public int id { get; set; } public string name { get; set; } public string password { get; set; } } /// <summary> /// 用户注册接口 /// </summary> [exceptionhandler(order = 1)] [loghandler(order = 2)] [userhandler(order = 3)] [afterloghandler(order = 5)] public interface iuserprocessor { void reguser(user user); } /// <summary> /// 用户注册接口实现类 /// </summary> public class userprocessor : iuserprocessor //可以不继承marshalbyrefobject类 { public void reguser(user user) { console.writeline($"用户注册成功。name:{user.name} password:{user.password}"); } } #endregion 业务 #region 特性 /// <summary> /// 异常处理特性 /// </summary> public class exceptionhandlerattribute : handlerattribute { public override icallhandler createhandler(iunitycontainer container) { return new exceptionhandler() { order = order }; } } /// <summary> /// 日志处理特性 /// </summary> public class loghandlerattribute : handlerattribute { public override icallhandler createhandler(iunitycontainer container) { return new loghandler() { order = order }; } } /// <summary> /// 用户信息特性 /// </summary> public class userhandlerattribute : handlerattribute { public override icallhandler createhandler(iunitycontainer container) { icallhandler handler = new userhandler() { order = order }; return handler; } } /// <summary> /// 后续日志特性 /// </summary> public class afterloghandlerattribute : handlerattribute { public override icallhandler createhandler(iunitycontainer container) { return new afterloghandler() { order = order }; } } #endregion 特性 #region 特性对应的行为 public class exceptionhandler : icallhandler { public int order { get; set; } public imethodreturn invoke(imethodinvocation input, getnexthandlerdelegate getnext) { imethodreturn methodreturn = getnext()(input, getnext); if (methodreturn.exception == null) { console.writeline("exceptionhandler:没有异常"); } else { console.writeline($"exceptionhandler:出现异常:{methodreturn.exception.message}"); } return methodreturn; } } public class loghandler : icallhandler { public int order { get; set; } public imethodreturn invoke(imethodinvocation input, getnexthandlerdelegate getnext) { user user = input.inputs[0] as user; string message = string.format($"name:{user.name} password:{user.password}"); console.writeline($"loghandler:日志已记录。message:{message}"); imethodreturn methodreturn = getnext()(input, getnext); return methodreturn; } } public class userhandler : icallhandler { public int order { get; set; } public imethodreturn invoke(imethodinvocation input, getnexthandlerdelegate getnext) { user user = input.inputs[0] as user; if (user.password.length < 10) { return input.createexceptionmethodreturn(new exception("userhandler:密码长度不能小于10位")); } //getnext()(input, getnext):委托后的委托,即多重委托。 imethodreturn methodreturn = getnext()(input, getnext); return methodreturn; } } public class afterloghandler : icallhandler { public int order { get; set; } public imethodreturn invoke(imethodinvocation input, getnexthandlerdelegate getnext) { imethodreturn methodreturn = getnext()(input, getnext); console.writeline($"afterloghandler:方法执行结果--{methodreturn.returnvalue}"); console.writeline("afterloghandler:方法执行后"); return methodreturn; } } #endregion 特性对应的行为 /// <summary> /// 运行测试 /// </summary> public static void show() { user user = new user() { name = "hello", password = "helloworld" }; iunitycontainer container = new unitycontainer(); //声明一个容器 container.addnewextension<interception>() .registertype<iuserprocessor, userprocessor>(new interceptor<transparentproxyinterceptor>(), new interceptionbehavior<policyinjectionbehavior>()); //显式拦截 iuserprocessor processor = container.resolve<iuserprocessor>(); processor.reguser(user); //调用 } }
3)调用:
static void main(string[] args) { #region 使用entlib\piab unity实现动态代理(非配置) unityaop.show(); console.read(); #endregion }
4)运行结果如下:
3.2.4、使用entlib\piab unity实现aop(带配置)
1)继续在nuget中安装unity.configuration、unity.interception.configuration及newtonsoft.json。
2)分别建立以下类:
/// <summary> /// 用户类 /// </summary> public class user { public int id { get; set; } public string name { get; set; } public string password { get; set; } }
/// <summary> /// 用户注册接口 /// </summary> public interface iuserprocessor { void reguser(user user); }
/// <summary> /// 用户注册接口实现类 /// </summary> public class userprocessor : iuserprocessor { public void reguser(user user) { console.writeline($"用户注册成功。name:{user.name} password:{user.password}"); } }
/// <summary> /// 使用entlib\piab unity实现动态代理(带配置) /// </summary> public class unityconfigaop { public static void show() { user user = new user() { name = "hello", password = "helloworld" }; //配置unitycontainer iunitycontainer container = new unitycontainer(); execonfigurationfilemap filemap = new execonfigurationfilemap { execonfigfilename = path.combine(appdomain.currentdomain.basedirectory + @"unityconfigaop\unity.config") }; configuration configuration = configurationmanager.openmappedexeconfiguration(filemap, configurationuserlevel.none); unityconfigurationsection configsection = (unityconfigurationsection)configuration.getsection(unityconfigurationsection.sectionname); configsection.configure(container, "aopcontainer"); iuserprocessor processor = container.resolve<iuserprocessor>(); processor.reguser(user); } }
/// <summary> /// 不需要特性 /// </summary> public class exceptionbehavior : iinterceptionbehavior { public bool willexecute { get { return true; } } public ienumerable<type> getrequiredinterfaces() { return type.emptytypes; } public imethodreturn invoke(imethodinvocation input, getnextinterceptionbehaviordelegate getnext) { imethodreturn methodreturn = getnext()(input, getnext); console.writeline("exceptionbehavior"); if (methodreturn.exception == null) { console.writeline("无异常"); } else { console.writeline($"异常:{methodreturn.exception.message}"); } return methodreturn; } }
/// <summary> /// 不需要特性 /// </summary> public class cachingbehavior : iinterceptionbehavior { private static dictionary<string, object> cachingbehaviordictionary = new dictionary<string, object>(); public bool willexecute { get { return true; } } public ienumerable<type> getrequiredinterfaces() { return type.emptytypes; } public imethodreturn invoke(imethodinvocation input, getnextinterceptionbehaviordelegate getnext) { console.writeline("cachingbehavior"); string key = $"{input.methodbase.name}_{newtonsoft.json.jsonconvert.serializeobject(input.inputs)}"; if (cachingbehaviordictionary.containskey(key)) { return input.createmethodreturn(cachingbehaviordictionary[key]); //断路器,直接返回。 } else { imethodreturn result = getnext().invoke(input, getnext); if (result.returnvalue != null) cachingbehaviordictionary.add(key, result.returnvalue); return result; } } }
/// <summary> /// 不需要特性 /// </summary> public class permissionbehavior : iinterceptionbehavior { public bool willexecute { get { return true; } } public ienumerable<type> getrequiredinterfaces() { return type.emptytypes; } public imethodreturn invoke(imethodinvocation input, getnextinterceptionbehaviordelegate getnext) { console.writeline("permissionbehavior"); console.writeline(input.methodbase.name); foreach (var item in input.inputs) { console.writeline(newtonsoft.json.jsonconvert.serializeobject(item)); //反射&序列化获取更多信息 } return getnext().invoke(input, getnext); } }
/// <summary> /// 不需要特性 /// </summary> public class parametercheckbehavior : iinterceptionbehavior { public bool willexecute { get { return true; } } public ienumerable<type> getrequiredinterfaces() { return type.emptytypes; } public imethodreturn invoke(imethodinvocation input, getnextinterceptionbehaviordelegate getnext) { user user = input.inputs[0] as user; //可以不写死类型,反射+特性完成数据有效性监测。 console.writeline("parametercheckbehavior"); if (user.password.length < 10) //可以过滤一下敏感词 { return input.createexceptionmethodreturn(new exception("密码长度不能小于10位")); } else { return getnext().invoke(input, getnext); } } }
/// <summary> /// 不需要特性 /// </summary> public class logbehavior : iinterceptionbehavior { public bool willexecute { get { return true; } } public ienumerable<type> getrequiredinterfaces() { return type.emptytypes; } public imethodreturn invoke(imethodinvocation input, getnextinterceptionbehaviordelegate getnext) { imethodreturn methodreturn = getnext()(input, getnext); //执行后面的全部动作 console.writeline("logbehavior"); console.writeline(input.methodbase.name); foreach (var item in input.inputs) { console.writeline(newtonsoft.json.jsonconvert.serializeobject(item)); //反射&序列化获取更多信息 } return methodreturn; } }
3)新建一个配置文件unity.config(本例代码是在unityconfigaop文件夹下),在其属性的复制到输出目录项下选择始终复制。
<configuration> <configsections> <section name="unity" type="microsoft.practices.unity.configuration.unityconfigurationsection, unity.configuration"/> </configsections> <unity> <sectionextension type="microsoft.practices.unity.interceptionextension.configuration.interceptionconfigurationextension, unity.interception.configuration"/> <containers> <container name="aopcontainer"> <extension type="interception"/> <!--注册匹配规则:前面是完整类型名称,后面是所在的dll名称。--> <register type="linkto.test.consoleaop.unityconfigaop.iuserprocessor,linkto.test.consoleaop" mapto="linkto.test.consoleaop.unityconfigaop.userprocessor,linkto.test.consoleaop"> <interceptor type="interfaceinterceptor"/> <!--拦截顺序为由上而下;配置会全部执行,除非遇到断路器等;建议异常处理包在最外层,即在最上面。--> <interceptionbehavior type="linkto.test.consoleaop.unityconfigaop.exceptionbehavior, linkto.test.consoleaop"/> <interceptionbehavior type="linkto.test.consoleaop.unityconfigaop.cachingbehavior, linkto.test.consoleaop"/> <interceptionbehavior type="linkto.test.consoleaop.unityconfigaop.permissionbehavior, linkto.test.consoleaop"/> <interceptionbehavior type="linkto.test.consoleaop.unityconfigaop.parametercheckbehavior, linkto.test.consoleaop"/> <interceptionbehavior type="linkto.test.consoleaop.unityconfigaop.logbehavior, linkto.test.consoleaop"/> </register> </container> </containers> </unity> </configuration>
4)调用:
static void main(string[] args) { #region 使用entlib\piab unity实现动态代理(带配置) unityconfigaop.unityconfigaop.show(); console.read(); #endregion }
5)运行结果如下:
3.3、il编织方式
il编织方式,可以使用postsharp框架来做,但是由于postsharp从2.0版本开始收费,此处不再作说明,有兴趣的话可以百度一下。
参考自:
e神公开课代码