2012-09-12 43 views
2

我有一個MethodInterceptor綁定到一個類中的方法,以便在類之前接觸到數據之前做一些簡單的邏輯。 但是,類本身會調用某些自己截取的方法,但在此時我不需要再重新運行該邏輯。谷歌Guice中的條件匹配

public class MyModule extends AbstractModule { 
    @Override 
    public void configure() { 
    bindInterceptor(Matchers.any(), Matchers.annotatedWith(MyAnnotation.class), new MyInterceptor()); 
    } 
} 

public class MyInterceptor implements MethodInterceptor { 
    @Override 
    public Object invoke(MethodInvocation invocation) throws Throwable { 
    // logic 
    } 
} 

public MyClass { 
    @MyAnnotation 
    void foo() { 
    bar(); 
    } 

    @MyAnnotation 
    void bar() { 
    } 
} 

有沒有一種方法可以調用foo中的bar來不被它接受?

回答

3

說實話,最簡單的方法是簡單地通過從來沒有從類中調用同一類的其他公共/註解的方法避免這個問題:

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 { 
    } 

} 
+0

哇,這是非常整潔。幸運的是,兩個第一(也是更優雅)的解決方案不容易獲得,但我認爲你的建議相當整潔。 –

+0

那麼,我的應用程序是結構的方式,因爲我只關心過濾第一個請求 @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); } } –

+0

其中 private final ThreadLocal inProcess = new ThreadLocal (); –