Spring AOP: #within behavior when extending classes - spring

I have an annotation which will be used on extended placeholder classes. Basically, our service will have an implementation, and we will have an explicit extension which will be annotated. I am not sure what the problem is, but #within is not invoking the code whereas #target is.
Here is a sample code - https://github.com/sahil-ag/Spring-AOP-Sample
#Component
public BaseClass { public void getData() {return "";}}
#SampleAnnotation
#Component
public DerivedClass extends BaseClass {}
Here if we now use a #within(SampleAnnotation) pointcut, we will not be able to intercept the getData() when called from a derived class bean.

The #within Annotation is used when you want to define a class where the pointcuts are located in. So make sure that your within-clause looks like:
#within(#MyAnnotation *)
The '*' is used to say any class. This is the part you are missing in your example.
Another approach would be to use the #annotation pointcut:
#Annotation(#MyAnnotation)
Official documentation:
https://www.eclipse.org/aspectj/doc/next/adk15notebook/annotations-pointcuts-and-advice.html

Related

Is it possible to cut into protected method inside abstract class using Spring AOP?

I am trying to execute some code after one protected method inside abstract class gets called. It looks something like this.
abstract class SomeAbstractClass : ServiceOne, ServiceTwo {
protected fun doSomething(p1: String, p2: Int): SomeEntity {
// some business logic
return SomeEntity()
}
}
I want to create annotation class like the one below and annotate method (above) with it and I want it to execute after class method returns value. I tried this and many other variations but nothing worked for me.
#Target(AnnotationTarget.FUNCTION)
#Retention(AnnotationRetention.RUNTIME)
annotation class EntityCreated {
#Aspect
#Component
class EntityAspect {
#AfterReturning("#annotation(EntityCreated)")
fun afterSuccessfulCreate(jp: JoinPoint) {
// Created, execute something here
}
}
}
While protected methods are off limits for JDK proxies when proxying interfaces, the CGLIB proxies used by Spring to extend classes can intercept protected methods. But in order to proxy a Kotlin class, you have to open it, so it is not final and can be proxied.
Please note, however, that if you override a protected method in Java, that overridden method will not "inherit" the super method's annotations. This is a Java limitation and unrelated to AOP. Annotation inheritance only works from parent to sub classes, if the annotation bears the #Inherited meta annotation. There is a way to emulate annotation inheritance using an advanced form of a technique called ITD (inter-type definition) in native AspectJ, but not in simple Spring AOP.
The exact answer to your question depends on your exact use case, your sample code is too fragmentary to answer in more detail.

How can I reference annotation arguments in a custom Spring annotation?

I have an abstract class with methods requiring certain permissions. I would like the implementing classes to be able to require additional permissions, while still requiring those from the abstract class. Simply adding another #PreAuthorize annotation seems to override whatever was required in the abstract class, so my idea was to implement an annotation of my own.
My question is: how do I reference arguments in the #PreAuthorize annotation?
This code:
// Custom annotation
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#PreAuthorize("hasRole('BASE_PERMISSION') and hasRole('#role')")
public #interface MyCustomPreAuthorize {
String role() default "";
}
// Implementing class
#MyCustomPreAuthorize(role = "ANOTHER_ROLE")
does not seem to work.

resilience4j annotations not working on chlid class

