說實話,最簡單的方法是簡單地通過從來沒有從類中調用同一類的其他公共/註解的方法避免這個問題:
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實例化的實例的方法。所以這個邏輯必須被移到攔截器本身。
這樣做的一種方法可能是使用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 {
}
}
哇,這是非常整潔。幸運的是,兩個第一(也是更優雅)的解決方案不容易獲得,但我認爲你的建議相當整潔。 –
那麼,我的應用程序是結構的方式,因爲我只關心過濾第一個請求 @Override public Object invoke(MethodInvocation invocation)throws Throwable this.inProcess.get (),invocation.getMethod()); Integer inProcess = this.inProcess.get(); 嘗試if(inProcess == null){this.inProcess.set(1); if(inProcess == null){ return doInvoke(invocation); } else { this.inProcess.set(inProcess + 1); return invocation.proceed();最後{ this.inProcess.set(inProcess); } } –
其中 private final ThreadLocal inProcess = new ThreadLocal (); –