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

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.

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.

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.

Spring AOP: #within behavior when extending classes

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

Spring Class.getAnnotationsByType does not work correctly when I introduce AOP

I have a Bean in Spring with a custom Annotation.
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface CliCommand {
Slice slice();
}
During runtime (startup) I want to load all classes that have this annotation.
During this process I call.
CliCommand[] cliAnnotations = myclass.getAnnotationsByType(CliCommand.class);
Before I introduced AOP it worked fine.
However now this returns NULL.
My AOP looks like this.
#Pointcut("within(com.companyx.cli..*)")
public void cliLayer() {
}
#Before("cliLayer()")
public void injectCLI(JoinPoint jp){
MDC.put(ConnectApplicationContext.LOG_LAYER_NAME, Layer.CLI);
}
I am not sure why this is happening. Has anyone seen this before. Is it something to do with the cgLib?
Cglib generates child of your class when creating proxy. So if 'myclass' in your example was obtained via someCglibObject.getClass() you will get cglib subclass, not yours.
In this case to make annotations declared on parent class to be available via childClass.getAnnotationsByType you should annotate your custom annotation with #Inherited

advice for annotation on either class or method

I have the following problem:
I have create annotation for Security:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.TYPE})
#Inherited
#Documented
public #interface Security {
Role[] roles();
}
I am now annotating classes and methods with this annotation. In some cases the class is annotated with this annotation and specific method within this class is also annotated with #Security with different roles.
How can create #Before advice that will catch either methods annotated with #Security ot methods within classes annotated with #Security, and also to get the more specific definition in case there are annotation on both the class and method.
I obviously need also the content of the annotation as well (the roles).
Is it possible?
Is it also possible in cases that I have inhertience between classes that all of them have the #Security annotation to get the most specific definition?
Yosi
This should work (native AspectJ code, but you can translate to #Aspect if you prefer that):
pointcut secureMethods() : execution(#Security * *(..));
pointcut secureTypes() : execution(* (#Security *).*(..));
Object around(Security security) : secureMethods() && #annotation(security) {
apply(security);
return proceed(security);
}
Object around(Security security) : secureTypes() && !secureMethods() && #target(security) {
apply(security);
return proceed(security);
}

Resources