zoukankan      html  css  js  c++  java
  • 利用Microsoft.Practices.Unity的拦截技术,实现.NET中的AOP

    1、记住这个单词的意思:Interception(拦截)

    2、首先说一下原理和背景

      原理:所谓的AOP就是面向切面编程,这里不多说,百度搜索。

      目的:个人认为是为了解耦,部分代码跟业务代码分离,业务代码里面不掺杂其它功能,比如:记录异常、记录操作日志。

      背景:项目基本功能已完成,产品要求记录用户的操作日志,新增的时候记录某人在某时做了某事(包括详细的信息,比如新增了哪些字段或者修改了哪些字段)。于是着手在业务代码里写了大量的关于记录操作日志的代码,怎么看怎么别扭,像是被XX了的感觉。

    3、解决办法

      针对上述背景,于是想到了在业务逻辑方法上面加个特性,用以记录操作日志,这样代码就变的非常干净。而刚好项目里用到了DI,是微软的Microsoft.Practices.Unity组件。于是在网上开始找资料,最终通过不断的阅读别人的代码和反复试验,总算实现了。因为Microsoft.Practices.Unity组件本身就自带拦截功能。这就是为什么一开始就说拦截单词(Interception)的原因。

      其实我是想实现这样的代码:  

    namespace Business
    {
        public interface IUserBusiness
        {
            string Speak();
    
            string Run();
    
            [OperationLog("UserName,Password,Id", "UserRepository")]
            User Create(User user, Authority authority);
    
            [OperationLog("", "UserRepository")]
            User Get(long id);
        }
    }

      通过在方法上面添加特性,实现记录用户操作日志的功能。

      OperationLog特性介绍:

      第一个参数表示我想记录哪些字段

      第二个参数表示我将采用哪个Repository来根据ID获取原始值。

    4、具体代码

      准备好需要的Dll(2.1.505.0版本):

      Microsoft.Practices.Unity.Configuration.dll
      Microsoft.Practices.Unity.dll
      Microsoft.Practices.Unity.Interception.Configuration.dll
      Microsoft.Practices.Unity.Interception.dll

      DI我采用的是在配置文件里面做的,如下:

      unity.di.infrastructure.config

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <configSections>
        <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration"/>
      </configSections>
      <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
        <!--引用程序集-->
        <assembly name="Business" />
        <assembly name="Repository" />
        <!--引用命名空间-->
        <namespace name="Business" />
        <namespace name="Repository" />
    
        <container>
          <register type="IUserBusiness" mapTo="UserBusiness" />
          
          <register type="IUserRepository" mapTo="UserRepository" />
        </container>
      </unity>
    </configuration>

      ioc帮助类:

    namespace AopDemo
    {
        /// <summary>
        /// 依赖注入帮助类
        /// 创建人:君爷
        /// 创建时间:2015-10-23
        /// </summary>
        public class IocHelper
        {
            /// <summary>
            /// 读取接口和实现类的XML配置文件,并向MVC控制器注入自定义的ControllerFactory
            /// </summary>
            public void Init()
            {
                try
                {
                    IUnityContainer container = new UnityContainer();
    
                    //加载Ioc配置文件,读取所有Ioc接口和实现
                    var fileMap = new ExeConfigurationFileMap { ExeConfigFilename = HttpContext.Current.Server.MapPath("~/app_data/unity.di.infrastructure.config") };
                    Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
                    UnityConfigurationSection section = (UnityConfigurationSection)configuration.GetSection("unity");
                    container.LoadConfiguration(section);
                    
                    //AOP
                    container.AddNewExtension<Interception>();
                    container.RegisterType<IUserBusiness, UserBusiness>().Configure<Interception>().SetInterceptorFor<IUserBusiness>(new InterfaceInterceptor());
                    
                    //向Mvc请求的上下文注入 Unity控制器工厂
                    IControllerFactory controllerFactory = new UnityControllerFactory(container);
                    ControllerBuilder.Current.SetControllerFactory(controllerFactory);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
        }
    
        /// <summary>
        /// 依赖工厂
        /// </summary>
        public class UnityControllerFactory : DefaultControllerFactory
        {
            private readonly IUnityContainer container;
    
            /// <summary>
            /// 构造方法
            /// </summary>
            /// <param name="container"></param>
            public UnityControllerFactory(IUnityContainer container)
            {
                //要做异常处理            
                this.container = container;
            }
    
            /// <summary>
            /// 根据请求的上下文实例化控制器
            /// </summary>
            /// <param name="requestContext">请求上下文</param>
            /// <param name="controllerType">控制器类型</param>
            /// <returns></returns>
            protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
            {
                //这里把Controller实例注册到了unity容器
                try
                {
                    IController icontroller = container.Resolve(controllerType) as IController;
                    return icontroller;
                }
                catch (Exception ex)
                {
                    return null;
                }
            }
        }
    }

      Global.asax的Application_Start方法添加依赖注入容器

        public class MvcApplication : System.Web.HttpApplication
        {
            protected void Application_Start()
            {
                AreaRegistration.RegisterAllAreas();
    
                WebApiConfig.Register(GlobalConfiguration.Configuration);
                FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
                RouteConfig.RegisterRoutes(RouteTable.Routes);
    
                //IOC
                var iocFactory = new IocHelper();
                iocFactory.Init();
            }
        }

      然后添加接口记录操作日志的Handler和Attribute

    public class OperationLogHandler : ICallHandler
        {
            private string _fields;
            private string _repository;
    
            public OperationLogHandler(string fields, string repository)
            {
                this._fields = fields;
                this._repository = repository;
            }
    
            public int Order { get; set; }//这是ICallHandler的成员,表示执行顺序
    
            public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
            {
                //定义存放原始值和新值的变量
                BaseModel oldValue;
                BaseModel newValue;
    
                //获取类型为BaseModel的参数
                object model = null;
                object authority = null;
                foreach (var argument in input.Arguments)
                {
                    if (argument.GetType().BaseType == typeof(BaseModel))
                    {
                        model = argument;
                    }
                    else if (argument.GetType() == typeof(Authority))
                    {
                        authority = argument;
                    }
                }
    
                if (model == null)
                {
                    throw new Exception("没有找到BaseModel类型,拦截导弹失败。");
                }
    
                //获取实体的Id属性的值
                var properties = model.GetType().GetProperties();
                var idProperty = properties.Where(m => m.Name == "Id").FirstOrDefault();
                long id = (long)idProperty.GetValue(model, null);
    
                //根据ID从数据库获取原先的值。其中Assembly.Load("Repository")中的参数是程序集的dll文件名称,.CreateInstance()中的参数是程序集中的命名空间和类名。
                var repository = Assembly.Load("Repository").CreateInstance("Repository." + this._repository) as IRepository;
                oldValue = repository.Get(id);
    
                //获取新增后返回的实体的值
                var returnValue = getNext()(input, getNext);
                newValue = returnValue.ReturnValue as BaseModel;
    
                //这样就可以获取到插入前和插入后的数据了;比较两个实体,如果不同就记录下来,插入日志表
                var arr = this._fields.Split(',');
                Dictionary<string, object> dic = new Dictionary<string, object>();
                foreach (var field in arr)
                {
                    var property = properties.Where(m => m.Name == field).FirstOrDefault();
                    var oldFieldValue = property.GetValue(oldValue, null);
                    var newFieldValue = property.GetValue(newValue, null);
    
                    var oldHashCode = oldFieldValue.GetHashCode();
                    var newHashCode = newFieldValue.GetHashCode();
    
                    if (oldHashCode != newHashCode)
                    {
                        dic.Add(field, newFieldValue);
                    }
                }
    
                return returnValue;
            }
        }
        public class OperationLogAttribute : HandlerAttribute
        {
            private string _fields;
            private string _repository;
    
            public OperationLogAttribute(string fields, string repository)
            {
                this._fields = fields;
                this._repository = repository;
            }
    
            public override ICallHandler CreateHandler(IUnityContainer container)
            {
                return new OperationLogHandler(this._fields, this._repository);//返回MyHandler
            }
        }

       这样通过配置文件和在代码里设置AOP映射关系,我们就可以轻松的在方法上添加特性。

            [OperationLog("UserName,Password,Id", "UserRepository")]
            User Create(User user, Authority authority);

       当然中间有些其它的都省略了,关键是这些代码。

      代码之后补全

  • 相关阅读:
    Java反射机制DOME
    Contos7 装bcm4312无线网卡驱动
    windows8.1+centos7双系统(装完centos后无win8引导)
    request+response
    HTTP协议+servlet配置
    类加载和反射
    线程池+线程安全
    IO流之Properties(String键值对)+序列流+打印流+commons-IO(最终流程)
    IO流之字节流 +字符流 (复制文件)
    IO流
  • 原文地址:https://www.cnblogs.com/subendong/p/5125360.html
Copyright © 2011-2022 走看看