正直なところ、最も簡単な解決策は、クラス内から同じクラスの別の public/annotated メソッドを呼び出さないことで、問題を単純に回避することです。
public class MyClass {
@MyAnnotation
public void foo() {
doBar();
}
@MyAnnotation
public void bar() {
doBar();
}
private void doBar() {
//doesn't go through interceptor
}
}
何らかの理由でそれができない場合は、このアプローチを検討してください。AspectJ のような表現力の高い AOP ライブラリを使用すると、ポイントカットをより柔軟に定義できます。
Guice では、ポイントカットは、Guice によってインスタンス化されたインスタンスに属しているアノテーションを持つ単なるメソッドです。したがって、このロジックはインターセプター自体に移動する必要があります。
これを行うための 1 つのアプローチは、 a を使用ThreadLocal
してインターセプターへのエントリを追跡することです。このようなものを拡張することは、出発点かもしれません:
public abstract class NonReentrantMethodInterceptor implements MethodInterceptor {
private final ThreadLocal<Deque<Object>> callStack = new ThreadLocal<>();
@Override
public final Object invoke(MethodInvocation invocation) throws Throwable {
Deque<Object> callStack = this.callStack.get();
if (callStack == null) {
callStack = new LinkedList<>();
this.callStack.set(callStack);
}
try {
return invokeIfNotReentrant(callStack, invocation);
} finally {
if (callStack.isEmpty()) {
this.callStack.remove();
}
}
}
private final Object invokeIfNotReentrant(Deque<Object> callStack, MethodInvocation invocation) throws Throwable {
Object target = invocation.getThis();
if (callStack.isEmpty() || callStack.peek() != target) {
//not being called on the same object as the last call
callStack.push(target);
try {
return doInvoke(invocation);
} finally {
callStack.pop();
}
} else {
return invocation.proceed();
}
}
protected abstract Object doInvoke(MethodInvocation invocation) throws Throwable;
}
これは、スレッド ローカル スタックを使用して、インターセプターへの呼び出しのスタックを追跡します。このインターセプターへの最後の呼び出しが同じオブジェクトを対象とした場合、proceed()
インターセプターを呼び出してバイパスします。これがインターセプターへの最初の呼び出しである場合、または最後の呼び出しが同じオブジェクトを対象としていない場合、インターセプターが適用されます。
次に、インターセプターがアクティブなときに適用する実際のロジックが に入りdoInvoke()
ます。
使用例:
public class NonReentrantTester {
public static void main(String[] args) {
Injector injector = Guice.createInjector(new Module());
MyClass instance = injector.getInstance(MyClass.class);
instance.foo();
}
static class Module extends AbstractModule {
@Override
protected void configure() {
bindInterceptor(Matchers.any(), Matchers.annotatedWith(PrintsFirstInvocation.class),
new PrintsFirstInvocationInterceptor());
}
}
public static class MyClass {
@PrintsFirstInvocation
void foo() {
bar();
}
@PrintsFirstInvocation
void bar() {
}
}
public static class PrintsFirstInvocationInterceptor extends NonReentrantMethodInterceptor {
@Override
protected Object doInvoke(MethodInvocation invocation) throws Throwable {
System.out.println(invocation.getMethod());
return invocation.proceed();
}
}
@BindingAnnotation
@Target({FIELD, PARAMETER, METHOD})
@Retention(RUNTIME)
public @interface PrintsFirstInvocation {
}
}