UnityInterception_With_ICallHandler

2021-06-09 15:03

阅读:675

标签:call   ons   ODB   dbase   gets   扩展   console   顺序   lazy   


应先阅读:《AOP概念,拦截,与UnityInterception基础应用》

https://www.cnblogs.com/xinpingqiyouhe/p/14482213.html

 

代码基于net472

/*
 * 附着InterfaceInterceptor,并通过ICallHandler处理拦截
 * 
 * 应先阅读:《AOP概念,拦截,与UnityInterception基础应用》
 * https://www.cnblogs.com/xinpingqiyouhe/p/14482213.html
 * 
 * 对ICallHandler做了一些抽象和凝练,以及进一步测试和验证。
 * 
 * 
 * 使用这种方式需要:
 *    1、实现ICallHandler作为拦截处理类
 *    2、实现抽象HandlerAttribute的特性,并将其标注到要拦截的目标(或其成员)上——用于实例化ICallHandler,同时表明使用这个Handler处理拦截
 *    3、为Unity容器配置拦截扩展,以及为目标对象配置拦截器
 * 
 * 
 * 这种方式可以支持“单次拦截”对“多拦截处理函数”,并按ICallHandler.Order有小到大(支持负数)的顺序执行:
 *    比如有两个处理函数CH1、CH2(有序),则实际效果为:CH1前 -> CH2前 -> 真实调用 -> CH2后 -> CH1后
 *    【但为Order = 0的永远排在最后!!!】
 *    
 *    另外,如果真实调用发生了未处理异常,不会打断后续处理链,即一切“后”照常执行(下方ExceptionCallHandler);
 *    而如果拦截处理链中,想主动【生成】异常,希望打断处理链,参见下方VerificationCallHandler
 * 
 * 
 * 这里的“多拦截处理函数”实际有两种情况:
 *    1、不同的处理函数(通过不同特性进行标注)
 *        由于特性的不可重叠,这种是最为合理的——同一个目标想拥有多个处理函数,给他挂上不同特性就好
 *        比如下方的ExceptionCallHandler(真实函数抛出异常)和VerificationCallHandler(拦截处理链主动【生成】异常)
 *    
 *    2、相同特性的处理函数
 *        由于特性挂在interface上、class上、和各自的member上,都可以正确标识拦截,所以存在同特性多次处理拦截的情况
 *        此时若Order相同,则调用顺序为:接口前 -> 接口成员前 -> 类前 -> 类成员前 -> 真实调用 -> 类成员后 -> 类后 -> 接口成员后 -> 接口后
 *        例子为下方LogCallHandler
 * 
 * 
 * 本例使用InterfaceInterceptor类型的拦截器,这种拦截器同样可以拦截对目标的一切访问,但目标必须是个接口。
 */

namespace ConsoleApp2
{
    #region 被拦截的接口,与对其实例化的类
    [LogInterception(Order = 60, ZiDingYi = "接口")]//特性放在接口上,对内部所有内容(包括属性)均生效
    [HandleExceptionInterception(Order = 55)]
    public interface IProductDao
    {
        [LogInterception(Order = 20, ZiDingYi = "接口属性")]//放在成员只对这个成员生效,这里导致处理函数叠加
        int Id
        {
            get;
            [VerificationInterception(Order = 15)]//仅处理对Id属性的Set调用
            set;
        }

        [LogInterception(Order = 30, ZiDingYi = "接口方法")]
        void Get();

        void ThrowExpectionMethod();
    }

    [LogInterception(Order = 40, ZiDingYi = "")]//特性放到类上也是OK,同样对内部所有内容生效
    public class ProductDao : IProductDao
    {
        int _id;

        [LogInterception(Order = -10, ZiDingYi = "类属性")]//与放在接口成员上效果一致,Order支持负数
        public int Id
        {
            get { return _id; }
            set { _id = value; Console.WriteLine("设置了Id属性"); }//本例中赋值为负数时会被VerificationInterception打断,不会执行
        }

        [LogInterception(Order = 50, ZiDingYi = "类方法")]
        public void Get()
        {
            Console.WriteLine("真实调用");
        }

        public void ThrowExpectionMethod()
        {
            Console.WriteLine("真实抛出异常");
            throw new Exception("错了");
        }
    }
    #endregion

