淺析DispatchProxy動態代理AOP(程式碼原始碼)
最近學習了一段時間Java,瞭解到Java實現動態代理
AOP
主要分為兩種方式JDK
、CGLIB
,我之前使用NET
實現AOP
切面程式設計,會用Filter攔截器
、Attribute特性
、中介軟體
、繼承父類重寫父類方法
。
經過查詢資料接觸到了(牛逼不分先後)
DispatchProxy類介紹
DispatchProxy原始碼,主要使用了Emit
類庫直接編寫IL
語言,動態生成類,和方法,然後會呼叫Invoke
方法(效能很高,幾乎和我們寫的C#編譯成IL沒有區別),我們繼承了DispatchProxy
抽象類,重寫Invoke
方法,具體實現就可以自定義了。
DispatchProxy原始碼
Activator
類在執行時可以動態構造物件AssemblyBuilder
類在執行時可以動態獲取和設定專用欄位的屬性、初始化專用欄位的建構函式,動態執行方法並返回結果。
// 動態代理生成類
internal static class DispatchProxyGenerator
{
// 動態代理容器(儲存已經生成過的代理)
private static readonly Dictionary<Type, Dictionary<Type, Type>> s_baseTypeAndInterfaceToGeneratedProxyType = new Dictionary<Type, Dictionary<Type, Type>>();
private static readonly ProxyAssembly s_proxyAssembly = new ProxyAssembly();
private static readonly MethodInfo s_dispatchProxyInvokeMethod = typeof(DispatchProxy).GetTypeInfo().GetDeclaredMethod("Invoke");
// 返回派生自'baseType'的代理的新例項,並實現'interfaceType'
internal static object CreateProxyInstance(Type baseType, Type interfaceType)
{
Debug.Assert(baseType != null);
Debug.Assert(interfaceType != null);
// 獲取代理型別
Type proxiedType = GetProxyType(baseType, interfaceType);
// 建立例項
return Activator.CreateInstance(proxiedType, (Action<object[]>)DispatchProxyGenerator.Invoke);
}
// 首先從代理容器中獲取,如果沒有進行建立
private static Type GetProxyType(Type baseType, Type interfaceType)
{
// 鎖住容器
lock (s_baseTypeAndInterfaceToGeneratedProxyType)
{
Dictionary<Type, Type> interfaceToProxy = null;
// 判斷baseType實現類型別容器中是否存在,不存在先初始化一個
if (!s_baseTypeAndInterfaceToGeneratedProxyType.TryGetValue(baseType, out interfaceToProxy))
{
interfaceToProxy = new Dictionary<Type, Type>();
s_baseTypeAndInterfaceToGeneratedProxyType[baseType] = interfaceToProxy;
}
Type generatedProxy = null;
// 判斷是否存在interfaceType介面型別代理類,不存在就建立一個
if (!interfaceToProxy.TryGetValue(interfaceType, out generatedProxy))
{
generatedProxy = GenerateProxyType(baseType, interfaceType);
interfaceToProxy[interfaceType] = generatedProxy;
}
return generatedProxy;
}
}
// 生成一個派生自'baseType'的新代理型別,並實現'interfaceType'
private static Type GenerateProxyType(Type baseType, Type interfaceType)
{
TypeInfo baseTypeInfo = baseType.GetTypeInfo();
// 介面型別必須是介面,而不是類
if (!interfaceType.GetTypeInfo().IsInterface)
{
throw new ArgumentException(SR.Format(SR.InterfaceType_Must_Be_Interface, interfaceType.FullName), "T");
}
// 基型別不能密封,因為代理需要將其子類化。
if (baseTypeInfo.IsSealed)
{
throw new ArgumentException(SR.Format(SR.BaseType_Cannot_Be_Sealed, baseTypeInfo.FullName), "TProxy");
}
// 基型別不能是抽象型別
if (baseTypeInfo.IsAbstract)
{
throw new ArgumentException(SR.Format(SR.BaseType_Cannot_Be_Abstract, baseType.FullName), "TProxy");
}
// 基型別必須有一個公共預設屬性(不然沒啥意義)
if (!baseTypeInfo.DeclaredConstructors.Any(c => c.IsPublic && c.GetParameters().Length == 0))
{
throw new ArgumentException(SR.Format(SR.BaseType_Must_Have_Default_Ctor, baseType.FullName), "TProxy");
}
// 建立baseType類
ProxyBuilder pb = s_proxyAssembly.CreateProxy("generatedProxy", baseType);
// 獲取介面中需要實現的資訊,動態新增實現
foreach (Type t in interfaceType.GetTypeInfo().ImplementedInterfaces)
pb.AddInterfaceImpl(t);
// 新增實現
pb.AddInterfaceImpl(interfaceType);
// 實現完介面,建立該型別
Type generatedProxyType = pb.CreateType();
return generatedProxyType;
}
// 呼叫(抽象的)DispatchProxy.Invoke()方法。
private static void Invoke(object[] args)
{
PackedArgs packed = new PackedArgs(args);
MethodBase method = s_proxyAssembly.ResolveMethodToken(packed.DeclaringType, packed.MethodToken);
// 方法是否泛型方法定義
if (method.IsGenericMethodDefinition)
// 建立泛型方法定義
method = ((MethodInfo)method).MakeGenericMethod(packed.GenericTypes);
// 呼叫(抽象的)DispatchProxy.Invoke()方法
try
{
Debug.Assert(s_dispatchProxyInvokeMethod != null);
// 執行packed.DispatchProxy該類方法,獲取方法的返回結果
object returnValue = s_dispatchProxyInvokeMethod.Invoke(packed.DispatchProxy,
new object[] { method, packed.Args });
// 執行返回結果
packed.ReturnValue = returnValue;
}
catch (TargetInvocationException tie)
{
ExceptionDispatchInfo.Capture(tie.InnerException).Throw();
}
}
DispatchProxy擴充封裝
根據前面的介紹,對DispatchProxy
有了一定的瞭解,我們可以進行一些封裝方便以後使用。
DynamicProxy動態代理類
我們建立DynamicProxy
類,包裝方法執行之前&執行之後的處理,主體方法報錯的處理,形成一個動態代理類。
public class DynamicProxy : DispatchProxy, IScopedDependency
{
private static ILogger<DynamicProxy>? _logger { get; set; }
/// <summary>
/// 目標類
/// </summary>
public object Target { get; set; }
/// <summary>
/// 動作之後執行
/// </summary>
private Action<object?[]?> _afterAction { get; set; }
/// <summary>
/// 動作之前執行
/// </summary>
private Action<object?[]?, object> _beforeAction { get; set; }
/// <summary>
/// 目標方法異常(預設丟擲異常資訊)
/// </summary>
private Action<MethodInfo?, object?[]?, Exception> _methodExceptionAction { get; set; } = (methodInfo, args, ex) => throw ex;
/// <summary>
/// 執行方法
/// </summary>
/// <param name="targetMethod">目標方法</param>
/// <param name="args">方法引數</param>
/// <returns></returns>
protected override object? Invoke(MethodInfo? targetMethod, object?[]? args)
{
// 異常資訊
Exception exception = null;
// 方法執行前處理
AfterAction(args);
// 方法執行結果
object resultValue = null;
if (targetMethod != null)
{
try
{
//呼叫實際目標物件的方法
resultValue = targetMethod.Invoke(Target, args);
}
catch (Exception ex)
{
_logger.LogError($"Invoke=>呼叫實際目標物件的方法出現錯誤:{ex.Message},{ex.StackTrace}");
_methodExceptionAction(targetMethod, args, ex);
}
}
// 方法執行後處理
BeforeAction(args, resultValue);
// 判斷主體方法執行是否異常
if (exception != null)
{
throw exception;
}
return resultValue;
}
/// <summary>
/// 建立代理例項
/// </summary>
/// <param name="target">代理的介面型別</param>
/// <param name="afterAction">方法執行前執行的事件</param>
/// <param name="beforeAction">方法執行後執行的事件</param>
/// <returns></returns>
public T Create<T>(T target,
Action<object?[]?> afterAction,
Action<object?[]?, object> beforeAction,
Action<MethodInfo?, object?[]?, Exception> targetMethodExceptionAction,
ILogger<DynamicProxy> logger)
{
// DispatchProxy.Create建立T物件
object proxy = Create<T, DynamicProxy>();
_logger = logger;
DynamicProxy proxyDecorator = (DynamicProxy)proxy;
proxyDecorator.Target = target;
proxyDecorator._afterAction = afterAction;
proxyDecorator._beforeAction = beforeAction;
proxyDecorator._methodExceptionAction = targetMethodExceptionAction;
return (T)proxy;
}
private void AfterAction(object?[]? args)
{
if (_afterAction == null)
{
return;
}
try
{
_afterAction.Invoke(args);
}
catch (Exception ex)
{
_logger.LogError($"AfterAction=>執行之前異常:{ex.Message},{ex.StackTrace}");
}
}
private void BeforeAction(object?[]? args, object? result)
{
if (_beforeAction == null)
{
return;
}
try
{
_beforeAction.Invoke(args, result);
}
catch (Exception ex)
{
_logger.LogError($"BeforeAction=>執行之後異常:{ex.Message},{ex.StackTrace}");
}
}
}
建立IProxyHandle
我們會有很多處理實現類,每個實現類都需要實現AfterAction
、BeforeAction
、TargetMethodExceptionAction
public interface IProxyHandle
{
/// <summary>
/// 執行之前
/// </summary>
/// <param name="args">目標方法引數</param>
void AfterAction(object?[]? args);
/// <summary>
/// 執行之後
/// </summary>
/// <param name="args">目標方法引數</param>
/// <param name="result">目標方法執行結果</param>
void BeforeAction(object?[]? args, object resultValue);
/// <summary>
/// 方法執行錯誤處理
/// </summary>
/// <param name="targetMethod">目標方法</param>
/// <param name="args">目標方法引數</param>
/// <param name="ex">目標方法執行結果</param>
void MethodExceptionAction(MethodInfo? targetMethod, object?[]? args, Exception ex);
}
建立ProxyHandleAttribute特性
這裡我嘗試過幾種方式,通過介面或者使用特性,最終特性比較方便團隊使用(介面也不錯)。
/// <summary>
/// 代理Aop特性
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class ProxyHandleAttribute : Attribute
{
public Type Type { get; set; }
public ProxyHandleAttribute(Type type)
{
this.Type = type;
}
}
ProxyFactory代理工廠
根據實現類獲取Aop切面型別,再獲取AOP工廠(
IEnumerable<IProxyHandle>
)中對應實現。
/// <summary>
/// 代理工廠
/// </summary>
public class ProxyFactory : IScopedDependency
{
private readonly IServiceProvider _serviceProvider;
private readonly IEnumerable<IProxyHandle> _proxyHandleList;
private readonly DynamicProxy _dynamicProxy;
private readonly ILogger<DynamicProxy> _logger;
public ProxyFactory(IEnumerable<IProxyHandle> proxyHandleList,
IServiceProvider serviceProvider,
DynamicProxy dynamicProxy,
ILogger<DynamicProxy> logger)
{
this._serviceProvider = serviceProvider;
this._proxyHandleList = proxyHandleList;
this._dynamicProxy = dynamicProxy;
this._logger = logger;
}
/// <summary>
/// 建立代理例項
/// </summary>
/// <returns></returns>
public T Create<T>() where T : class
{
var target = _serviceProvider.GetService<T>();
if (target == null)
{
throw new BusinessException($"執行ProxyFactory=》Create方法:{typeof(T).FullName}未注入");
}
var type = target.GetType();
var proxyHandleAttribute = type.GetCustomAttribute<ProxyHandleAttribute>();
if (proxyHandleAttribute == null)
{
throw new BusinessException($"執行ProxyFactory=》Create方法:{type.FullName}需要新增ProxyHandleAttribute特性");
}
var proxyHandle = _proxyHandleList.FirstOrDefault(x => x.GetType() == proxyHandleAttribute.Type);
if (proxyHandleAttribute == null)
{
throw new BusinessException($"執行ProxyFactory=》Create方法:沒有找到對應IProxyHandle介面實現");
}
//建立代理類
var proxy = _dynamicProxy.Create(target,
proxyHandle.AfterAction,
proxyHandle.BeforeAction,
proxyHandle.MethodExceptionAction,
_logger);
return proxy;
}
}
DynamicProxy使用示例
IProxyHandle
實現類(ProxyHandleTest
)
public class ProxyHandleTest : IProxyHandle
{
public void AfterAction(object[] args)
{
Console.WriteLine($"ProxyHandleTest=》AfterAction方法執行,args:{JsonSerializer.Serialize(args)}");
}
public void BeforeAction(object[] args, object resultValue)
{
Console.WriteLine($"ProxyHandleTest=》BeforeAction方法執行,args:{JsonSerializer.Serialize(args)},result:{resultValue}");
}
public void MethodExceptionAction(MethodInfo targetMethod, object[] args, Exception ex)
{
Console.WriteLine($"ProxyHandleTest=》MethodExceptionAction方法執行,targetMethod,:{targetMethod.Name},args:{JsonSerializer.Serialize(args)},ex:{ex.Message}");
}
}
- 準備
ITestService
、TestService
public interface ITestService
{
/// <summary>
/// 獲取使用者Id資訊
/// </summary>
/// <returns></returns>
int GetUserId();
void SetUserId(int userId);
}
[ProxyHandle(typeof(ProxyHandleTest))]
public class TestService : ITestService, IScopedDependency
{
public int GetUserId()
{
Console.WriteLine($"執行TestService=>GetUserId()");
return 10;
}
public void SetUserId(int userId)
{
Console.WriteLine($"執行TestService=>SetUserId({userId})");
throw new Exception("執行TestService=>SetUserId測試異常");
}
}
- 最後只需要注入
ProxyFactory
public class TestController : AbpController
{
private readonly ProxyFactory _proxyFactory;
public TestController(ProxyFactory proxyFactory)
{
_proxyFactory = proxyFactory;
}
// GET: api/<TestController>
[HttpGet]
public IEnumerable<string> Get()
{
// 從工廠獲取代理類
var testService = _proxyFactory.Create<ITestService>();
testService.GetUserId();
return new string[] { "value1", "value2" };
}
總結
DispatchProxy
實現動態代理主要是依賴Emit
類庫直接編寫IL
語言。Activator
類在執行時動態構造物件。AssemblyBuilder
類執行時動態一個獲取和設定專用欄位的屬性、初始化專用欄位的建構函式,可以動態執行方法並返回結果。
不管是Java
還是Net
實現AOP切面程式設計原理差不多。