动态代理是实现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会自动注入进来。
但是如果是通过类实现动态代理,运用下面写法会抛异常,提示没法为其注入构造方法的参数提示值。