首页 > 代码库 > 【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    }}
View Code

 

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    }}
View Code

 

第二步:添加配置文件,为需要被拦截的类添加拦截行为。

例如如下的配置,是为实现了接口

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    }}
View Code

 

第二:将特性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    }}
View Code

 

第三:拦截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    }}
View Code