【ASP.NET Core 3.1_参考中间件源码实现自定义AOP】
一.参考中间件源码实现自定义AOP
场景:在真实执行逻辑学习这个方法包一层层(学习前吃点东西、上个厕所),模拟中间件,一层层穿过
思路:对象构造完,加1个动态代理,基于Castle、组装委托,来个AOP扩展,像一个俄罗斯套娃
二.AOP扩展方法-ContainerAOPExtensions.cs
2.1.Nuget引入Castle.Core.dll
2.2.实现扩展方法,目的把服务包一层代理
/// <summary>
/// 自定义AOP扩展
/// </summary>
public static class ContainerAOPExtensions
{
/// <summary>
///
/// </summary>
/// <param name="t">对象</param>
/// <param name="interfaceType">接口类型</param>
/// <returns></returns>
public static object AOP(this object t, Type interfaceType)
{
ProxyGenerator generator = new ProxyGenerator();
ProxyInterceptor interceptor = new ProxyInterceptor();
t = generator.CreateInterfaceProxyWithTarget(interfaceType, t, interceptor);
return t;
}
}
三.自定义拦截属性
3.1.基类
public abstract class BaseInterceptorAttribute : Attribute
{
public abstract Action Do(IInvocation invocation, Action action);
}
3.2.吃点东西拦截属性
/// <summary>
/// 学习前吃点东西
/// </summary>
public class EatInterceptorAttribute : BaseInterceptorAttribute
{
public override Action Do(IInvocation invocation, Action action)
{
return () =>
{
Console.WriteLine($"This is Eat1 {invocation.Method.Name} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")}");
//去执行真实逻辑
action.Invoke();
Console.WriteLine($"This is Eat2 {invocation.Method.Name} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")}");
};
}
}
3.3.上个厕所拦截属性
/// <summary>
/// 学习前上个厕所
/// </summary>
public class GoToiletInterceptorAttribute : BaseInterceptorAttribute
{
public override Action Do(IInvocation invocation, Action action)
{
return () =>
{
Console.WriteLine($"This is GoToilet1 {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")}");
//去执行真实逻辑
action.Invoke();
Console.WriteLine($"This is GoToilet2 {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")}");
};
}
}
四.拦截器-ProxyInterceptor.cs
/// <summary>
/// 拦截器
/// </summary>
public class ProxyInterceptor : StandardInterceptor
{
/// <summary>
/// 调用前的拦截器
/// </summary>
/// <param name="invocation"></param>
protected override void PreProceed(IInvocation invocation)
{
Console.WriteLine($"调用前的拦截器 方法名是:{invocation.Method.Name} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")}");
}
/// <summary>
/// 拦截的方法返回时调用的拦截器
/// </summary>
/// <param name="invocation"></param>
protected override void PerformProceed(IInvocation invocation)
{
var method = invocation.Method;
//真实逻辑包成委托--Study()--对应学生服务里的‘我要学习了’
Action action = () => base.PerformProceed(invocation);
if (method.IsDefined(typeof(BaseInterceptorAttribute), true))
{
foreach (var attribute in method.GetCustomAttributes<BaseInterceptorAttribute>().ToArray().Reverse())
{
//组装、再组装委托
action = attribute.Do(invocation, action);
}
}
//最后一次性执行委托
action.Invoke();
}
/// <summary>
/// 调用后的拦截器
/// </summary>
/// <param name="invocation"></param>
protected override void PostProceed(IInvocation invocation)
{
Console.WriteLine($"调用后的拦截器 方法名是:{invocation.Method.Name} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")}");
}
}
五.学生接口、学生服务
5.1.IStudentService.cs
在接口上面加上自定义拦截属性
/// <summary>
/// 学生接口
/// </summary>
public interface IStudentService
{
/// <summary>
/// 学习
/// </summary>
[EatInterceptor]
[GoToiletInterceptor]
public void Study();
}
5.2.StudentService.cs
Study()是真实逻辑
/// <summary>
/// 学生服务
/// </summary>
public class StudentService : IStudentService
{
/// <summary>
/// 学习
/// </summary>
public void Study()
{
Console.WriteLine("我要学习了!");
}
}
六.上端调用
public class TestCustomAOPController : Controller
{
//学生服务接口
private IStudentService _iStudentService = null;
public TestCustomAOPController(IStudentService iStudentService)
{
_iStudentService = iStudentService;
}
public IActionResult Index()
{
Console.WriteLine("******************普通方法调用*********************");
_iStudentService.Study();
Console.WriteLine("*******************AOP扩展后********************");
_iStudentService = (IStudentService)_iStudentService.AOP(typeof(IStudentService));
_iStudentService.Study();
return View();
}
}
七.项目结构
八.执行效果
总结:把真实逻辑Study()方法先不执行,先包成委托传入GoToilet,GoToilet里面对委托组装后返回一个委托,再次把返回的委托再次传入Eat,Eat里面对委托再次组装后再返回一个委托,最后一次性执行委托
委托传递,组装、再组装、最后一次性执行委托,这其实就是我对ASP.NET Core Middleware中间件的理解
旧书不厌百回读,熟读深思子自知。