前面我们介绍了Unity的依赖注入功能,现在来介绍下Unity的AOP功能。AOP是面向切面编程,它能够使我们在不改变现有代码结构的情况下额外的为其添加一些功能。
我们还是使用配置文件来对类型进行注入
<configuration> <configSections> <!--<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>--> <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/> </configSections> <unity> <!--<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration"/>--> <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Unity.Interception.Configuration"/> <containers> <container name="IocContainer"> <!-- 格式:类名,程序集名称 --> <register type="IocDemo.IService.IPay, IocDemo.IService" mapTo="IocDemo.Service.WeChatPay, IocDemo.Service"/> <register type="IocDemo.IService.IOrder,IocDemo.IService" mapTo="IocDemo.Service.Order,IocDemo.Service"> <lifetime type="transient" /> <constructor> <!--<param name="id" type="System.Int32" value="3" />--> <!--<param name="iPay" type="IocDemo.IService.IPay,IocDemo.IService"></param>--> </constructor> </register> </container> </containers> </unity> </configuration>
接着先来看一下代码方式怎么实现AOP功能。
namespace IocDemo.Aop { #region 特性对应的行为 public class UserCallHandler : ICallHandler { public int Order { get; set; } public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) { Console.WriteLine("参数检测无误"); IMethodReturn methodReturn = getNext()(input, getNext); //getNext.Invoke().Invoke(input, getNext); return methodReturn; } } public class LogCallHandler : ICallHandler { public int Order { get; set; } public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) { Console.WriteLine("日志已记录,Message:{0},CreateTime:{1}", "测试LogHandler", DateTime.Now); return getNext()(input, getNext); } } public class ExceptionCallHandler : ICallHandler { public int Order { get; set; } public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) { IMethodReturn methodReturn = getNext()(input, getNext); if (methodReturn.Exception == null) { Console.WriteLine("无异常"); } else { Console.WriteLine("异常:{0}", methodReturn.Exception.Message); } return methodReturn; } } public class AfterLogCallHandler : ICallHandler { public int Order { get; set; } public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) { IMethodReturn methodReturn = getNext()(input, getNext); Console.WriteLine("完成日志,Message:{0},CreateTime:{1},计算结果{2}", "测试LogHandler", DateTime.Now, methodReturn.ReturnValue); return methodReturn; } } #endregion 特性对应的行为 #region 特性 public class UserHandlerAttribute : HandlerAttribute { public override ICallHandler CreateHandler(IUnityContainer container) { ICallHandler handler = new UserCallHandler() { Order = this.Order }; return handler; } } public class LogHandlerAttribute : HandlerAttribute { public override ICallHandler CreateHandler(IUnityContainer container) { return new LogCallHandler() { Order = this.Order }; } } public class ExceptionHandlerAttribute : HandlerAttribute { public override ICallHandler CreateHandler(IUnityContainer container) { return new ExceptionCallHandler() { Order = this.Order }; } } public class AfterLogHandlerAttribute : HandlerAttribute { public override ICallHandler CreateHandler(IUnityContainer container) { return new AfterLogCallHandler() { Order = this.Order }; } } #endregion 特性 }
namespace IocDemo.IService { [UserHandler(Order = 3)] [LogHandler(Order = 5)] [AfterLogHandler] [ExceptionHandler] public interface IOrder { string ToPay(); } }
在代码中使用 container.AddNewExtension 加入AOP
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap(); fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "Config\Unity.Config");//找配置文件的路径 Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); UnityConfigurationSection section = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName); IUnityContainer container = new UnityContainer(); section.Configure(container, "IocContainer"); container.AddNewExtension<Interception>().Configure<Interception>().SetInterceptorFor<IOrder>(new InterfaceInterceptor()); IOrder order = container.Resolve<IOrder>(); Console.WriteLine(order.ToPay()); Console.ReadLine();
运行结果:
可以看到,使用代码方式实现AOP要编写的代码很多,需要编写继承 Unity.Interception.PolicyInjection.Policies.HandlerAttribute的属性,重写CreateHandler方法,该方法需要返回一个ICallHandler对象,然后需要将继承HandlerAttribute属性的attribute加在需要拦截的接口上面,调用的时候就会命中ICallHandler里面的Invoke方法。
标记Attribute的方式有三种,一般使用第3种方式:
1、TransparentProxyInterceptor:直接在类的方法上进行标记,但是这个类必须继承MarshalByRefObject...不建议用
2、VirtualMethod:直接在类的方法上进行标记,但这个方法必须是虚方法(就是方法要带virtual关键字)
3、InterfaceInterceptor:在接口的方法上进行标记,这样继承这个接口的类里实现这个接口方法的方法就能被拦截
最后,让我们来看一下配置文件怎么配置AOP。
<container name="IocContainer"> <!-- 格式:类名,程序集名称 --> <register type="IocDemo.IService.IPay, IocDemo.IService" mapTo="IocDemo.Service.WeChatPay, IocDemo.Service"/> <register type="IocDemo.IService.IOrder,IocDemo.IService" mapTo="IocDemo.Service.Order,IocDemo.Service"> <interceptor type="InterfaceInterceptor"/> <interceptionBehavior type="IocDemo.Aop.LogBehavior, IocDemo.Aop"/> </register> </container>
定义一个实现IInterceptionBehavior的LogBehavior类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Unity.Interception.InterceptionBehaviors; using Unity.Interception.PolicyInjection.Pipeline; namespace IocDemo.Aop { public class LogBehavior : IInterceptionBehavior { public IEnumerable<Type> GetRequiredInterfaces() { return Type.EmptyTypes; } public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) { Console.WriteLine("LogBehavior before"); IMethodReturn method = getNext()(input, getNext); Console.WriteLine("LogBehavior after"); return method; } public bool WillExecute { get { return true; } } } }
代码中调用
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap(); fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "Config\unity.config");//找配置文件的路径 Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); UnityConfigurationSection section = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName); IUnityContainer container = new UnityContainer(); section.Configure(container, "IocContainer"); //被拦截 /* 实现Unity.Interception.InterceptionBehaviors.IInterceptionBehavior接口,进入Invoke方法 */ IOrder order = container.Resolve<IOrder>(); Console.WriteLine(order.ToPay()); Console.ReadLine();
运行结果可以看到,在调用ToPay之前进入了LogBehavior的Invoke方法
可见使用配置文件的方式实现AOP比代码方式实现简单很多。需要注意的是,再引用Unity的时候需要注意Unity和Unity.Interception版本的问题,笔者在这个版本问题上调试了很久才找到匹配的版本。