java - Google Guice 中的条件匹配

标签 java dependency-injection aop guice

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

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)/带注释的方法来避免该问题:

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

这使用线程本地堆栈来跟踪拦截器的调用堆栈。当最后一次调用此拦截器针对同一对象时,它会调用 proceed() 并绕过拦截器。当这是第一次调用拦截器时,或者如果最后一次调用不是针对同一对象,则会应用拦截器。

然后,当拦截器处于 Activity 状态时您想要应用的实际逻辑将进入 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 {
    }

}

关于java - Google Guice 中的条件匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12393643/

相关文章:

java - 反转 boolean 值?

.net - codecampserver 中的 StaticFactory<T> 是众所周知的模式吗?

java - Spring AOP - 异常 : org. springframework.beans.factory.BeanDefinitionStoreException:

java - AspectJ - 尝试包装用一个注释而不是另一个注释注释的方法

Java8 时间库不能正确解释 BST 时区

java - Java 的访问者模式

java - 如何将使用(和,或..等)等连词连接的两个或多个句子分成两个单独的句子?

java - 通过拦截器或监听器或预处理器覆盖特定的 guice 绑定(bind)

c# - 依赖注入(inject)和开发生产力

java - 使用 AOP 拦截私有(private)注释方法