Unity容器实现AOP面向切面编程
为什么要有AOP
需求总是变化的,比如经常会对一些方法后期增加日志、异常处理、权限、缓存、事务的处理,遇到这种情况我们往往只能修改类。
为了应对变化,我们常常使用设计模式解决,但是也有其局限性:设计模式这能替换整个对象,但是没办法把一个类动态改变。所以我们需要引入AOP的编程思想,因为它允许开发者动态的修改静态的OO模型,构造出一个不断增长,不断变化的需求。
AOP是一种编程思想,是对OOP面向对象编程思想的补充。
使用AOP编程可以方便我们聚焦一些核心业务逻辑,比如权限、异常、日志、缓存、事务这种通用功能可以封装起来,通过AOP添加到指定的方法,简化程序设计。
如何使用AOP
1、添加引用
2、配置文件
在configuration节点下添加(注意看注释)
<configSections>
<!--这里添加一个unity扩展--> <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="oneBehaviorTestContainer"> <extension type="Interception" /> <!--设置接口的实现类--> <register type="IServers.IUser,IServers" mapTo="Providers.UserProvider,Providers"> <!--InterfaceInterceptor:继承接口的方法都会被拦截。 TransparentProxyInterceptor:继承类使用的方法都会被拦截。 VirtualMethodInterceptor:继承的方法必须是虚方法且必须是公开的方法才会被拦截。--> <interceptor type="InterfaceInterceptor"/> <!--配置文件的注册顺序是调用顺序,然后才是业务方法,但是扩展逻辑也可以在业务方法之后--> <!--应该把捕捉异常的拦截器放到第一位,这样还可以捕捉其他拦截器内的异常--> <interceptionBehavior type="AOPExe.Interceptions.ExceptionBehavior, AOPExe"/> <!--应该把性能计算的拦截器放到第二位,这样还可以捕捉其他拦截器内的异常--> <interceptionBehavior type="AOPExe.Interceptions.MonitorBehavior, AOPExe"/> <!--参数检查--> <interceptionBehavior type="AOPExe.Interceptions.ParameterCheckBehavior, AOPExe"/> <!--缓存--> <interceptionBehavior type="AOPExe.Interceptions.CachingBehavior, AOPExe"/> </register> </container> </containers> </unity>
3、程序调用
3.1程序调用
//声明一个Unity容器 var container = new UnityContainer(); //获取到Unity部分 UnityConfigurationSection unitySection = (UnityConfigurationSection)ConfigurationManager.GetSection("unity"); //将扩展部分注册到容器 unitySection.Configure(container, "oneBehaviorTestContainer"); //获取接口实例 var userService = container.Resolve<IUser>(); //调用接口方法(该方法被添加了拦截器) var user = userService.GetUser(100); if (user!=null) { Console.WriteLine(user.Name); }
3.2 拦截器
拦截器的类要实现:IInterceptionBehavior接口
/// <summary> /// 缓存(用于方法前) /// </summary> public class CachingBehavior : IInterceptionBehavior { public bool WillExecute => true; public IEnumerable<Type> GetRequiredInterfaces() { return Type.EmptyTypes; } public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) { return input.CreateMethodReturn(new UserModel() { Id = 1, Name = "缓存姓名" }); } }
/// <summary>
/// 异常处理
/// </summary>
public class ExceptionBehavior : IInterceptionBehavior { public bool WillExecute => true; public IEnumerable<Type> GetRequiredInterfaces() { return Type.EmptyTypes; } public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) { IMethodReturn methodReturn = getNext()(input, getNext); if (methodReturn.Exception != null) { //处理异常 Console.WriteLine("ExceptionBehavior捕捉到异常:" + methodReturn.Exception.Message); //隐藏异常 methodReturn.Exception = null; } else { Console.WriteLine("ExceptionBehavior没有捕捉到异常。"); } return methodReturn; } }
/// <summary> /// 性能检测 /// </summary> public class MonitorBehavior : IInterceptionBehavior { public bool WillExecute => true; public IEnumerable<Type> GetRequiredInterfaces() { return Type.EmptyTypes; } public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) { Stopwatch sw = new Stopwatch(); sw.Start(); var returnMethod = getNext()(input, getNext); sw.Stop(); Console.WriteLine("MonitorBehavior 本次方法共耗时:" + sw.ElapsedMilliseconds); return returnMethod; } }
/// <summary> /// 参数检查 /// </summary> public class ParameterCheckBehavior : IInterceptionBehavior { public bool WillExecute => true; public IEnumerable<Type> GetRequiredInterfaces() { return Type.EmptyTypes; } public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) { Console.WriteLine("ParameterCheckBehavior,打印所有传入参数:"); foreach (var ipt in input.Inputs) { Console.WriteLine(ipt); } int id = (int)input.Inputs[0]; if (id > 100) { //这种写法不对 //new Exception("Id不能超过100"); return input.CreateExceptionMethodReturn(new Exception("Id不能超过100")); } else { Console.WriteLine("参数检查通过"); } return getNext()(input, getNext); } }
3.3业务方法
public interface IUser { UserModel GetUser(int Id); } public UserModel GetUser(int Id) { Console.WriteLine("数据库中读取UserModel,可能时间会比较长一点点(对比缓存)"); //throw new Exception("业务方法中抛出异常");//这里抛出的异常,也可以捕获到 Thread.Sleep(10000); return new UserModel() { Id = Id , Name = "张三" }; }
4、如果不想对接口内的所有方法都添加拦截该怎么办?
我能想到的办法是为接口方法添加特性,然后再拦截器内判断该方法是否含有该特性。小伙伴们有啥想法欢迎留言。