Aspect annotation for class - spring-boot

I have a custom annotation, that is handled with AOP in Spring boot. It works perfect when I put it above a method, but when I put it above class I am not able to extract its value :(
Annotation
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface UserAuthorization {
UserRoleEnum[] userRoles();
String paramName() default "userDetails";
String errorMessage() default "NOT AUTHORIZED";
}
Aspect:
#Aspect
#Component
public class UserAuthorizationAspect {
#Around("#annotation(UserAuthorization)")
public Object validateAuthoritiesAspect(ProceedingJoinPoint pjp) throws Throwable {
MethodSignature signature = (MethodSignature) pjp.getSignature();
UserAuthorization userAuthorization = signature.getMethod().getAnnotation(UserAuthorization.class);
// Some code
}
}

Change your aspect to check if the class has annotation
signature.getMethod().getDeclaringClass()
.getAnnotation(UserAuthorization.class)
Change you annotation to support both class level and method level annotations
#Target({ElementType.TYPE, ElementType.METHOD})

Related

Custom class level annotation in spring boot

I created a custom class level annotation.
Below is the aspect class:
#Aspect
#Slf4j
#Component
public class LoggingAspect {
private String generateLogMessage() {
return ("Entering method");
}
#Before("#within(mypackage.logging.Loggable) || #annotation(mypackage.logging.Loggable)")
public void logMethodEntry(JoinPoint joinPoint) {
String logMessage = generateLogMessage();
log.debug(logMessage);
}
}
Below is my custom annotation:
#Target(value = {ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface Loggable { }
When I use the annotation at class level it works. But if I am calling different methods within the same class the logging is done only once. I want to do the logging for each methods called using the class level #Loggable annotation.
I tried to use it at method level it works. But I want to use it at class level.

How to get the value from Custom Annotation in my advice method in Spring AOP?

I have my custom annotation like this:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.TYPE})
public #interface CutomAnnotation{
String value() default "";
}
My aspect class looks like this:
#Aspect
#Component
public class MyCustomAspect{
#Around("#annotation(com.forceframework.web.handlers.monitoring.MeterRegTimer)")
public Object aroundJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("Timer started: "+joinPoint.getSignature());
Object objToReturn=joinPoint.proceed();
System.out.println("Timer ended: "+joinPoint.getSignature());
return objToReturn;
}
}
The place I use the annotation in a controller class:
#CustomAnnotation(value="timer")
#GetMapping(value="/test")
public ResponseEntity test() {}
I would like to know can I access the value passed from my CustomAnnotation in the around advice method aroundJoinPoint in MyCustomAspect class.
Your advice should be declared as shown below:
#Around("#annotation(customAnnotationArgumentName)")
public Object aroundJoinPoint(ProceedingJoinPoint joinPoint, CustomAnnotation customAnnotationArgumentName) throws Throwable {
// ...
}
See documentation for more info.
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
CutomAnnotation methodAnnotation = method.getDeclaredAnnotation(CutomAnnotation.class);

Spring AOP not working for Feign Client

