我有一个 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/