GUICE中继承AOP注解
我正在使用 Guice 和 AspectJ,并且我正在尝试做一些 AOP 来测量某些方法的执行时间.
I'm using Guice and AspectJ, and I am trying to do some AOP to measure the execution time of certain methods.
我有这个注释,用于注释我需要测量的所有方法:
I have this annotation which will be used to annotate all the methods I need to measure:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
public @interface MyStopWatch {
}
我有这个方法拦截器:
public class MyInterceptor implements org.aopalliance.intercept.MethodInterceptor {
private final Logger logger = LoggerFactory.getLogger(MyInterceptor.class);
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
final org.apache.commons.lang3.time.StopWatch stopWatch = org.apache.commons.lang3.time.StopWatch.createStarted();
final Object returnedObject = invocation.proceed();
stopWatch.stop();
logger.info("[STOPWATCH] Method: {} - Time: {}.", invocation.getMethod().getName(), stopWatch.getTime());
return returnedObject;
}
}
我有这个界面
public interface MySuperClass {
@MyStopWatch
default void test() {
System.out.println("Hello, world");
}
}
然后我有这个继承自 MySuperClass 的子类:
Then I have this subclass which inherits from MySuperClass:
public class MySubClass implements MySuperClass {
}
最后,我有了这个绑定:
And finally, I have this bind:
public class MyAOPModule extends AbstractModule {
@Override
protected void configure() {
bindInterceptor(
Matchers.any(),
Matchers.annotatedWith(MyStopWatch.class),
new MyInterceptor()
);
}
}
我像这样初始化我的 Guice 模块:
I initialize my Guice module like this:
public static void main(String[] args) throws Throwable {
Injector injector = createInjector(new MyAOPModule());
injector.getInstance(MySubClass.class).test();
}
我的问题是没有记录任何内容,好像子类对 test() 方法的执行没有注释.
My problem is that there is nothing being logged, as if the subclass's execution of the test() method was not annotated.
有什么办法可以解决这个问题吗?
Any way I can go around this?
这是一个已知和老问题,不太可能很快解决.
This is a known and old issue and is unlikely to be fixed soon.
有几种方法可以避免这种情况,但没有一种是标准的.最有价值的来自这里:https://codingcraftsman.wordpress.com/2018/11/11/google-guice-aop-with-interface-annotations/
There exist several ways to avoid this, but none are standard. The one with the most merit comes from here: https://codingcraftsman.wordpress.com/2018/11/11/google-guice-aop-with-interface-annotations/
// the configure override of a Guice Abstract Module
@Override
protected void configure() {
bindInterceptor(any(),
// an instance of the matcher looking for HTTP methods
new HasOrInheritsAnnotation(GET.class, PATCH.class, PUT.class, POST.class, DELETE.class),
// the method interceptor to bind with
new MyInterceptor());
}
以及 HasOrInheritsAnnotation
类(我尝试解决任何缺失的通用问题,如果仍然存在,请编辑此答案以修复它):
And the HasOrInheritsAnnotation
class (I tried to resolve any missing generic issue, if any persists, please edit this answer to fix it):
/**
* Matcher for methods which either have, or override a method which has a given
* annotation type from the set.
*/
public class HasOrInheritsAnnotation extends AbstractMatcher<Method> {
private final Set<Class<? extends Annotation>> annotationTypes;
/**
* Construct with the types of annotations required
* @param annotationTypes which types of annotation make this matcher match
*/
@SafeVarArgs
public HasOrInheritsAnnotation(Class<? extends Annotation>... annotationTypes) {
this.annotationTypes = ImmutableSet.copyOf(annotationTypes);
}
@Override
public boolean matches(Method method) {
// join this method in a stream with the super methods to fine
// one which has the required annotation
return Stream.concat(
Stream.of(method),
getSuperMethods(method))
.anyMatch(this::hasARequiredAnnotation);
}
/**
* Do any of the annotations on the method appear in our search list?
* @param method the method to inspect
* @return true if any of the annotations are found
*/
private boolean hasARequiredAnnotation(Method method) {
return Arrays.stream(method.getDeclaredAnnotations())
.map(Annotation::annotationType)
.anyMatch(annotationTypes::contains);
}
/**
* Provide a stream of methods from all super types of this
* @param method the method to search with
* @return all methods that are overridden by <code>method</code>
*/
private Stream<Method> getSuperMethods(Method method) {
return streamOfParentTypes(method.getDeclaringClass())
.map(clazz -> getSuperMethod(method, clazz))
.filter(Optional::isPresent)
.map(Optional::get);
}
/**
* A stream of every parent type in the class hierarchy
* @param declaringClass the class to read
* @return a stream of the immediate parent types and their parents
*/
private static Stream<Class<?>> streamOfParentTypes(Class<?> declaringClass) {
return Stream.of(
// all interfaces to the declaring class
Arrays.stream(declaringClass.getInterfaces()),
// all the parent types of those interfaces
Arrays.stream(declaringClass.getInterfaces())
.flatMap(HasOrInheritsAnnotation::streamOfParentTypes),
// the super class of the declaring class
Optional.ofNullable(declaringClass.getSuperclass())
.map(Stream::of)
.orElse(Stream.empty()),
// any parents of the super class
Optional.ofNullable(declaringClass.getSuperclass())
.map(HasOrInheritsAnnotation::streamOfParentTypes)
.orElse(Stream.empty()))
// turn from a stream of streams into a single stream of types
.flatMap(Function.identity());
}
/**
* Search for the given method in a super class. In other words, which
* is the super class method that the given method overrides?
* @param method the method to match
* @param superType the type to scan
* @return an optional with the super type's method in, or empty if not found
*/
private static Optional<Method> getSuperMethod(Method method, Class superType) {
try {
return Optional.of(superType.getMethod(method.getName(), method.getParameterTypes()));
} catch (NoSuchMethodException e) {
// this exception means the method doesn't exist in the superclass
return Optional.empty();
}
}
}