一、讲AOP之前先了解一下POP(面向过程编程)和OOP(面向对象编程)
- POP:分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次执行可以了。
- OOP: 建立模型来体现抽象思维过程,模型是用来反映现实世界中事物特征的。任何一个模型都不可能反映客观事物的一切具体特征,只能对事物特征和变化规律的一种抽象,且在它所涉及的范围内更普遍、更集中、更深刻地描述客体的特征。
二、AOP(Aspect Oriented Programming)切面编程
AOP是OOP的补充,类是OOP的最小单位,那么当每次需要新增功能或者修改功能的时候,必定要改动到类。那么这时候有一种场景是记录所有操作的日志。那么是不是需要在所有的业务类编写记录操作日志呢?这样类的封装是不是破坏了呢?如果后面有N种这样的需求进来,那整个上层类封装就需要修改N次,实际上这个架构已经崩了。那么能不能实现一种方案是即不修改类封装,又能拓展新功能呢?AOP的思想也就孕运而生了。比如将日志记录,性能统计,安全控制,事务处理,异常处理等。代码从业务逻辑代码中划分出来,通过对这些行为的分离,可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。从而不破坏原有架构。
三、GOF –代理模式
代理模式是一种对象结构型模式。引入了一个新的代理对象,代理对象在客户端对象和目标对象之间起到中介的作用,它去掉客户不能看到的内容和服务或者增添客户需要的额外的新服务。为其他对象提供一种代理以控制对这个对象的访问。
- 静态代理,将需要拓展的功能写死到一个代理类中。
public interface IDBHelper
{
void show();
}
public class MySqlDBHelper: IDBHelper
{
public void show()
{
Console.WriteLine("I am Mysql");
}
}
public class OracleDBHelper: IDBHelper
{
public void show()
{
Console.WriteLine("I am Oracle.");
}
}
public class SqlServerDBHelper: IDBHelper
{
public void show()
{
Console.Write("I am sqlserver.");
}
}
如上代码,三个类实现同一个抽象,如果这个时候我想在show 之前统一加一个Log记录时间,show之后再加一个Log记录时间。这个时候一般的oop是不是要把三个Helper类的show方法都得修改一遍?
显然这样的方式不太可行,开闭原则让我们对拓展开放,对修改关闭。那么这个时候就可以引入一个代理类来统一处理这个事情。
public class DBHelperProxy:IDBHelper
{
private IDBHelper _dbHelper { get; set; }
public DBHelperProxy(IDBHelper dbHelper)
{
_dbHelper= dbHelper;
}
public void BeforeShow()
{
Console.WriteLine("Before Show");
}
public void show()
{
BeforeShow();
_dbHelper.show();
AfterShow();
}
public void AfterShow()
{
Console.WriteLine("AfterShow Show");
}
}
如上代码,新创建一个代理类后,是不是可以不用修改原来的业务逻辑类了,只需要调用我这个代理类,就能实现在show之前和之后做一些统一的业务。这就是对拓展开放啊。但是静态代理有个缺陷就是写死的,它只能为构造方法中传进来的一种业务类型做拓展,而不能对所有的业务类型做拓展。那么随着业务类型的增加,代理类就没完没了了。
- 动态代理:通过反射动态生成代理类,实现任何类都可以通过动态代理实现业务拓展。
netcore 已经为我们完成了一些工作,提供了DispatchProxy 这个类,Create 创建代理的实例对象,实例对象在调用方法时候会自动执行Invoke。
public abstract class DispatchProxy
{
//
protected DispatchProxy();
//
// 类型参数:
// T:
//
// TProxy:
public static T Create<T, TProxy>() where TProxy : DispatchProxy;
//
// 参数:
// targetMethod:
//
// args:
protected abstract object Invoke(MethodInfo targetMethod, object[] args);
}
public class DynamicProxy<T> : DispatchProxy
{
public T _decorated;
private event Action<MethodInfo, object[]> _afterAction; //动作之后执行
private event Action<MethodInfo, object[]> _beforeAction; //动作之前执行
/// <summary>
/// 创建代理实例
/// </summary>
/// <param name="decorated">代理的接口类型</param>
/// <param name="beforeAction">方法执行前执行的事件</param>
/// <param name="afterAction">方法执行后执行的事件</param>
/// <returns></returns>
public T Create(T decorated, Action<MethodInfo, object[]> beforeAction, Action<MethodInfo, object[]> afterAction)
{
object proxy = Create<T, DynamicProxy<T>>(); //调用DispatchProxy 的Create 创建一个新的T
((DynamicProxy<T>)proxy)._decorated = decorated;
((DynamicProxy<T>)proxy)._afterAction = afterAction;
((DynamicProxy<T>)proxy)._beforeAction = beforeAction;
return (T)proxy;
}
/// <summary>
///
/// </summary>
/// <param name="targetMethod"></param>
/// <param name="args"></param>
/// <returns></returns>
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
if (_beforeAction != null)
{
this._beforeAction(targetMethod, args);
}
object result = targetMethod.Invoke(_decorated, args);
if (_afterAction != null)
{
this._afterAction(targetMethod, args);
}
return result;
}
}
如上代码,先动态Create一个外部传进来的任意类型和执行前执行后的委托方法,创建完实例后执行Invoke方法。这样就避免了一个业务实现一个业务代理类的麻烦了,通用一个。以上就是代理模式。
四、总结
通过上面静态代理和动态代理的代码逻辑,基本上知道了AOP是在干什么了。
- 它弥补了OOP的不足,减少了重复代码的编写
- 方便拓展,可以轻松拓展更多需求,比如鉴权,缓存,异常等。
- 聚焦业务逻辑,开发人员不需要再去关注一些非业务功能。
- 集中维护通用模块。
原创文章,作者:1402239773,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/271442.html