I am using resilience4j with SpringBoot. I see that the resilience4j annotations work only if they are placed in the class which throws the exception. If the class is extended by another class & the parent class has the annotation then the retries do not work.
Resilience4j Config
resilience4j.retry:
instances:
service:
maxRetryAttempts: 5
waitDuration: 1000
retryException:
- com.common.exception.RetriableException
Parent Class
#Retry(name = "service")
#Component
public class httpClient extends client{
// This method is invoked from outside
public HttpResponse<T> getResponse(
String url, Class<T> responseType) {
return super.getResponse(url, requestEntity, responseType);
}
}
Child Class
#Retry(name = "service") // Without this line, the retries don't work, even though it is present in the parent class
#Component
public class client{
public HttpResponse<T> getResponse(
String url, Class<T> responseType) {
//Impl which throws RetriableException
}
}
Is this the expected behaviour ? Can you let me know if I am missing something
I never used Resilience4j before, but what I can tell you about Java annotations in general is:
An overridden method in a subclass never inherits annotations from the original parent class method.
In a class implementing an interface an implementing method never inherits annotations from the corresponding interface method.
A classes implementing an interface never inherits annotations from the interface itself.
An interface extending another interface also never inherits any type or method level annotations.
By default not even a subclass inherits annotations from its parent class.
There is a single exception to this "annotations are never inherited" rule: A type level annotation (something like #Foo class Base, can also be abstract) can be inherited by a subclass (something like class Sub extends Base) if and only if the annotation class itself carries the meta annotation #Inherited.
Having said that and looking at the #Retry annotation, you will notice that there is no #Inherited annotation there, so it also cannot work in your case.
If there is another way (e.g. via reflection) to get this done in Resilience4j, I do not know because, as I said, I never used it before.

Not Matching this type name

I have many aspect class in com.aop.aspect package. What I want to do is to work all class except for one class named for com.aop.dao.MyDemoLoggingAspect
When I run the app, there is an error appeared on the console.
java.lang.IllegalArgumentException: warning no match for this type name: com.aop.dao.MyDemoLoggingAspect [Xlint:invalidAbsoluteTypeName]
Here is my aspect class
#Aspect
public class LuvAopExpressionsOrder {
#Pointcut("execution(* com.aop.dao.*.*(..))")
public void forDaoPackage() {}
// create pointcut for getter methods
#Pointcut("execution(* com.aop.dao.*.get*(..))")
public void getter() {}
// create pointcut for setter methods
#Pointcut("execution(* com.aop.dao.*.set*(..))")
public void setter() {}
// create pointcut for setter methods
#Pointcut("!execution(* com.aop.dao.MyDemoLoggingAspect.*(..))")
public void excludeMyDemoLoggingAspect() {}
// create pointcut: include package ... exclude getter/setter and MyDemoLoggingAspect
#Pointcut("forDaoPackage() && !(getter() || setter()) && excludeMyDemoLoggingAspect() ")
public void forDaoPackageNoGetterSetter() {}
}
If you use Spring AOP you don't need to be afraid that one aspect will intercept methods from another because Spring does not support that, as is documented in chapter 5.4.2. Declaring an Aspect. Scroll down a bit and look for this info box:
Advising aspects with other aspects?
In Spring AOP, aspects themselves cannot be the targets of advice from other aspects. The #Aspect annotation on a class marks it as an aspect and, hence, excludes it from auto-proxying.
So basically your issue is a non-issue.
If however you use AspectJ via LTW you are subject to no such limitations and thus have to be careful to exclude other aspects which would normally be intercepted due to matching pointcuts. I recommend to put aspects in packages which are easy to exclude, otherwise you have to do it class name by class name. Use pointcuts like these, depending on your situation:
!within(com.aop.dao.MyDemoLoggingAspect)
!within(com.acme.aop..*)
!within(com.acme..*Aspect)

Not able to inject values in a field

#Component
#PropertySources({ #PropertySource("classpath:mail.properties") })
public class A implements B {
#Value("${mail.team.address}")
private String teamAddress;
// has getter and setters .not shown for brevity.
Now when i call the class i get the value of teamAddress as NULL .But in the property file mail.team.address has some value.
My property file is present under src/main/resource folder
Making a call
A a = new A ();
a.someMethodinClassA();
You can not create instance of class by yourself when you want Spring to resolve #Value annotation.
See documentation:
Note that actual processing of the #Value annotation is performed by a BeanPostProcessor which in turn means that you cannot use #Value within BeanPostProcessor or BeanFactoryPostProcessor types. Please consult the javadoc for the AutowiredAnnotationBeanPostProcessor class (which, by default, checks for the presence of this annotation).
Simple solution for you: just annotate class with any #Component annotation and let Spring to create an instance of your class.
You can't create (with a "new" keywoard) for spring bean. If you do it like this, spring doesn't participate in the object creation and configuration, which means that there is no autowiring, the bean is not in Application Context, etc. And of course, #Value annotation won't be processed among other things
The better way is to inject the class A to the code that you used in your example:
A a = new A ();
a.someMethodinClassA();
Show become:
#Component
public class SomeClass {
private final A a;
public SomeClass(A a) {
this.a = a;
}
public void foo() {
a.someMethodinClassA();
}
}
You should read some basics around spring dependency injection. The class that you have autowired with #Component is scanned via component scanning and its object is created by spring container for you.
that is the reason you should not create the object yourself using new keyword.
wherever in your new class you want to use your class A object you can autowire it as below:
#Component
public class TestC{
private A a; // this object will be injected by spring for you
}

Resources