首页 > 代码库 > 【Unity】AOP编程--拦截,用于缓存和异常处理
【Unity】AOP编程--拦截,用于缓存和异常处理
第一步:定义拦截行为:CachingBehavior 和 ExceptionLoggingBehavior
他们都继承接口:IInterceptionBehavior (程序集 Microsoft.Practices.Unity.Interception.dll, v2.1.505.0
命名空间:Microsoft.Practices.Unity.InterceptionExtension)
需要实现连个接口:
public IEnumerable<Type> GetRequiredInterfaces()public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
CachingBehavior.cs
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using Keasy5.Infrastructure.Caching;using Microsoft.Practices.Unity.InterceptionExtension;namespace Keasy5.Infrastructure.InterceptionBehaviors{ /// <summary> /// 表示用于方法缓存功能的拦截行为。 /// </summary> public class CachingBehavior : IInterceptionBehavior { #region Private Methods /// <summary> /// 根据指定的<see cref="CachingAttribute"/>以及<see cref="IMethodInvocation"/>实例, /// 获取与某一特定参数值相关的键名。 /// </summary> /// <param name="cachingAttribute"><see cref="CachingAttribute"/>实例。</param> /// <param name="input"><see cref="IMethodInvocation"/>实例。</param> /// <returns>与某一特定参数值相关的键名。 /// <remarks> /// 例如:<see cref="ICacheProvider.Add"/> /// </remarks> /// </returns> private string GetValueKey(CachingAttribute cachingAttribute, IMethodInvocation input) { switch (cachingAttribute.Method) { // 如果是Remove,则不存在特定值键名,所有的以该方法名称相关的缓存都需要清除 case CachingMethod.Remove: return null; // 如果是Get或者Put,则需要产生一个针对特定参数值的键名 case CachingMethod.Get: case CachingMethod.Put: if (input.Arguments != null && input.Arguments.Count > 0) { var sb = new StringBuilder(); for (int i = 0; i < input.Arguments.Count; i++) { sb.Append(input.Arguments[i].ToString()); if (i != input.Arguments.Count - 1) sb.Append("_"); } return sb.ToString(); } else return "NULL"; default: throw new InvalidOperationException("无效的缓存方式。"); } } #endregion #region IInterceptionBehavior Members /// <summary> /// 获取当前行为需要拦截的对象类型接口。 /// </summary> /// <returns>所有需要拦截的对象类型接口。</returns> public IEnumerable<Type> GetRequiredInterfaces() { return Type.EmptyTypes; } /// <summary> /// 通过实现此方法来拦截调用并执行所需的拦截行为。 /// </summary> /// <param name="input">调用拦截目标时的输入信息。</param> /// <param name="getNext">通过行为链来获取下一个拦截行为的委托。</param> /// <returns>从拦截目标获得的返回信息。</returns> public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) { var method = input.MethodBase; var key = method.Name; if (method.IsDefined(typeof(CachingAttribute), false)) { var cachingAttribute = (CachingAttribute)method.GetCustomAttributes(typeof(CachingAttribute), false)[0]; var valKey = GetValueKey(cachingAttribute, input); switch (cachingAttribute.Method) { case CachingMethod.Get: try { if (CacheManager.Instance.Exists(key, valKey)) { var obj = CacheManager.Instance.Get(key, valKey); var arguments = new object[input.Arguments.Count]; input.Arguments.CopyTo(arguments, 0); return new VirtualMethodReturn(input, obj, arguments); } else { var methodReturn = getNext().Invoke(input, getNext); CacheManager.Instance.Add(key, valKey, methodReturn.ReturnValue); return methodReturn; } } catch (Exception ex) { return new VirtualMethodReturn(input, ex); } case CachingMethod.Put: try { var methodReturn = getNext().Invoke(input, getNext); if (CacheManager.Instance.Exists(key)) { if (cachingAttribute.Force) { CacheManager.Instance.Remove(key); CacheManager.Instance.Add(key, valKey, methodReturn.ReturnValue); } else CacheManager.Instance.Put(key, valKey, methodReturn.ReturnValue); } else CacheManager.Instance.Add(key, valKey, methodReturn.ReturnValue); return methodReturn; } catch (Exception ex) { return new VirtualMethodReturn(input, ex); } case CachingMethod.Remove: try { var removeKeys = cachingAttribute.CorrespondingMethodNames; foreach (var removeKey in removeKeys) { if (CacheManager.Instance.Exists(removeKey)) CacheManager.Instance.Remove(removeKey); } var methodReturn = getNext().Invoke(input, getNext); return methodReturn; } catch (Exception ex) { return new VirtualMethodReturn(input, ex); } default: break; } } return getNext().Invoke(input, getNext); } /// <summary> /// 获取一个<see cref="Boolean"/>值,该值表示当前拦截行为被调用时,是否真的需要执行 /// 某些操作。 /// </summary> public bool WillExecute { get { return true; } } #endregion }}
ExceptionLoggingBehavior.cs
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using Microsoft.Practices.Unity.InterceptionExtension;namespace Keasy5.Infrastructure.InterceptionBehaviors{ /// <summary> /// 表示用于异常日志记录的拦截行为。 /// </summary> public class ExceptionLoggingBehavior : IInterceptionBehavior { #region IInterceptionBehavior Members /// <summary> /// 获取当前行为需要拦截的对象类型接口。 /// </summary> /// <returns>所有需要拦截的对象类型接口。</returns> public IEnumerable<Type> GetRequiredInterfaces() { return Type.EmptyTypes; } /// <summary> /// 通过实现此方法来拦截调用并执行所需的拦截行为。 /// </summary> /// <param name="input">调用拦截目标时的输入信息。</param> /// <param name="getNext">通过行为链来获取下一个拦截行为的委托。</param> /// <returns>从拦截目标获得的返回信息。</returns> public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) { var methodReturn = getNext().Invoke(input, getNext); if (methodReturn.Exception != null) { Utils.Log(methodReturn.Exception); } return methodReturn; } /// <summary> /// 获取一个<see cref="Boolean"/>值,该值表示当前拦截行为被调用时,是否真的需要执行 /// 某些操作。 /// </summary> public bool WillExecute { get { return true; } } #endregion }}
第二步:添加配置文件,为需要被拦截的类添加拦截行为。
例如如下的配置,是为实现了接口
Keasy5.ServiceContract.IProductService
的类的所以方法添加拦截行为。
web/app.config文件:
<!--BEGIN: Unity--> <unity xmlns="http://schemas.microsoft.com/practices/2010/unity"> <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration" /> <container> <extension type="Interception" />。。。。。 <register type="Keasy5.ServiceContract.IProductService, Keasy5.ServiceContract" mapTo="Keasy5.Application.Implementation.ProductServiceImpl, Keasy5.Application"> <interceptor type="InterfaceInterceptor" /> <interceptionBehavior type="Keasy5.Infrastructure.InterceptionBehaviors.CachingBehavior, Keasy5.Infrastructure" /> <interceptionBehavior type="Keasy5.Infrastructure.InterceptionBehaviors.ExceptionLoggingBehavior, Keasy5.Infrastructure" /> </register>。。。。
比如:对于ExceptionLoggingBehavior的拦截行为作用于IProductService接口的实现类的所有方法:
/// <summary> /// 通过实现此方法来拦截调用并执行所需的拦截行为。 /// </summary> /// <param name="input">调用拦截目标时的输入信息。</param> /// <param name="getNext">通过行为链来获取下一个拦截行为的委托。</param> /// <returns>从拦截目标获得的返回信息。</returns> public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) { var methodReturn = getNext().Invoke(input, getNext); if (methodReturn.Exception != null) { Utils.Log(methodReturn.Exception); } return methodReturn; }
效果是:如果IProductService接口的实现类的所有方法如果抛出了异常,将调用
Utils.Log(methodReturn.Exception);
将异常信息写入日志系统(这就是我吗进行拦截的目的)。
------------------------------------------------
对应缓存CachingBehavior的拦截的补充:
第一:定义特性:CachingAttribute
CachingAttribute.cs
using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Keasy5.Infrastructure.Caching{ /// <summary> /// 表示由此特性所描述的方法,能够获得来自基础结构层所提供的缓存功能。 /// </summary> [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] public class CachingAttribute : Attribute { #region Ctor /// <summary> /// 初始化一个新的<c>CachingAttribute</c>类型。 /// </summary> /// <param name="method">缓存方式。</param> public CachingAttribute(CachingMethod method) { this.Method = method; } /// <summary> /// 初始化一个新的<c>CachingAttribute</c>类型。 /// </summary> /// <param name="method">缓存方式。</param> /// <param name="correspondingMethodNames"> /// 与当前缓存方式相关的方法名称。注:此参数仅在缓存方式为Remove时起作用。 /// </param> public CachingAttribute(CachingMethod method, params string[] correspondingMethodNames) : this(method) { this.CorrespondingMethodNames = correspondingMethodNames; } #endregion #region Public Properties /// <summary> /// 获取或设置缓存方式。 /// </summary> public CachingMethod Method { get; set; } /// <summary> /// 获取或设置一个<see cref="Boolean"/>值,该值表示当缓存方式为Put时,是否强制将值写入缓存中。 /// </summary> public bool Force { get; set; } /// <summary> /// 获取或设置与当前缓存方式相关的方法名称。注:此参数仅在缓存方式为Remove时起作用。 /// </summary> public string[] CorrespondingMethodNames { get; set; } #endregion }}
第二:将特性CachingAttribute应用于IProductService的接口方法:
using System;using System.Collections.Generic;using System.Linq;using System.ServiceModel;using System.Text;using System.Threading.Tasks;using ByteartRetail.DataObjects;using Keasy5.DataObject;using Keasy5.Infrastructure;using Keasy5.Infrastructure.Caching;namespace Keasy5.ServiceContract{ /// <summary> /// 表示与“商品”相关的应用层服务契约。 /// </summary> [ServiceContract(Namespace = "http://www.ByteartRetail.com")] public interface IProductService : IApplicationServiceContract { #region Methods /// <summary> /// 创建商品信息。 /// </summary> /// <param name="productDataObjects">需要创建的商品信息。</param> /// <returns>已创建的商品信息。</returns> [OperationContract] [FaultContract(typeof(FaultData))] [Caching(CachingMethod.Remove, "GetProductsForCategory", "GetProductsWithPagination", "GetFeaturedProducts", "GetProductsForCategoryWithPagination", "GetProducts", "GetProductByID")] ProductDataObjectList CreateProducts(ProductDataObjectList productDataObjects); /// <summary> /// 创建商品分类。 /// </summary> /// <param name="categoryDataObjects">需要创建的商品分类。</param> /// <returns>已创建的商品分类。</returns> [OperationContract] [FaultContract(typeof(FaultData))] [Caching(CachingMethod.Remove, "GetCategories")] CategoryDataObjectList CreateCategories(CategoryDataObjectList categoryDataObjects); /// <summary> /// 更新商品信息。 /// </summary> /// <param name="productDataObjects">需要更新的商品信息。</param> /// <returns>已更新的商品信息。</returns> [OperationContract] [FaultContract(typeof(FaultData))] [Caching(CachingMethod.Remove, "GetProductsForCategory", "GetProductsWithPagination", "GetFeaturedProducts", "GetProductsForCategoryWithPagination", "GetProducts", "GetProductByID")] ProductDataObjectList UpdateProducts(ProductDataObjectList productDataObjects); /// <summary> /// 更新商品分类。 /// </summary> /// <param name="categoryDataObjects">需要更新的商品分类。</param> /// <returns>已更新的商品分类。</returns> [OperationContract] [FaultContract(typeof(FaultData))] [Caching(CachingMethod.Remove, "GetCategories", "GetCategoryByID")] CategoryDataObjectList UpdateCategories(CategoryDataObjectList categoryDataObjects); /// <summary> /// 删除商品信息。 /// </summary> /// <param name="productIDs">需要删除的商品信息的ID值。</param> [OperationContract] [FaultContract(typeof(FaultData))] [Caching(CachingMethod.Remove, "GetProductsForCategory", "GetProductsWithPagination", "GetFeaturedProducts", "GetProductsForCategoryWithPagination", "GetProducts", "GetProductByID")] void DeleteProducts(IDList productIDs); /// <summary> /// 删除商品分类。 /// </summary> /// <param name="categoryIDs">需要删除的商品分类的ID值。</param> [OperationContract] [FaultContract(typeof(FaultData))] [Caching(CachingMethod.Remove, "GetCategories", "GetCategoryByID")] void DeleteCategories(IDList categoryIDs); /// <summary> /// 设置商品分类。 /// </summary> /// <param name="productID">需要进行分类的商品ID值。</param> /// <param name="categoryID">商品分类ID值。</param> /// <returns>带有商品分类信息的对象。</returns> [OperationContract] [FaultContract(typeof(FaultData))] [Caching(CachingMethod.Remove, "GetProductsForCategory", "GetProductsForCategoryWithPagination")] CategorizationDataObject CategorizeProduct(Guid productID, Guid categoryID); /// <summary> /// 取消商品分类。 /// </summary> /// <param name="productID">需要取消分类的商品ID值。</param> [OperationContract] [FaultContract(typeof(FaultData))] [Caching(CachingMethod.Remove, "GetProductsForCategory", "GetProductsForCategoryWithPagination")] void UncategorizeProduct(Guid productID); /// <summary> /// 根据指定的ID值获取商品分类。 /// </summary> /// <param name="id">商品分类ID值。</param> /// <param name="spec">查询方式。</param> /// <returns>商品分类。</returns> [OperationContract] [FaultContract(typeof(FaultData))] [Caching(CachingMethod.Get)] CategoryDataObject GetCategoryByID(Guid id, QuerySpec spec); /// <summary> /// 获取所有的商品分类。 /// </summary> /// <param name="spec">查询方式。</param> /// <returns>所有的商品分类。</returns> [OperationContract] [FaultContract(typeof(FaultData))] [Caching(CachingMethod.Get)] CategoryDataObjectList GetCategories(QuerySpec spec); /// <summary> /// 根据指定的ID值获取商品信息。 /// </summary> /// <param name="id">商品信息ID值。</param> /// <param name="spec">查询方式。</param> /// <returns>商品信息。</returns> [OperationContract] [FaultContract(typeof(FaultData))] [Caching(CachingMethod.Get)] ProductDataObject GetProductByID(Guid id, QuerySpec spec); /// <summary> /// 获取所有的商品信息。 /// </summary> /// <param name="spec">查询方式。</param> /// <returns>商品信息。</returns> [OperationContract] [FaultContract(typeof(FaultData))] [Caching(CachingMethod.Get)] ProductDataObjectList GetProducts(QuerySpec spec); /// <summary> /// 以分页的方式获取所有商品信息。 /// </summary> /// <param name="pagination">带有分页参数信息的对象。</param> /// <returns>经过分页的商品信息。</returns> [OperationContract] [FaultContract(typeof(FaultData))] [Caching(CachingMethod.Get)] ProductDataObjectListWithPagination GetProductsWithPagination(Pagination pagination); /// <summary> /// 根据指定的商品分类ID值,获取该分类下所有的商品信息。 /// </summary> /// <param name="categoryID">商品分类ID值。</param> /// <returns>所有的商品信息。</returns> [OperationContract] [FaultContract(typeof(FaultData))] [Caching(CachingMethod.Get)] ProductDataObjectList GetProductsForCategory(Guid categoryID); /// <summary> /// 根据指定的商品分类ID值,以分页的方式获取该分类下所有的商品信息。 /// </summary> /// <param name="categoryID">商品分类ID值。</param> /// <param name="pagination">带有分页参数信息的对象。</param> /// <returns>所有的商品信息。</returns> [OperationContract] [FaultContract(typeof(FaultData))] [Caching(CachingMethod.Get)] ProductDataObjectListWithPagination GetProductsForCategoryWithPagination(Guid categoryID, Pagination pagination); /// <summary> /// 获取所有的特色商品信息。 /// </summary> /// <param name="count">需要获取的特色商品信息的个数。</param> /// <returns>特色商品信息。</returns> [OperationContract] [FaultContract(typeof(FaultData))] [Caching(CachingMethod.Get)] ProductDataObjectList GetFeaturedProducts(int count); #endregion }}
第三:拦截IProductService接口的实现类的所有方法的所有方法,
并通过CachingAttribute特性进行方法 的筛选,
还进一步根据CachingAttribute的属性值进行一步的处理:
CachingBehavior .cs
/// <summary> /// 通过实现此方法来拦截调用并执行所需的拦截行为。 /// </summary> /// <param name="input">调用拦截目标时的输入信息。</param> /// <param name="getNext">通过行为链来获取下一个拦截行为的委托。</param> /// <returns>从拦截目标获得的返回信息。</returns> public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) { var method = input.MethodBase; var key = method.Name; if (method.IsDefined(typeof(CachingAttribute), false)) { var cachingAttribute = (CachingAttribute)method.GetCustomAttributes(typeof(CachingAttribute), false)[0]; var valKey = GetValueKey(cachingAttribute, input); switch (cachingAttribute.Method) { case CachingMethod.Get: try { if (CacheManager.Instance.Exists(key, valKey)) { var obj = CacheManager.Instance.Get(key, valKey); var arguments = new object[input.Arguments.Count]; input.Arguments.CopyTo(arguments, 0); return new VirtualMethodReturn(input, obj, arguments); } else { var methodReturn = getNext().Invoke(input, getNext); CacheManager.Instance.Add(key, valKey, methodReturn.ReturnValue); return methodReturn; } } catch (Exception ex) { return new VirtualMethodReturn(input, ex); } case CachingMethod.Put: try { var methodReturn = getNext().Invoke(input, getNext); if (CacheManager.Instance.Exists(key)) { if (cachingAttribute.Force) { CacheManager.Instance.Remove(key); CacheManager.Instance.Add(key, valKey, methodReturn.ReturnValue); } else CacheManager.Instance.Put(key, valKey, methodReturn.ReturnValue); } else CacheManager.Instance.Add(key, valKey, methodReturn.ReturnValue); return methodReturn; } catch (Exception ex) { return new VirtualMethodReturn(input, ex); } case CachingMethod.Remove: try { var removeKeys = cachingAttribute.CorrespondingMethodNames; foreach (var removeKey in removeKeys) { if (CacheManager.Instance.Exists(removeKey)) CacheManager.Instance.Remove(removeKey); } var methodReturn = getNext().Invoke(input, getNext); return methodReturn; } catch (Exception ex) { return new VirtualMethodReturn(input, ex); } default: break; } } return getNext().Invoke(input, getNext); }
--------------------------------
unity服务定位器:ServiceLocator
ServiceLocator.cs
using System;using System.Collections.Generic;using System.Configuration;using System.Linq;using System.Reflection;using System.Text;using Microsoft.Practices.Unity;using Microsoft.Practices.Unity.Configuration;namespace Keasy5.Infrastructure{ /// <summary> /// Represents the Service Locator. /// </summary> public sealed class ServiceLocator : IServiceProvider { #region Private Fields private readonly IUnityContainer container; #endregion #region Private Static Fields private static readonly ServiceLocator instance = new ServiceLocator(); #endregion #region Ctor /// <summary> /// Initializes a new instance of <c>ServiceLocator</c> class. /// </summary> private ServiceLocator() { UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity"); container = new UnityContainer(); section.Configure(container); } #endregion #region Public Static Properties /// <summary> /// Gets the singleton instance of the <c>ServiceLocator</c> class. /// </summary> public static ServiceLocator Instance { get { return instance; } } #endregion #region Private Methods private IEnumerable<ParameterOverride> GetParameterOverrides(object overridedArguments) { List<ParameterOverride> overrides = new List<ParameterOverride>(); Type argumentsType = overridedArguments.GetType(); argumentsType.GetProperties(BindingFlags.Public | BindingFlags.Instance) .ToList() .ForEach(property => { var propertyValue = http://www.mamicode.com/property.GetValue(overridedArguments, null); var propertyName = property.Name; overrides.Add(new ParameterOverride(propertyName, propertyValue)); }); return overrides; } #endregion #region Public Methods /// <summary> /// Gets the service instance with the given type. /// </summary> /// <typeparam name="T">The type of the service.</typeparam> /// <returns>The service instance.</returns> public T GetService<T>() { return container.Resolve<T>(); } public IEnumerable<T> ResolveAll<T>() { return container.ResolveAll<T>(); } /// <summary> /// Gets the service instance with the given type by using the overrided arguments. /// </summary> /// <typeparam name="T">The type of the service.</typeparam> /// <param name="overridedArguments">The overrided arguments.</param> /// <returns>The service instance.</returns> public T GetService<T>(object overridedArguments) { var overrides = GetParameterOverrides(overridedArguments); return container.Resolve<T>(overrides.ToArray()); } /// <summary> /// Gets the service instance with the given type by using the overrided arguments. /// </summary> /// <param name="serviceType">The type of the service.</param> /// <param name="overridedArguments">The overrided arguments.</param> /// <returns>The service instance.</returns> public object GetService(Type serviceType, object overridedArguments) { var overrides = GetParameterOverrides(overridedArguments); return container.Resolve(serviceType, overrides.ToArray()); } #endregion #region IServiceProvider Members /// <summary> /// Gets the service instance with the given type. /// </summary> /// <param name="serviceType">The type of the service.</param> /// <returns>The service instance.</returns> public object GetService(Type serviceType) { return container.Resolve(serviceType); } #endregion }}