zoukankan      html  css  js  c++  java
  • 【C#】使用 Castle 实现 AOP,以及 Autofac 集成 Castle

    Castle 是 2003 年诞生于 Apache Avalon 项目,目的是为了创建一个IOC 框架。发展到现在已经有四个组件:

    • ORM组件:ActiveRecord
    • IOC组件:Windsor
    • 动态代理组件:DynamicProxy
    • Web MVC组件:MonoRail

    本文主要介绍 动态代理组件 Castle.DynamicProxy

    基本用法

    Castle.DynamicProxy 是通过 Emit 反射动态生成代理类来实现的,效率相对静态植入要慢一点,但比普通的反射又高一些。动态代理只对公共接口方法、类中的虚方法生效,因为只有接口中的方法、类中的虚方法才可以在子类中重写。

    基于接口的拦截器

    public interface IProductRepository
    {
        void Add(string name);
    }
    
    public class ProductRepository : IProductRepository
    {
        public void Add(string name) => Console.WriteLine($"新增产品:{name}");
    }
    
    public class LoggerInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            var methodName = invocation.Method.Name;
    
            Console.WriteLine($"{methodName} 执行前");
    
            //调用业务方法
            invocation.Proceed();
    
            Console.WriteLine($"{methodName} 执行完毕");
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            ProxyGenerator generator = new ProxyGenerator();
    
            IInterceptor loggerIntercept = new LoggerInterceptor();
    
            IProductRepository productRepo = new ProductRepository();
    
            IProductRepository proxy = generator.CreateInterfaceProxyWithTarget(productRepo, loggerIntercept);
    
            proxy.Add("大米");
    
            Console.Read();
        }
    }
    

    基于类的拦截器

    public class ProductRepository
    {
        public virtual void Add(string name) => Console.WriteLine($"新增产品:{name}");
    }
    
    static void Main(string[] args)
    {
        ProxyGenerator generator = new ProxyGenerator();
    
        IInterceptor loggerIntercept = new LoggerInterceptor();
    
        ProductRepository proxy = generator.CreateClassProxyWithTarget(new ProductRepository(), loggerIntercept);
        // 使用 CreateClassProxy 泛型方法可以省去实例化代码
        //ProductRepository proxy = generator.CreateClassProxy<ProductRepository>(loggerIntercept);
    
        proxy.Add("大米");
    }
    

    在上例中,如果 ProductRepository.Add 不是虚方法,也不会报错,但是拦截器不会被调用。

    异步函数拦截

    Castle.DynamicProxy 对异步函数的拦截跟同步没啥差别,只是,如果要在方法执行完成后插入内容,需要 await

    public class ProductRepository
    {
        public virtual Task Add(string name)
        {
            return Task.Run(() =>
                            {
                                Thread.Sleep(1000);
                                Console.WriteLine($"异步新增产品:{name}");
                            });
        }
    }
    
    public class LoggerInterceptor : IInterceptor
    {
        public async void Intercept(IInvocation invocation)
        {
            var methodName = invocation.Method.Name;
    
            Console.WriteLine($"{methodName} 执行前");
    
            invocation.Proceed();
    
            // 不 await 的话将会先输出“执行完毕”,再输出“异步新增产品”
            var task = (Task)invocation.ReturnValue;
            await task;
    
            Console.WriteLine($"{methodName} 执行完毕");
        }
    }
    

    上面这个写法是简单粗暴的,如果碰到返回值是 Task<TResult>,或者不是异步函数,就会出错。所以这里是要对返回值进行一个判断的。

    可以使用 Castle.Core.AsyncInterceptor 包,它包装了 Castle,使异步调用更简单。

    Castle.Core.AsyncInterceptor 的 GitHub 地址:https://github.com/JSkimming/Castle.Core.AsyncInterceptor

    public class ProductRepository : IProductRepository
    {
        public Task Add(string name)
        {
            return Task.Run(() =>
                            {
                                Thread.Sleep(1000);
                                Console.WriteLine($"异步新增产品:{name}");
                            });
        }
    
        public Task<string> Get()
        {
            return Task.Run(() =>
                            {
                                Thread.Sleep(1000);
                                Console.WriteLine($"获取产品");
    
                                return "大米";
                            });
        }
    }
    
    public class LoggerInterceptor : IAsyncInterceptor
    {
        public void InterceptAsynchronous(IInvocation invocation)
        {
            invocation.ReturnValue = InternalInterceptAsynchronous(invocation);
        }
    
        async Task InternalInterceptAsynchronous(IInvocation invocation)
        {
            var methodName = invocation.Method.Name;
    
            Console.WriteLine($"{methodName} 异步执行前");
    
            invocation.Proceed();
            await (Task)invocation.ReturnValue;
    
            Console.WriteLine($"{methodName} 异步执行完毕");
        }
    
        public void InterceptAsynchronous<TResult>(IInvocation invocation)
        {
            invocation.ReturnValue = InternalInterceptAsynchronous<TResult>(invocation);
    
            Console.WriteLine(((Task<TResult>)invocation.ReturnValue).Id);
        }
    
        private async Task<TResult> InternalInterceptAsynchronous<TResult>(IInvocation invocation)
        {
            var methodName = invocation.Method.Name;
    
            Console.WriteLine($"{methodName} 异步执行前");
    
            invocation.Proceed();
            var task = (Task<TResult>)invocation.ReturnValue;
            TResult result = await task;
    
            Console.WriteLine(task.Id);
    
            Console.WriteLine($"{methodName} 异步执行完毕");
    
            return result;
        }
    
        public void InterceptSynchronous(IInvocation invocation)
        {
            var methodName = invocation.Method.Name;
    
            Console.WriteLine($"{methodName} 同步执行前");
    
            invocation.Proceed();
    
            Console.WriteLine($"{methodName} 同步执行完毕");
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            ProxyGenerator generator = new ProxyGenerator();
    
            IAsyncInterceptor loggerIntercept = new LoggerInterceptor();
    
            IProductRepository productRepo = new ProductRepository();
    
            IProductRepository proxy = generator.CreateInterfaceProxyWithTarget(productRepo, loggerIntercept);
    
            proxy.Get();
        }
    }
    

    这是 Castle.Core.AsyncInterceptor 提供的示例写法,这里有个问题,也是我的疑惑。invocation.ReturnValue = InternalInterceptAsynchronous(invocation); 将导致代理返回的 Task 是一个新的 Task,这一点我们可以输出 Task.Id 来确认。个人感觉有点画蛇添足。

    public async void InterceptAsynchronous<TResult>(IInvocation invocation)
    {
        var methodName = invocation.Method.Name;
    
        Console.WriteLine($"{methodName} 异步执行前");
    
        invocation.Proceed();
        var task = (Task<TResult>)invocation.ReturnValue;
        await task;
    
        Console.WriteLine($"{methodName} 异步执行完毕");
    }
    

    这样就挺好的。

    如果有小伙伴知道为什么要返回一个新的 Task,请留言告诉我,谢谢!

    Autofac 集成

    Autofac.Extras.DynamicProxy 是一个 Autofac 扩展,可与 Castle 一起提供 AOP 拦截。

    基于接口的拦截器

    static void Main(string[] args)
    {
        ContainerBuilder builder = new ContainerBuilder();
        //注册拦截器
        builder.RegisterType<LoggerInterceptor>().AsSelf();
    
        //注册要拦截的服务
        builder.RegisterType<ProductRepository>().AsImplementedInterfaces()
            .EnableInterfaceInterceptors()                  //启用接口拦截
            .InterceptedBy(typeof(LoggerInterceptor));      //指定拦截器
    
        IContainer container = builder.Build();
    
        IProductRepository productRepo = container.Resolve<IProductRepository>();
    
        productRepo.Add("大米");
    }
    

    基于类的拦截器

    static void Main(string[] args)
    {
        ContainerBuilder builder = new ContainerBuilder();
        //注册拦截器
        builder.RegisterType<LoggerInterceptor>().AsSelf();
    
        //注册要拦截的服务
        builder.RegisterType<ProductRepository>()
            .EnableClassInterceptors()                      //启用类拦截
            .InterceptedBy(typeof(LoggerInterceptor));      //指定拦截器
    
        IContainer container = builder.Build();
    
        ProductRepository productRepo = container.Resolve<ProductRepository>();
    
        productRepo.Add("大米");
    }
    

    异步函数拦截

    Castle.Core.AsyncInterceptor 中,IAsyncInterceptor 接口并不集成 IInterceptor 接口,而 Autofac.Extras.DynamicProxy 是绑定 Castle 的,所以按上面同步拦截的写法是会报错的。

    IAsyncInterceptor 提供了 ToInterceptor() 扩展方法来进行类型转换。

    public class LoggerInterceptor : IInterceptor
    {
        readonly LoggerAsyncInterceptor interceptor;
    
        public LoggerInterceptor(LoggerAsyncInterceptor interceptor)
        {
            this.interceptor = interceptor;
        }
    
        public void Intercept(IInvocation invocation)
        {
            this.interceptor.ToInterceptor().Intercept(invocation);
        }
    }
    
    public class LoggerAsyncInterceptor : IAsyncInterceptor
    {
        public void InterceptAsynchronous(IInvocation invocation)
        {
            //...
        }
    
        public void InterceptAsynchronous<TResult>(IInvocation invocation)
        {
            //...
        }
    
        public void InterceptSynchronous(IInvocation invocation)
        {
            //...
        }
    }
    
    static void Main(string[] args)
    {
        ContainerBuilder builder = new ContainerBuilder();
        //注册拦截器
        builder.RegisterType<LoggerInterceptor>().AsSelf();
        builder.RegisterType<LoggerAsyncInterceptor>().AsSelf();
    
        //注册要拦截的服务
        builder.RegisterType<ProductRepository>().AsImplementedInterfaces()
            .EnableInterfaceInterceptors()                  //启用接口拦截
            .InterceptedBy(typeof(LoggerInterceptor));      //指定拦截器
    
        var container = builder.Build();
    
        IProductRepository productRepo = container.Resolve<IProductRepository>();
    
        productRepo.Get();
    }
    

    参考

    https://www.cnblogs.com/youring2/p/10962573.html


    ------------------------------------ END ------------------------------------

    欢迎扫码关注我的公众号


    码农升级
  • 相关阅读:
    jmeter压测学习12-设置持续压测时间(调度器的使用)
    jmeter压测学习11-模拟浏览器访问web页面
    pytest文档73-pytest+yaml实现接口自动化框架之用例参数关联
    selenium+python自动化101-使用execute_script() 方法获取 JavaScript 返回值
    pytest文档72- 使用 template 替换 yaml 文件的变量
    pytest文档71-pytest+yaml实现接口自动化框架
    容器一直处于Create状态
    CAS原理与协议
    Linux route命令详解和使用示例(查看和操作IP路由表)
    Linux reboot全过程
  • 原文地址:https://www.cnblogs.com/gl1573/p/14428321.html
Copyright © 2011-2022 走看看