I have a weird problem trying to add a parameterized logging interceptor to my Java EE 7 application.
I went from a class interceptor using the #Interceptors annotation to writing a custom interceptor binding. What I have not looks like this...
The annotation
#Inherited
#InterceptorBinding
#Retention(RetentionPolicy.RUNTIME)
#Target({
ElementType.TYPE,
ElementType.METHOD
})
public #interface LogMethodCall {
MethodLogger logLevel() default MethodLogger.INFO;
}
The interceptor
#Slf4j
#LogMethodCall
#Interceptor
#Priority(Interceptor.Priority.APPLICATION)
public class ActionInterceptor {
#AroundInvoke
protected Object protocolInvocation(final InvocationContext ic) throws Exception {
log.info(
"{}: <{}> called. Parameters={}",
ic.getTarget().getClass().getName(),
ic.getMethod().getName(),
ic.getParameters());
return ic.proceed();
}
}
The usage
#GET
#Path("/{account}")
#LogMethodCall
public void inboxes(#Suspended AsyncResponse response, #PathParam("account") String account) {
...
}
When I use it like this everything works OK.
Buy when I try to use change the logLevel and use
#LogMethodCall(logLevel=MethodLogger.DEBUG)
then my interceptor never gets called.
What am I missing here? Why setting the annotation value breaks the code?
If you say that your interceptor is catching only when the value is INFO, you can consider to put your logLevel() attribute as #Nonbinding.
By default, qualifier arguments are considered for matching bean qualifiers to injection point qualifiers. A #Nonbinding argument is not considered for matching.
Try this:
#Inherited
#InterceptorBinding
#Retention(RetentionPolicy.RUNTIME)
#Target({
ElementType.TYPE,
ElementType.METHOD
})
public #interface LogMethodCall {
#Nonbinding MethodLogger logLevel() default MethodLogger.INFO;
}
Related
I'm writing an aspect to log Request and Response of each API call in a controller.
I want to be able to use this annotation on a class, hence used #Target(ElementType.TYPE)
Previously I had added #Target(ElementType.Method) and I was using this annotation on methods and it was working fine.
Now I want to change it to #Target(ElementType.TYPE)
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface ReLogger {}
#Aspect
#Component
public class ReLoggerAspect {
public static final Logger log = LoggerFactory.getLogger("ReLoggerAspect");
#PostConstruct
private void postConstruct() {
log.info("ReLoggerAspect Created");
}
#Around("#annotation(ReLogger)")
private Object reqLoggingAspect(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("Request {}",jointPoint.getArgs()[0);
}
}
Using #ReLoggerAspect on a class
#RestController
#RequestMapping(value = "....", produces = { "application/json" })
#ReLogger
public class Samplecontroller {
/** Some logic here**/.....
}
It doesn't print the Request when an API SampleController is invoked
Your premise that #annotation would match type annotations is wrong, see (Spring AOP manual](https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop-pointcuts-designators):
#within: Limits matching to join points within types that have the given annotation (the execution of methods declared in types with the given annotation when using Spring AOP).
#annotation: Limits matching to join points where the subject of the join point (the method being executed in Spring AOP) has the given annotation.
Thus, you ought to use #within(fully.qualified.AnnotationType).
I have an annotation
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface A {
Class<?> value();
}
and another annotation that uses #AliasFor
#A (Void.class)
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface B {
#AliasFor(annotation = A.class)
Class<?> value();
}
which is used on class
#B(D.class)
public class C implements D {
}
If I have an instance of C, how can I programatically resolve A.value() to Class<D>?
I'm trying to synthesise the annotation with AnnotationUtils but when I retrieve the value I'm constantly getting Class<Void>.
After some digging around, the answer is that I've used the wrong class. Instead of AnnotationUtils it can be done with AnnotatedElementUtils:
#Test
public void shouldFindAliasedValue() {
Class<?> actual = AnnotatedElementUtils.findMergedAnnotation(C.class, A.class).value();
then(actual).isEqualTo(D.class);
}
I have created some custom annotations to use for system tests which are run via JUnit.
A test e.g. looks like this:
#TestCaseName("Change History")
public class ChangeHistory extends SystemTestBase
{
#Test
#Risk(1)
public void test()
{
...
I am now implementing a Test Runner which shall report the test name, the risk and the somewhere for documentation purposes.
public class MyRunner extends BlockJUnit4ClassRunner
{
...
#Override
protected void runChild(final FrameworkMethod method, RunNotifier notifier)
{
...
System.out.println("Class annotations:");
Annotation[] classanno = klass.getAnnotations();
for (Annotation annotation : classanno) {
System.out.println(annotation.annotationType());
}
System.out.println("Method annotations:");
Annotation[] methanno = method.getAnnotations();
for (Annotation annotation : methanno) {
System.out.println(annotation.annotationType());
}
The output is
Class annotations:
Method annotations:
interface org.junit.Test
So getAnnotations() seems to return annotations of JUnit only and not all annotations. This is not mentioned in the documentation of JUnit:
Returns the annotations on this method
The return type is java.lang.Annotation which made me believe that I can use any annotation. I defined the annotation like follows - I just used it and when there was an error I let Eclipse generate the annotation:
public #interface Risk {
int value();
}
How do I get all annotations of the test class and test method?
You need to set the retention policy of the Risk annotation to RUNTIME. Otherwise, the annotation will be discarded after the compilation and won't be available during the execution of the code.
This should be working:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface Risk {
int value();
}
I want to simplify this
#Controller
#Scope("prototype")
public class Struts2ActionClass{
...
}
to this
#Struts2Action
public class Struts2ActionClass{
...
}
attempt to avoid foggetting the #Scope("prototype")
Dose anyone have any idea?
Update:
I did this copy the code of #Controller,it seems worked.
#Target({ ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
#Controller
#Scope("prototype")
public #interface Struts2Action {
}
But why?
I did this copy the code of #Controller,it seems worked.
#Target({ ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
#Controller
#Scope("prototype")
public #interface Struts2Action {
}
It works because that's the way to combine annotations. So now you don't need to write every annotation (Controller, Scope etc.), just the parent one (Struts2Action)
I recently found that ( http://www.javabeat.net/articles/30-annotations-in-java-50-2.htmlthe )syntax of the #Override annotation is
#Retention(RetentionPolicy.CLASS)
#Target(ElementType.RUNTIME)
public #interface Override
{
}
But I think the following.Since it can be applied only to methods and since it inform this to compiler.
#Retention(RetentionPolicy.CLASS
#Target(ElementType.METHOD)
public #interface Override
{
}
Please tell me which one is correct. Please explain, if i am wrong.
Thanks.
Both are wrong; it is defined (according to javadoc) as
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.SOURCE)
public #interface Override