注:ここで私自身の質問に答え、loddar2012によって提案されたソリューションにさらに情報とパラメーター化の追加機能を追加します。彼の答えが私を正しい方向に導いたので、ここでのこの答えは、(自分自身を引用して)など、元の質問からの私のすべてのニーズに本当に対応しているにもかかわらず、私はそれを受け入れるつもりです。
ただし、いくつかのバリエーションがあります。ポイントカットとアドバイスには、ログに出力されるargまたはこのパラメーターが含まれる場合があります。他の多くの呼び出しをラップしていない単なるマイナーな呼び出しである場合、「完了」メッセージが出力されないことがあります
ここで扱っている基本的なことは、ラムニヴァス・ラダッドが彼の著書AspectJinActionでワーカーオブジェクトパターンと呼んでいるものです。彼の(そしてloddar2012の)アイデアは、単純な散文です
- 呼び出しを匿名クラス(ワーカーオブジェクト)のインスタンスにラップするには、
- 基本クラスまたは実装されたインターフェースは、作業を行うことを目的としたメソッドを提供します。
- ワーカーオブジェクトは、ワーカーメソッドの具体的な実装を提供し、具体的には
proceed()
その中で呼び出します。
- ワーカーメソッドは、オブジェクトの作成直後(ここで行うように)または後で、場合によっては独自のスレッドで呼び出すことができます。
- ワーカーオブジェクトは、渡されたり、スケジューリングキューに追加されたりする場合があります(ここでは必要ありません)。
proceed()
呼び出しを非同期で実行する必要がある場合の洗練されたソリューションは、匿名Runnable
クラスのインスタンスを作成することです。ただし、お茶に砂糖をもう少し入れたいので、独自の抽象基本クラスを使用LogHelper
します。具体的には、ログメッセージと、各ワーカーへのログ出力に影響を与えるその他のパラメーターを渡すオプションです。だからこれは私がしたことです(パッケージ名とインポートはサンプルコードに示されていません):
抽象ワーカー基本クラス:
abstract class LogHelper {
// Object state needed for logging
String message;
boolean logDone;
boolean indent;
LogType type;
// Main constructor
LogHelper(String message, boolean logDone, boolean indent, LogType type) {
this.message = message;
this.logDone = logDone;
this.indent = indent;
this.type = type;
}
// Convenience constructors for frequent use cases
LogHelper(String message, boolean logDone) {
this(message, logDone, true, LogType.VERBOSE);
}
LogHelper(String message) {
this(message, true);
}
// Worker method to be overridden by each anonymous subclass
abstract void log();
}
ワーカーオブジェクトの実行をキャプチャするログアドバイス:
aspect LoggingAspect
{
void around(LogHelper logHelper) :
execution(* LogHelper.log()) && this(logHelper)
{
try {
SimpleLogger.log(logHelper.type, logHelper.message);
if (logHelper.indent)
SimpleLogger.indent();
proceed(logHelper);
} finally {
if (logHelper.indent)
SimpleLogger.dedent();
if (logHelper.logDone)
SimpleLogger.log(logHelper.type, logHelper.message + " - done");
}
}
// (...)
}
ご覧のとおり、ロギングアドバイスは、呼び出し前proceed(logHelper)
(つまり、ワーカーオブジェクトのlog()
メソッドの実行)にいくつかのことを行い、その後、ワーカーオブジェクト内に格納されている状態情報を使用していくつかのことを行います。
- ログに記録されるメッセージ、
- ログレベル(ここでは「タイプ」と呼ばれます)、
- 続行する前にインデントレベルを上げる必要があるかどうかを指定するフラグ、
- ワーカーの実行後に「done」メッセージを出力するかどうかを指定するフラグ。
私のユースケースでは、ログに記録されたすべてのメソッドが返されるため、void
戻り値の受け渡しを実装する必要はありませんが、必要に応じて簡単に実行できます。その場合、アドバイスの戻り値はそのままになりObject
、結果をproceed()
呼び出し元に返します。大したことはありません。
ログに記録される参加ポイントをキャプチャし、パラメータ化されたワーカーオブジェクトを利用して作業を完了するためのアドバイス:
aspect LoggingAspect
{
// (...)
pointcut processBook() : execution(* OpenbookCleaner.downloadAndCleanBook(Book));
pointcut download() : execution(* Downloader.download());
pointcut cleanBook() : execution(* OpenbookCleaner.cleanBook(Book));
pointcut cleanChapter() : execution(* OpenbookCleaner.cleanChapter(Book, File));
pointcut initialiseTitle() : execution(* *Filter.initialiseTitle(boolean));
void around(final Book book) : processBook() && args(book) {
new LogHelper("Book: " + book.unpackDirectory) {
void log() { proceed(book); } }.log();
}
void around() : download() {
new LogHelper("Downloading, verifying (MD5) and unpacking") {
void log() { proceed(); } }.log();
}
void around() : cleanBook() {
new LogHelper("Filtering") {
void log() { proceed(); } }.log();
}
void around(final File origFile) : cleanChapter() && args(*, origFile) {
new LogHelper("Chapter: " + origFile.getName()) {
void log() { proceed(origFile); } }.log();
}
void around() : initialiseTitle() {
new LogHelper("Initialising page title", false) {
void log() { proceed(); } }.log();
}
}
例はあなたができる方法を示しています
- 匿名
LogHelper
を1つ以上のコンストラクターパラメーターを使用してワーカーオブジェクトとしてインスタンス化し、その状態を設定します
- またはを
log()
介してバインドされたジョインポイント状態をオプションで使用して、メソッドを実装します。this()
args()
- ワーカーオブジェクトを呼び出す/実行します(呼び出しはロギングアドバイスのポイントカットによってインターセプトされ、実際のロギングビジネスはそこで行われます)。