AOP是什么?为什么要使用AOP

一、AOP简介
AOP的全称是Aspect-Oriented Programming,即面向切面编程(也称面向方面编程)。它是面向对象编程(OOP)的一种补充,目前已成为一种比较成熟的编程方式。

在传统的业务处理代码中,通常都会进行事务处理、日志记录等操作。虽然使用OOP可以通过组合或者继承的方式来达到代码的重用,但如果要实现某个功能(如日志记录),同样的代码仍然会分散到各个方法中。这样,如果想要关闭某个功能,或者对其进行修改,就必须要修改所有的相关方法。这不但增加了开发人员的工作量,而且提高了代码的出错率。

为了解决这一问题,AOP思想随之产生。AOP采取横向抽取机制,将分散在各个方法中的重复代码提取出来,然后在程序编译或运行时,再将这些提取出来的代码应用到需要执行的地方。这种采用横向抽取机制的方式,采用传统的OOP思想显然是无法办到的,因为OOP只能实现父子关系的纵向的重用。虽然AOP是一种新的编程思想,但却不是OOP的替代品,它只是OOP的延伸和补充。

在AOP思想中,类与切面的关系如下图所示。从图中可以看出,通过Aspect(切面)分别在Class1和Class2的方法中加入了事务、日志、权限和异常等功能。


 

AOP的使用,使开发人员在编写业务逻辑时可以专心于核心业务,而不用过多的关注于其他业务逻辑的实现,这不但提高了开发效率,而且增强了代码的可维护性。

二、AOP的实现

我们在上面讲解了有关AOP的一些理论知识,那么如何在代码里面实现呢?

实现AOP有两种方式:

  • 静态代理实现。所谓静态代理,就是我们自己来写代理对象。
  • 动态代理实现。所谓动态代理,就是在程序运行时,去生成一个代理对象。

1、静态代理

实现静态代理需要使用到两种设计模式:装饰器模式和代理模式。

装饰器模式:允许向一个现有的对象添加新的功能,同时又不改变这个现有对象的结构。属于结构型设计模式,它是作为现有类的一种包装。首先会创建一个装饰类,用来包装原有的类,并在保持类的完整性的前提下,提供额外的功能。
看下面的例子。

我们首先创建一个User类:

 

 

 接着我们创建一个账号服务的接口,里面有一个方法,用来注册一个用户:

 

 

 然后创建一个类来实现上面的接口:

  

 我们在创建一个装饰器类:

 

  我们会发现装饰器类同样实现了IAccountService接口。最后我们在Main方法里面调用:

 

 

代理模式:即一个类代表另一个类的功能。我们会创建一个代理类,这个代理类和装饰器类基本一样。

 

调用方式更简单。不再这里演示了。
这里的静态代理方式有一些缺陷,虽然确保了业务类职责单一,但如果需要代理的类很多,那么我们需要写大量的辅助类,工作量非常大。
所以实际用的比较多的就是如下所示的动态代理。

2、动态代理

动态代理实现也有两种方式;

  • 通过代码织入的方式。例如PostSharp第三方插件。我们知道.NET程序最终会编译成IL中间语言,在编译程序的时候,PostSharp会动态的去修改IL,在IL里面添加代码,这就是代码织入的方式。
  • 通过反射的方式实现。通过反射实现的方法非常多,也有很多实现了AOP的框架,例如Unity、MVC过滤器、Autofac等。

我们先来看看如何使用PostSharp实现动态代理。PostSharp是一款收费的第三方插件。

首先新创建一个控制台应用程序,然后创建一个订单业务类:

 

 接着在Main方法里面调用:

 

 这时又提出了一个新的需求,要去添加一个日志功能,记录业务的执行情况,按照以前的办法,需要定义一个日志帮助类:

 

 如果不使用AOP,我们就需要在记录日志的地方实例化Loghelper对象,然后记录日志:

 

 

 

 这样修改可以实现记录日志的功能。但是上面的方法会修改原先已有的代码,这就违反了开闭原则。而且添加日志也不是业务需求的变动,不应该去修改业务代码。

下面使用AOP来实现。首先安装PostSharp,直接在NuGet里面搜索,然后安装即可:

 

 然后定义一个LogAttribute类,继承自OnMethodBoundaryAspect。这个Aspect提供了进入、退出函数等连接点方法。另外,Aspect上必须设置“[Serializable] ”,这与PostSharp内部对Aspect的生命周期管理有关:

 

 然后Log特性应用到DoWork函数上面:

 

 这样修改以后,只需要在方法上面添加一个特性,以前记录日志的代码就可以注释掉了,这样就不会再修改业务逻辑代码了,运行程序:

 

 

这样就实现了AOP功能。

我们在看看使用Remoting来实现动态代理。

首先还是创建一个User实体类:

 

 然后创建一个接口,里面有一个注册方法:

 

 然后创建接口的实现类:

 

 然后创建一个泛型的动态代理类:

 

我们看到,这个泛型动态代理类里面有两个泛型委托:BeforeAction、AfterAction。通过构造函数把代理泛型类传递进去。最后调用Invoke方法执行代理类的方法。
最后我们还要创建一个代理工厂类,用来创建代理对象,通过调用动态代理来创建动态代理对象:

using System;
 
namespace DynamicProxy
{
    /// <summary>
    /// 动态代理工厂类
    /// </summary>
    public static class ProxyFactory
    {
        public static T Create<T>(Action before, Action after)
        {
            // 实例化被代理泛型对象
            T instance = Activator.CreateInstance<T>();
            // 实例化动态代理,创建动态代理对象
            var proxy = new DynamicProxy<T>(instance) { BeforeAction = before, AfterAction = after };
            // 返回透明代理对象
            return (T)proxy.GetTransparentProxy();
        }
    }
}

我们最后在Main方法里面调用:

using DynamicProxy.Model;
using DynamicProxy.Services;
using System;
 
namespace DynamicProxy
{
    class Program
    {
        static void Main(string[] args)
        {
            // 调用动态代理工厂类创建动态代理对象,传递AccountService,并且传递两个委托
            var acount = ProxyFactory.Create<AccountService>(before:() =>
            {
                Console.WriteLine("注册之前");
            }, after:() =>
            {
                Console.WriteLine("注册之后");
            });
 
            User user = new User() 
            {
             Name="张三",
             Password="123456"
            };
            // 调用注册方法
            acount.Reg(user);
 
            Console.ReadKey();
        }
    }
}

 

posted @ 2022-04-20 13:18  SilverFox8588  阅读(708)  评论(0编辑  收藏  举报