更新日期:2019年9月21日。
Github源码:​​​[点我获取源码]​​​ Gitee源码:​​[点我获取源码]​

索引

  • ​​AspectTrack模块简介​​
  • ​​使用AspectTrack​​
  • ​​自定义切面代理​​
  • ​​实现切面代理​​
  • ​​常规调用​​
  • ​​切面代理调用​​
  • ​​修改方法实参​​
  • ​​获取方法返回值​​
  • ​​拦截方法调用​​
  • ​​运行时检视面板​​

AspectTrack模块简介

AspectTrack模块主要是根据AOP思想架构的一个面向切面的程序代码追踪模块,它可以跟踪每一个方法的调用,在调用前拦截该方法,自定义拦截规则,亦或是更改其传入的实参,更改其返回值等!可以用于调用日志打印,系统运行监控等需求!

使用AspectTrack

自定义切面代理

要使用AspectTrack,首先必须新建自定义的切面代理,切面代理必须满足以下条件:

1.继承至 AspectProxyBase

2.扩展接口 IAspectTrackObject

介于过程较繁琐,推荐使用快捷创建方式:

Project界面右键 -> Create -> HTFramework -> C# AspectProxy Script

【Unity】 HTFramework框架(一) AspectTrack面向切面的动态调试模块_自定义


如下图,我自定义了一个名为PlayerAttackProxy的切面代理,IPlayerAttackProxy接口为其代理的所有方法:

/// <summary>
/// 新建切面代理者
/// </summary>
public class PlayerAttackProxy<T> : AspectProxyBase<T> where T : IPlayerAttackProxy
{
public PlayerAttackProxy(T realObject) : base(realObject) { }

protected override object[] OnBeforeInvoke(MethodBase method, object[] args)
{
GlobalTools.LogWarning("进入方法:" + method.Name);
return args;
}

protected override void OnAfterInvoke(MethodBase method, object returnValue)
{
GlobalTools.LogWarning("离开方法:" + method.Name);
}
}

public interface IPlayerAttackProxy : IAspectTrackObject
{
void Attack(int power);
void Hit(int hurt);
}

实现切面代理

我们新建一个类Player,使其实现IPlayerAttackProxy接口:

public class Player : MonoBehaviour, IPlayerAttackProxy
{
int code;

private void Awake()
{
code = GetHashCode();
}

public void Attack(int power)
{
GlobalTools.LogInfo(code + " 攻击:[攻击力 " + power + "]");
}

public void Hit(int hurt)
{
GlobalTools.LogInfo(code + " 被击:[伤害 " + hurt + "]");
}

public void Track()
{
GlobalTools.LogInfo(" 追踪目标:" + GetType().Name + " " + code);
}

private void Start()
{
Attack(100);
Hit(50);
Track();
}
}

常规调用

然后我们在场景中挂载Player脚本,运行!结果如我们所料:

【Unity】 HTFramework框架(一) AspectTrack面向切面的动态调试模块_ide_02

切面代理调用

接下来我们想要对PlayerAttack方法和Hit方法进行追踪,当然这也是我们一开始的初衷,修改Player类的Start方法:

private void Start()
{
//创建代理者
Main.m_AspectTrack.CreateProxyer(new PlayerAttackProxy<IPlayerAttackProxy>(this));
//获取代理对象
IPlayerAttackProxy player = Main.m_AspectTrack.GetProxyObject<IPlayerAttackProxy>(this);

player.Attack(100);
player.Hit(50);
player.Track();
}

在检视面板启用Track。

【Unity】 HTFramework框架(一) AspectTrack面向切面的动态调试模块_ide_03

然后我们在场景中挂载Player脚本,运行!结果如你所料:

【Unity】 HTFramework框架(一) AspectTrack面向切面的动态调试模块_自定义_04


很明显的是,Player中的Attack方法和Hit方法,每次调用都将会被PlayerAttackProxy捕获到,BeforeInvoke将于方法正式调用前被呼叫,AfterInvoke将于方法正式调用后被呼叫。

修改方法实参

想要修改方法调用时传入的实参,只需修改BeforeInvoke传入的参数并传出便可:

protected override object[] OnBeforeInvoke(MethodBase method, object[] args)
{
GlobalTools.LogWarning("进入方法:" + method.Name);
//将所有int数据放大到10000
for (int i = 0; i < args.Length; i++)
{
if (args[i] is int)
{
args[i] = 10000;
}
}

return args;
}

运行!

【Unity】 HTFramework框架(一) AspectTrack面向切面的动态调试模块_Unity_05

获取方法返回值

方法的返回值可以从AfterInvoke中获取:

protected override void OnAfterInvoke(MethodBase method, object returnValue)
{
GlobalTools.LogWarning("离开方法:" + method.Name);
GlobalTools.LogInfo("方法:" + method.Name + " 返回值 " + (returnValue == null ? "null" : returnValue.ToString()));
}

运行!

【Unity】 HTFramework框架(一) AspectTrack面向切面的动态调试模块_自定义_06

拦截方法调用

可以自定义任意拦截规则,以阻断任何满足规则的方法的调用,不过需要注意的是,这里只能拦截void返回值的方法,如若方法有非空返回值,那么拦截了它的调用之后,将不太好处理之后引发的一系列麻烦。

先在检视面板启用Intercept。

【Unity】 HTFramework框架(一) AspectTrack面向切面的动态调试模块_Unity_07


然后我们新增一个拦截规则,在Start中加入如下代码:

private void Start()
{
//新增拦截规则
Main.m_AspectTrack.AddInterceptCondition("拦截所有名为Attack的方法", (method, args) =>
{
//返回true,则确认拦截
return method.Name == "Attack";
});


//创建代理者
Main.m_AspectTrack.CreateProxyer(new PlayerAttackProxy<IPlayerAttackProxy>(this));
//获取代理对象
IPlayerAttackProxy player = Main.m_AspectTrack.GetProxyObject<IPlayerAttackProxy>(this);

player.Attack(100);
player.Hit(50);
player.Track();
}

运行!很明显,没有Attack方法的打印记录,任何地方任何时候将无法再进入Attack方法,除非移除拦截规则。

【Unity】 HTFramework框架(一) AspectTrack面向切面的动态调试模块_AOP_08

运行时检视面板

在编辑器中运行时将会出现运行时检视面板(Runtime Data),主要用以调试或数据监测,目前面板如下:

【Unity】 HTFramework框架(一) AspectTrack面向切面的动态调试模块_AOP_09


1.Intercept Conditions:当前存在的拦截规则(显示唯一标识名称)。