首页 技术交流 net6运用Castle.Core类库利用动态代理实现aop切面编程(一) 正文

net6运用Castle.Core类库利用动态代理实现aop切面编程(一)

发布日期:2022年01月07日 14:32        阅读次数:1960

动态代理是实现AOP的一种方式,即在开发过程中我们不需要处理切面中(日志等)的工作,而是在运行时,通过动态代理来自动完成。Castle DynamicProxy是一个实现动态代理的框架,被很多优秀的项目用来实现AOP编程,EF Core、Autofac等。


第一步,首先在vs的nuget包管理器中安装Castle.Core包。

                                             

第二步:定义拦截器,实现IInterceptor接口,新建aop类继承IInterceptor

 public class LoggingInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            Console.WriteLine($"Calling method {invocation.TargetType}.{invocation.Method.Name}.");
            invocation.Proceed(); // continue
        }
    }

定义IBlogService类

 public class BlogPost
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public bool Disabled { get; set; }
        public DateTime Created { get; set; }
    }

 public interface IBlogService
    {
        void DisablePost(BlogPost post);
        BlogPost GetPost(int id);
    }
      

    public class BlogService : IBlogService
    {
        public BlogPost GetPost(int id)
        {
            return new BlogPost
            {
                Id = id,
                Title = "Test",
                Description = "Test",
                Disabled = false,
                Created = DateTime.UtcNow
            };
        }

        public void DisablePost(BlogPost post)
        {
            post.Disabled = true;
        }
    }

    public class BlogService2
    {
        public virtual BlogPost GetPost(int id)
        {
            return new BlogPost
            {
                Id = id,
                Title = "Test",
                Description = "Test",
                Disabled = false,
                Created = DateTime.UtcNow
            };
        }

        public void DisablePost(BlogPost post)
        {
            post.Disabled = true;
        }
    }

net6program顶级语句进行aop的注入:

//以动态代理接口方式实现aop
services.AddScoped<BlogService>();
services.AddScoped(typeof(IBlogService), serviceProvider =>
{
    var proxyGenerator = serviceProvider.GetRequiredService<ProxyGenerator>();

    var interceptors = serviceProvider.GetServices<IInterceptor>().ToArray();
    var c = serviceProvider.GetRequiredService<BlogService>();
    return proxyGenerator.CreateInterfaceProxyWithTarget(typeof(IBlogService), c, interceptors);
});

//以动态代理类实现aop 这里需要注意的是通过动态代理类action方法必须设置为virtual方法例如BlogService2类的virtual BlogPost GetPost(int id),否则无法切面

services.AddScoped(serviceProvider =>
{
    var proxyGenerator = serviceProvider.GetRequiredService<ProxyGenerator>();

    var interceptors = serviceProvider.GetServices<IInterceptor>().ToArray();
    return proxyGenerator.CreateClassProxy<BlogService2>(interceptors);
});

控制器的实现:

public class DemoController : ControllerBase
    {
        private readonly IBlogService _blogService;
        private readonly BlogService2 _blogService2;

        public DemoController(IBlogService blogService,BlogService2 blogService2)
        {
           _blogService = blogService;
            _blogService2 = blogService2;
        }

       
        [HttpGet]
         public string Test1()
        {
            
            _blogService2.GetPost(2);

        }
        [HttpGet]
         public string Test2()
        {
           _blogService2.GetPost(2);

        }
      }


以上是在netcore依赖注入的框架环境下进行aop切面。


下面是在controler控制器进行编码,这样来理解动态代理的业务逻辑:

 [HttpGet]
        public async Task<BlogPost> TestInterceptor()
        {
            var generator = new ProxyGenerator();
            var actual = new BlogService();

            //为接口使用代理
            var a = generator.CreateInterfaceProxyWithTarget(typeof(IBlogService), actual, new LoggingInterceptor());
            var proxiedService = (IBlogService)a;
            var aa= proxiedService.GetPost(1);
            //为类使用代理
            var b =generator.CreateClassProxy<BlogService2>(new LoggingInterceptor());
            b.GetPost(2);
            return await Task.FromResult(aa);
        }

这里需要注意的就是

以动态代理类实现aop 这里需要注意的是通过动态代理类action方法必须设置为virtual方法例如BlogService2类的virtual BlogPost GetPost(int id),否则无法切面

DynamicProxy的基本原理

动态代理只对公共接口方法、类中的虚方法生效,这是为什么呢?

其实,动态代理是在运行时为我们动态生成了一个代理类,通过Generator生成的时候返回给我们的是代理类的实例,而只有接口中的方法、类中的虚方法才可以在子类中被重写。

有一篇博文解释的很好,拷贝到这加深理解:

如果不使用动态代理,我们的代理服务应该是什么样的呢?来看下面的代码,让我们手工创建一个代理类:

 public interface IProductRepository
 {     
      public void Update(Product product);
  }
  public class ProductRepository : IProductRepository
  {    
     public  void Update(Product product)
     {        //执行更新操作
             //......
     }
   }

为接口使用代理:

public class ProductRepositoryProxy : IProductRepository
        {
            private readonly ILogger logger;
            private readonly IProductRepository target;
            public ProductRepositoryProxy(ILogger logger, IProductRepository target)
            {
                this.logger = logger;
                this.target = target;
            }
            public void Update(Product product)
            {        //调用IProductRepository的Update操作
                target.Update(product);        //记录日志
                this.logger.WriteLog($"{nameof(Update)} 已执行");
            }
        }
        //使用代理类IProductRepository target = new ProductRepository();
        ILogger logger = new ConsoleLogger();
        IProductRepository productRepository = new ProductRepositoryProxy(logger, target);

为类使用代理:

 public class ProductRepository : IProductRepository
        {    //改写为虚方法
            public virtual void Update(Product product)
            {        //执行更新操作
                     //......
            }
        }
        public class ProductRepositoryProxy : ProductRepository
        {
            private readonly ILogger logger;
            public ProductRepositoryProxy(ILogger logger)
            {
                this.logger = logger;
            }
            public override void Update(Product product)
            {        //调用父类的Update操作
                base.Update(product);        //记录日志
                this.logger.WriteLog($"{nameof(Update)} 已执行");
            }
        }
        //使用代理类ILogger logger = new ConsoleLogger();
        ProductRepository productRepository = new ProductRepositoryProxy(logger);

开源类库地址:https://github.com/castleproject/Core

demo演示地址:https://github.com/zHaytam/AspNetCore.DynamicProxies.Sample


继续更新:

最后在实操过程中,发现通过接口注入动态代理类:

 public class BlogService : IBlogService
    {
        private readonly IRepository<tasksqz> _repository;
        public BlogService(IRepository<tasksqz> repository)
        {
            _repository = repository;
        }
        public BlogPost GetPost(int id)
        {
            _repository.FindAsync(x => x.id == id);
            return new BlogPost
            {
                Id = id,
                Title = "Test",
                Description = "Test",
                Disabled = false,
                Created = DateTime.UtcNow
            };
        }

        public void DisablePost(BlogPost post)
        {
            post.Disabled = true;
        }
    }

这样是没有问题的,仓库类,_repository会自动注入进来。

但是如果是通过类实现动态代理,运用下面写法会抛异常,提示没法为其注入构造方法的参数提示值。

评论:

共 0 页 0 条记录,当前为第 1 页