I having an aop-setup
#Target({ElementType.METHOD})
#Retention(value = RetentionPolicy.RUNTIME)
public #interface IgnoreHttpClientErrorExceptions { }
#Aspect
#Component
public class IgnoreHttpWebExceptionsAspect {
#Around(value = "#annotation(annotation)", argNames = "joinPoint, annotation")
public Object ignoreHttpClientErrorExceptions(ProceedingJoinPoint joinPoint, IgnoreHttpClientErrorExceptions annotation)
throws Throwable {
try {
//do something
} catch (HttpClientErrorException ex) {
//do something
}
}
If I add this annotation(#IgnoreHttpClientErrorExceptions) in service layer,
#Service
public class SentenceServiceImpl implements SentenceService {
#Autowired
VerbClient verbClient;
#HystrixCommand(ignoreExceptions = {HttpClientErrorException.class})
#IgnoreHttpClientErrorExceptions
public ResponseEntity<String> patch(String accountId, String patch) {
return verbClient.patchPreferences(accountId, patch);
}
}
My AOP is invoked.
But when I add this annotation(#IgnoreHttpClientErrorExceptions) in my feign layer.
#FeignClient(value = "account")
#RequestMapping(value = "/url")
public interface VerbClient {
#RequestMapping(value = "/{id}/preferences", method = RequestMethod.PATCH, consumes = MediaType.APPLICATION_JSON_VALUE)
#IgnoreHttpClientErrorExceptions
ResponseEntity<String> patchPreferences(#PathVariable("id") String accountId, String patchJson);
}
AOP is not invoked.
Any idea why aop is not get invoked, when I add the annotation in feign-layer?
Dependency added:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Annotation on method is not supposed to inherited.
Hence spring AOP cannot intercept your methods.
Event #Inherited only support inheritance from superclass to subclasses.
So in this case, you should try another pointcut, depend on your need:
// Match all method in interface VerbClient and subclasses implementation
#Around(value = "execution(* com.xxx.VerbClient+.*(..))")
// Match all method in interface VerbClient and subclasses implementation
#Around(value = "execution(* com.xxx.VerbClient+.*(..))")
// Match all method `patchPreferences` in interface VerbClient and subclasses implementation
#Around(value = "execution(* com.xxx.VerbClient+.patchPreferences(..))")
// Or make IgnoreHttpClientErrorExceptions work for Type,
// and match all method with in annotated interface and subclass implementation
// (#Inherited must be used)
// By this way, you can mark your VerbClient feign interface with this annotation
#Around(value = "execution(* (com.yyy.IgnoreHttpClientErrorExceptions *+).*(..))")

Java method introspection with Spring AOP

I use spring-aop to make some treatments on my services methods. The methods on wich the treatment must occur are annotated with #History. Moreover, the annotation can have some params like "comment". Here is on exemple :
#Service
public class MyServiceImpl implements IMyService {
#Override
#History(comment = "my comment")
public void myMethod() {...}
}
public interface IMyService {
void create();
}
And, I have a aspect defined like this :
#Aspect
#Component
public class MyHistoryAspect {
#AfterReturning(pointcut = "execution(* my.service.package.*(..)) && #annotation(history)", returning = "result")
public void myTreatment(JoinPoint joinPoint, History history, Object result) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
...
}
}
Now, my problem : when I use reflection to find out the value of "comment" in my aspect, I can't find it. The reason : the method is the method signature of IMyService, not the method signature of MyServiceImpl. And if I put my annotation on the interface instead of the service, my Aspect is never reached.
Am I missing something or is it the normal behavior of spring aop ?
Thank you

Spring AspectJ Custom Annotation for Logging

I have defined a custom annotation as below.
package com.xyz;
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface Loggable {
String message() default "Log Message";
}
My aspect class contains the below method:
#Around(value = "#annotation(com.xyz.Loggable)")
public void logAround(ProceedingJoinPoint joinPoint) throws Throwable {
// come code here
}
My service interface is as below.
public interface Service {
#Loggable
public void method1();
}
My implementation is as below.
public class ServiceImpl implements Service {
public void method1() {
// some code here
}
}
With this setup, My advice is not getting triggered. (however it gets triggered if i move the #Loggable annotation to method1() in ServiceImpl class).
I would like to keep the annotation defined at interface level instead of method implementations. Is there a way to get this work ?
No, that is not possible (yet?).
Annotations can only be inherited among Classes and even then only if they are themselves annotated with the meta-Annotation #Inherited:
http://docs.oracle.com/javase/8/docs/api/java/lang/annotation/Inherited.html
It is not possible to have annotations on Interfaces be inherited to their implementing classes.
This is also explained in the AspectJ documentation: http://www.eclipse.org/aspectj/doc/released/adk15notebook/annotations.html#annotation-inheritance
#Inherited annotations are not inherited when used to annotate anything other than a type. A type that implements one or more interfaces never inherits any annotations from the interfaces it implements.

Resources