AOP是面向切面编程,以下是对于AOP的定义:在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
OOP是面向对象编程,其是以对象模型为基础进行的抽象过程,并在应用过程中形成了描述自己的抽象概念定义,包括对象、类、封装、继承以及多态等。但是OOP同时也是静态的、封闭的,任何需求的变化都会带来影响。而AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程的某个步骤或阶段,以获得逻辑过程的中各部分之间低耦合的隔离效果。这两种设计思想在目标上有着本质的差异。AOP这种方式可以在不影响类的 封装的条件下,为类带来更多的新的功能,往往是在方法前、后添加通用逻辑,如:日志、缓存、异常、权限、校验等逻辑,而主要业务逻辑还是需要OOP完成。
AOP的优势主要表现在:
1、将通用功能从业务逻辑中抽离出来,可以省略大量重复代码,有利于代码的操作和维护。
2、在软件设计时,抽出通用功能(切面),有利于软件设计的模块化,降低软件架构的复杂度。也就是说通用的功能都是一个单独的模块,在项目的主业务里面是看不到这些通用功能的设计代码的。
以下是通过静态拦截的方式实现AOP(GOF23装饰器模式)
以下是接口代码:
public interface IUserProcessor { void RegUser(User user); }
继承接口类:
public class UserProcessor : IUserProcessor { public void RegUser(User user) { Console.WriteLine("用户已注册。Name:{0},PassWord:{1}", user.Name, user.Password); } }
装饰器类:
/// <summary> /// 装饰器的模式去提供一个AOP功能 /// </summary> public class UserProcessorDecorator : IUserProcessor { private IUserProcessor UserProcessor { get; set; } public UserProcessorDecorator(IUserProcessor userprocessor) { UserProcessor = userprocessor; } public void RegUser(User user) { BeforeProceed(user); this.UserProcessor.RegUser(user); AfterProceed(user); } /// <summary> /// 业务逻辑之前 /// </summary> /// <param name="user"></param> public void BeforeProceed(User user) { Console.WriteLine("方法执行前"); } /// <summary> /// 业务逻辑之后 /// </summary> /// <param name="user"></param> public void AfterProceed(User user) { Console.WriteLine("方法执行后"); } }
具体实现AOP代码:
public static void Show() { User user = new User() { Name = "用户1", Password = "123123123123" }; IUserProcessor processor = new UserProcessor(); processor.RegUser(user); Console.WriteLine("***************"); processor = new UserProcessorDecorator(processor); processor.RegUser(user); }
以上就是简单装饰器模式通过静态拦截的方式实现AOP,类UserProcessorDecorator、UserProcessor都继承了接口IUserProcessor,且类UserProcessorDecorator内置了UserProcessor一个实例,在不破坏类UserProcessor封装的前提条件下,可通过封装类UserProcessorDecorator的方式,在执行方法RegUser前、后执行一些通用操作。
此方式实现很简单,但是我们也可以明确的看出来这就是AOP的功能,在不破坏类型封装,通过外部修改的方式,给类型带来额外操作,但是这种方式实现成本太高,每次功能修改都需要重新封装装饰器,这仅仅是静态实现方式。
下面我们通过微软封装的RealProxy(真实代理)进一步了解AOP
先封装一个基础于RealProxy的真是真实代理类MyRealProxy
/// <summary> /// 真实代理 /// </summary> /// <typeparam name="T"></typeparam> public class MyRealProxy<T> : RealProxy { private T tTarget; public MyRealProxy(T target) : base(typeof(T)) { this.tTarget = target; } public override IMessage Invoke(IMessage msg) { BeforeProceede(msg); IMethodCallMessage callMessage = (IMethodCallMessage)msg; object returnValue = callMessage.MethodBase.Invoke(this.tTarget, callMessage.Args); AfterProceede(msg); return new ReturnMessage(returnValue, new object[0], 0, null, callMessage); } public void BeforeProceede(IMessage msg) { Console.WriteLine("方法执行前可以加入的逻辑"); } public void AfterProceede(IMessage msg) { Console.WriteLine("方法执行后可以加入的逻辑"); } }
再封装一个透明代理类TransparentProxy /// <summary>
/// 透明代理 /// </summary> public static class TransparentProxy { public static T Create<T>() { T instance = Activator.CreateInstance<T>(); MyRealProxy<T> realProxy = new MyRealProxy<T>(instance);
//返回 System.Runtime.Remoting.Proxies.RealProxy 的当前实例的透明代理 T transparentProxy = (T)realProxy.GetTransparentProxy(); return transparentProxy; } }
接口IUserProcessor我们在装饰器模式中已经实现过了,接下来我们需要重新封装接口的继承类UserProcessor(在使用RealProxy过程中,实现类必须继承于MarshalByRefObject)
/// <summary> /// 必须继承自MarshalByRefObject父类 /// </summary> public class UserProcessor : MarshalByRefObject, IUserProcessor { public void RegUser(User user) { Console.WriteLine("用户已注册。用户名称{0} Password{1}", user.Name, user.Password); } }
接下来我们就可以通过这种方式来实现AOP了
public static void Show() { User user = new User() { Name = "用户2", Password = "123456" }; UserProcessor processor = new UserProcessor(); processor.RegUser(user); Console.WriteLine("*********************"); UserProcessor userProcessor = TransparentProxy.Create<UserProcessor>(); userProcessor.RegUser(user); int result = userProcessor.GetUserId(); }
我们会发现直接申明UserProcessor后调用RegUser()函数时,没有任务多余操作,就直接执行UserProcessor中申明的RegUser,而通过透明代理TransparentProxy所申明的UserProcessor实例,在执行RegUser()时,会在执行前添加BeforeProceede(msg),执行后添加AfterProceede(msg)操作。
这种方式虽然也能实现AOP,相比于装饰器的方式,这种不用每次都重新写一个类,但是最大的限制是,使用的类必须继承于MarshalByRefObject。
这是微软关于RealProxy的介绍 https://msdn.microsoft.com/zh-cn/library/dn574804.aspx
下面介绍一下Castle.DynamicProxy的方式实现AOP
Castle DynamicProxy是一个用于在运行时生成.NET代理的库。它允许您动态地更改和扩展业务对象的行为。这使得您的域模型更加可维护,因为交叉关切纯粹与核心域模型脱钩。如果为任何组件指定拦截器,Castle将自动创建代理。您使用拦截器将代理注入行为。话不多说,直接上代码。
要使用Castle DynamicProxy做拦截,我们必须封装一个实现IInterceptor接口(来自于Castle.DynamicProxy)的类
public class MyInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { PreProceed(invocation); invocation.Proceed(); PostProceed(invocation); } public void PreProceed(IInvocation invocation) { Console.WriteLine("方法执行前"); } public void PostProceed(IInvocation invocation) { Console.WriteLine("方法执行后"); } }
同时咋们也需要重新封装一下类UserProcessor(使用Castle DynamicProxy作拦截时,类必须是virtual)
public class UserProcessor : IUserProcessor { /// <summary> /// 必须带上virtual /// </summary> /// <param name="user"></param> public virtual void RegUser(User user) { Console.WriteLine($"用户已注册。Name:{user.Name},PassWord:{user.Password}"); } }
好了,接下来就可以直接实现AOP了
public static void Show() { User user = new User() { Name = "用户3", Password = "123456" }; UserProcessor processor = new UserProcessor(); processor.RegUser(user); Console.WriteLine("*********************"); ProxyGenerator generator = new ProxyGenerator(); MyInterceptor interceptor = new MyInterceptor(); UserProcessor userprocessor = generator.CreateClassProxy<UserProcessor>(interceptor); userprocessor.RegUser(user); }
执行show函数,通过直接申明UserProcessor的实例调用RegUser函数,无其他拦截操作,但是通过ProxyGenerator创建的UserProcessor实例,执行RegUser函数前会添加PreProceed操作,执行函数后会添加PostProceed操作。这也是AOP的一种实现方式。但是这种方式最大的弊端是,拦截的函数需要是虚方法。
还有另一种方式是可以通过IOC框架Unity,通过特性的方式实现AOP,由于篇幅有限就不再讲了。
以上是对AOP的简单理解,在AOP 其实就是通过拦截函数的方式,在OOP中划一个小口子出来,使得我们能通过外部方式在小口子里实现一些通用的操作,AOP也是对OOP的一种补充。