Spring AOP not working for Feign Client - spring

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 *+).*(..))")

Related

Why can't Spring AOP determine if proxy should be made for #target? [duplicate]

When using spring AOP with class level annotations, spring context.getBean seems to always create and return a proxy or interceptor for every class, wether they have the annotation or not.
This behavior is only for class level annotation. For method level annotations, or execution pointcuts, if there is no need for interception, getBean returns a POJO.
Is this a bug? As designed? Or am I doing something wrong?
#Component
#Aspect
public class AspectA {
#Around("#target(myAnnotation)")
public Object process(ProceedingJoinPoint jointPoint, MyAnnotation myAnnotation) throws Throwable {
System.out.println(
"AspectA: myAnnotation target:" + jointPoint.getTarget().getClass().getSimpleName());
System.out.println(" condition:" + myAnnotation.condition());
System.out.println(" key:" + myAnnotation.key());
System.out.println(" value:" + myAnnotation.value());
return jointPoint.proceed();
}
}
#Component("myBean2")
//#MyAnnotation(value="valtest-classLevel2", key="keytest-classLevel2", condition="contest-classLevel2")
public class MyBean2 {
public Integer testAspectCallInt(int i) {
System.out.println("MyBean2.testAspectCallInt(i=" + i + ")");
return i + 1000;
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE, ElementType.METHOD })
public #interface MyAnnotation {
String value() default "";
String key() default "";
String condition() default "";
}
#ComponentScan()
#EnableAspectJAutoProxy
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(Test.class);
MyBean2 bean = (MyBean2) ctx.getBean("myBean2");
System.out.println(bean.getClass()); // prints CGLIB proxy, even when annotation is commented out on class
bean.testAspectCallInt(12); // calling method
}
}
Andy Brown is right, it is by design. The reason is that according to the AspectJ manual pointcut designators such as #args, #this, #target, #within, #withincode, and #annotation (or the subset of those available in Spring AOP) are used to match based on the presence of an annotation at runtime. This is why in the Spring debug log you see that proxies are created for all components which might need aspect functionality.
If you want to avoid that, you can refactor your aspect into something like this at the cost of an uglier pointcut and even uglier reflection in the advice code:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.annotation.Annotation;
#Component
#Aspect
public class AspectA {
#Around("execution(* (#MyAnnotation *).*(..)) || execution(#MyAnnotation * *(..))")
public Object process(ProceedingJoinPoint joinPoint) throws Throwable {
MyAnnotation myAnnotation = null;
for (Annotation annotation : ((MethodSignature) joinPoint.getSignature()).getMethod().getDeclaredAnnotations()) {
if (annotation instanceof MyAnnotation) {
myAnnotation = (MyAnnotation) annotation;
break;
}
}
if (myAnnotation == null) {
myAnnotation = joinPoint.getTarget().getClass().getAnnotationsByType(MyAnnotation.class)[0];
}
System.out.println("AspectA: myAnnotation target:" + joinPoint.getTarget().getClass().getSimpleName());
System.out.println(" condition:" + myAnnotation.condition());
System.out.println(" key:" + myAnnotation.key());
System.out.println(" value:" + myAnnotation.value());
return joinPoint.proceed();
}
}
If neither the bean's class nor any of its methods bears the annotation, no proxy will be created. The advice detects both types of annotation, but prefers a method annotation if both are present.
Update: Instead of this workaround you could of course use full AspectJ from within Spring and avoid proxies altogether.

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

Aspect annotation for class

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

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 Aspects (AspectJ) does not seem to work

Well I really don't know why this does not work:
Every jar needed is in the place. Including aspectjrt.
Basically I start with configuration class:
#Configuration
#ComponentScan(basePackages = { "some.path" })
#EnableAspectJAutoProxy
public class SomeConf { ... }
Then I have my Aspect:
#Component
#Aspect
public class ControllerLoggerAspect {
#Pointcut("execution(* some.path.ATest.*(..))")
private void aspectTest() {
System.out.println("\n\n ASPECT WORKING \n\n");
}
}
Under some.path I have Atest class:
package some.path;
public class ATest {
public void dummyMethod(){
System.out.println("\n\n\nDummy static executed\n\n\n");
}
}
Imagine now that I have controller:
#Controller
#RequestMapping(value = "/mapping")
public class SomeController {
#RequestMapping(value = "/something")
public ResponseEntity<String> publish(#RequestParam("Id") Long[] ids) {
//aspect should be invoked here
new ATest().dummyMethod();
return new ResponseEntity<>("{ \"status\": \"stubbed\"}", HttpStatus.OK);
}
}
Everything is invoked properly except aspect method. No errors, no exceptions, nothing. Any ideas?
Spring AOP only works on Spring beans, i.e. if you want to intercept one of its methods, class ATest needs to be a #Component.
You need to configure the advice you want execute. The pointcut only helps in determining the join points. The advice will be executed, not the pointcut.
You could write a Advice like this using your pointcut "aspectTest()":
#Before("aspectTest()")
public void beforeAdvice(){
System.out.println("\n\n ASPECT WORKING \n\n");
}
Or you could just replace your Pointcut annotation in the example with an advice annotation like this:
#Before("execution(* some.path.ATest.*(..))")
public void aspectTest() {
System.out.println("\n\n ASPECT WORKING \n\n");
}
Here you have a list of all adivces:
#Before("aspectTest()")
#After("aspectTest()")
#AfterReturning(pointcut = "aspectTest()", returning="retVal")
#AfterThrowing(pointcut = "aspectTest()", throwing="ex")
#Around("aspectTest()")

Resources