    #region 日志拦截,演示“同Handler多次注册”——Attribute挂哪都有效;Order也有效;以及不设置Order时的顺序
    public class LogHandler : ICallHandler
    {
        public int Order { get; set; }
        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            Console.WriteLine($"Log: Order={Order}, {ZiDingYi}_前");//拦截器将输出自定义的语句
            var r = getNext()(input, getNext);
            Console.WriteLine($"Log: Order={Order}, {ZiDingYi}_后");
            return r;
        }
        //演示自定义参数的使用
        public string ZiDingYi { get; set; }
    }
    //以特性的方式,把拦截处理函数挂到目标上。它与ICallHandler一一对应
    public class LogInterceptionAttribute : HandlerAttribute
    {
        //演示自定义参数
        public string ZiDingYi;
        public override ICallHandler CreateHandler(IUnityContainer container)
        {
            //return new LogCallHandler { ZiDingYi = ZiDingYi};//这样就是不为Order赋值,所有Order相同
            return new LogHandler { ZiDingYi = ZiDingYi, Order = Order };//不要忘记赋值,Attribute和Handler没有继承关系
        }
    }
    #endregion

    #region 异常处理拦截,演示真实函数发生异常的情形——不会打断拦截处理链
    public class HandleExceptionHandler : ICallHandler
    {
        public int Order { get; set; }

        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            Console.WriteLine($"Ex: Order={Order}, 前");
            var r = getNext()(input, getNext);
            if (r.Exception != null)//真实函数发生的未处理异常,它不会打断后续拦截处理链。
            {
                //这里可以直接获取反射信息
                Console.WriteLine($"Ex: Order={Order}, 后。{input.Target}->{input.MethodBase.Name} Exception:{r.Exception.Message}");
            }
            else
                Console.WriteLine($"Ex: Order={Order}, 后");
            return r;
        }
    }
    /// 
    /// 模拟处理真实函数异常
    /// 
    public class HandleExceptionInterceptionAttribute : HandlerAttribute
    {
        public override ICallHandler CreateHandler(IUnityContainer container)
        {
            return new HandleExceptionHandler { Order = Order };
            //return new HandleExceptionHandler { };用这句话试出了,为Order=0的(相当于没赋值过)的处理函数会排在最后一个
        }
    }
    #endregion

    #region 校验拦截,演示如何在拦截处理链中主动【生成】异常,并打断处理链
    public class VerificationCallHandler : ICallHandler
    {
        public int Order { get; set; }

        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            Console.WriteLine($"Vrf: Order={Order}, 前");
            //由于本处理函数仅挂在Id.Set()方法上,所以必然会有这个参数值
            var value = (int)input.Arguments[0];

            if (value 0)
                //这个return会打断整个拦截链,并在主调语句抛出本异常
                return input.CreateExceptionMethodReturn(new ArgumentException("Id不能为负数"));

            var r = getNext()(input, getNext);
            Console.WriteLine($"Vrf: Order={Order}, 后");
            return r;
        }
    }
    /// 
    /// 校验参数是否非负
    /// 
    public class VerificationInterceptionAttribute : HandlerAttribute
    {
        public override ICallHandler CreateHandler(IUnityContainer container)
        {
            return new VerificationCallHandler { Order = Order };
        }
    }
    #endregion

    class Program
    {
        static void Main(string[] args)
        {
            IUnityContainer container = new UnityContainer();


            UnityConfigurationSection unitySection = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
            unitySection.Configure(container);
            //流式配置写法,不需要配置文件
            //container
            //    .AddNewExtension()
            //    .RegisterType()
            //    .Configure()
            //    .SetInterceptorFor(new InterfaceInterceptor());


            //IProductDao dao = new ProductDao();//这么写无法触发拦截。哪怕用的也是IProductDao
            IProductDao dao = container.Resolve();//构造函数不会触发拦截,但必须从容器中解析,实际生成拦截器代理对象

            Console.WriteLine("------调用方法------");
            dao.Get();//触发拦截

            Console.WriteLine();
            Console.WriteLine("------调用属性------");
            Console.WriteLine($"ID={dao.Id}");//会出现在本次调用的最后,因为拦截发生在读Id属性时,而不是这条输出语句

            Console.WriteLine();
            Console.WriteLine("------真实方法异常,不打断拦截处理链------");
            try
            {
                dao.ThrowExpectionMethod();//尽管真实函数的未处理异常不会打断后续处理链,但这个异常还是会抛出的
            }
            catch
            {
                Console.WriteLine("主调方进行了捕获");//会出现在本次调用的最后——即整个拦截处理链完成后,在主调语句抛出了获异常
            }

            Console.WriteLine();
            Console.WriteLine("------拦截中生成异常,打断拦截处理链------");
            try
            {
                dao.Id = -3;//尽管真实函数的未处理异常不会打断后续处理链,但这个异常还是会抛出的
            }
            catch(Exception ex)
            {
                Console.WriteLine($"主调方捕获了异常:{ex.Message}");//会出现在本次调用的最后——即整个拦截处理链完成后,在主调语句抛出了获异常
            }

            Console.Read();
        }
    }
}

 

配置文件(可选)

xml version="1.0" encoding="utf-8" ?>
configuration>

  configSections>
    section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
  configSections>

  unity>
    
    
    alias alias="MyClass" type="ConsoleApp1.MyClassForProxyInterception, ConsoleApp1"/>
    alias alias="MyInterceptionBehavior" type="ConsoleApp1.MyInterceptionBehavior, ConsoleApp1"/>

    sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration"/>

    container name="MyInterception">
      
      extension type="Interception"/>

      
      register type="MyClass">
        
        interceptor type="TransparentProxyInterceptor"/>
        interceptionBehavior name="MyBehavior" type="MyInterceptionBehavior"/>
        policyInjection/>
      register>
      
    container>
  unity>
  
    startup> 
        supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
    startup>
configuration>

 

执行结果

技术图片

 

UnityInterception_With_ICallHandler

标签:call   ons   ODB   dbase   gets   扩展   console   顺序   lazy   

原文地址:https://www.cnblogs.com/xinpingqiyouhe/p/14489705.html


评论


亲,登录后才可以留言!