Java googleguice中的条件匹配

Java googleguice中的条件匹配,java,dependency-injection,aop,guice,Java,Dependency Injection,Aop,Guice,我有一个绑定到类中方法的MethodInterceptor,以便在类接触数据之前对数据执行一些简单的逻辑。 然而,类本身会调用它自己截获的一些方法,但此时我不再需要重新运行该逻辑 public class MyModule extends AbstractModule { @Override public void configure() { bindInterceptor(Matchers.any(), Matchers.annotatedWith(MyAnnotation

我有一个绑定到类中方法的MethodInterceptor,以便在类接触数据之前对数据执行一些简单的逻辑。 然而,类本身会调用它自己截获的一些方法,但此时我不再需要重新运行该逻辑

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?

老实说,最简单的解决方案是通过从不在类中调用同一类的另一个公共/注释方法来避免问题:

public class MyClass {
  @MyAnnotation
  public void foo() {
     doBar();
  }

  @MyAnnotation
  public void bar() {
     doBar();
  }

  private void doBar() {
     //doesn't go through interceptor
  }
}
如果出于某种原因,这不是一种选择,那么您可以考虑这种方法。更具表现力的AOP库(如AspectJ)为定义切入点提供了更大的灵活性

在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;
}

哇,太好了。幸运的是,前两个(也是更优雅的)解决方案很容易获得,但我确实认为你所建议的相当简洁。好吧,我的应用程序的结构方式,我可能可以做类似的事情,因为我只关心过滤第一个请求@Override public Object invoke(MethodInvocation invocation)抛出的Throwable{this.inProcess.get(),invocation.getMethod());整数inProcess=this.inProcess.get();尝试{if(inProcess==null){this.inProcess.set(1);return doInvoke(invocation);}else{this.inProcess.set(inProcess+1);return invocation.procement();}最后{this.inProcess.set(inProcess);}}}其中private final ThreadLocal inProcess=new ThreadLocal();
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 {
    }

}