反復的なタスクにアスペクト指向プログラミングを利用しようとしています。懸念事項を分離する方法がわかりません。私は C# を使用しており、AOP には Castle.DynamicProxy (Autofac の InterceptedBy 機能を使用) を使用していますが、この質問への回答が、他の AOP ソリューションにも適用するのに十分な一般的なアドバイスになることを願っています (おそらくあなたは納得できるでしょう)。別の AOP ソリューションに切り替える必要があります)。
たとえば、クラスへのすべてのメソッド呼び出しをインターセプトする次のインターセプターのようなものがあります。メソッドが呼び出されたとき、(1) 呼び出しにかかった時間を測定し、(2) 呼び出される前後のメソッドの名前をログに記録します。
public class TimeLoggingInterceptor : IInterceptor
{
private ILog m_Log;
public TimeLoggingInterceptor(ILog log)
{
m_Log = log;
}
public void Intercept(IInvocation invocation)
{
// Logging concerns
string fullMethodName = invocation.TargetType.Name + "." + invocation.MethodInvocationTarget.Name;
m_Log.Debug(fullMethodName + " started.");
// Timing concerns
DateTime beforeStamp = DateTime.UtcNow;
// Call method
invocation.Proceed();
// Timing concerns
DateTime afterStamp = DateTime.UtcNow;
TimeSpan callTime = afterStamp - beforeStamp;
// Logging concerns
m_Log.Debug(fullMethodName + " finished. Took " + callTime.TotalMilliseconds + "ms.");
}
}
ここでのタイミングの問題 (メソッド呼び出しにかかった時間の測定) は、ロギングの問題 (ログ ファイルへの書き込み) とは別のものにすべきだと思います。私はこのようなことを考えていますが、順序付けにどのようにアプローチするか、またはcallTime変数をどこに置くべきかわかりません:
public class TimingInterceptor : IInterceptor
{
private static ThreadLocal<TimeSpan> callTime = new ThreadLocal<TimeSpan>();
public static TimeSpan CallTime
{
get
{
if (!callTime.IsValueCreated) throw new InvalidOperationException("callTime was never set");
return callTime.Value;
}
}
public void Intercept(IInvocation invocation)
{
// Timing concerns
DateTime beforeStamp = DateTime.UtcNow;
// Call method
invocation.Proceed();
// Timing concerns
DateTime afterStamp = DateTime.UtcNow;
callTime.Value = afterStamp - beforeStamp;
}
}
public class LoggingInterceptor : IInterceptor
{
private ILog m_Log;
public LoggingInterceptor(ILog log)
{
m_Log = log;
}
public void Intercept(IInvocation invocation)
{
// Logging concerns
string fullMethodName = invocation.TargetType.Name + "." + invocation.MethodInvocationTarget.Name;
m_Log.Debug(fullMethodName + " started.");
// Call method
invocation.Proceed();
// Logging concerns
m_Log.Debug(fullMethodName + " finished. Took " + TimingInterceptor.CallTime.TotalMilliseconds + "ms.");
}
}
ここで基本的に私が考えているのは、何らかの方法でTimingInterceptorがメソッドを直接インターセプトする必要があり、次にLoggingInterceptorがそれをラップする必要があるということです。
これらの懸念が正しい順序で発生することを確認するために、人々はどのようなアプローチを使用しますか? LoggingInterceptor が TimingInterceptor の Intercept メソッドをインターセプトするようにして、インターセプターをチェーンしますか? [InterceptOrder(1|2|3|...)]
それとも、インターセプター クラスに何らかの属性を設定する必要がありますか? または[InterceptAfter(typeof(TimingInterceptor))]
、LoggingInterceptor のようなものを配置できますか?
また、スレッドローカル変数を使用して呼び出し時間を追跡するより良い方法はありますか? はい、これをスレッドセーフにしたいと思います。この変数をスタックに保持することが望ましいと考えていますが、LoggingInterceptor がカップリングをあまり導入せずに TimingInterceptor へのハンドルを取得する方法がわかりません (LoggingInterceptor を再コンパイルせずに TimingInterceptor の実装を切り替えられるとよいでしょう)。 .