advice for annotation on either class or method - spring

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

Related

#Around Pointcut not getting invoked for custom annotation

The question seems to be duplicate but none of the answers from existing questions worked.
I am creating a custom annotation as below,
#Target({ ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface Translate {
}
I have created an Aspect as,
#Aspect
#Configuration
public class TranslateAspect {
#Around("#annotation(translate)")
public Object translate(ProceedingJoinPoint joinPoint, Translate translate) throws Throwable {
Object result = joinPoint.proceed();
System.out.println(result); //Other logic
return result;
}
}
I tried providing the complete class name with the package also. The entity is getting passed to RestController,
#Entity
#Getter
#Setter
public class Pojo {
#Translate
private String label;
}
But the translate method is not getting invoked whenever the new request is served.
Highly appreciate any help around this.
From the reference documentation : 5.2. Spring AOP Capabilities and Goals
Spring AOP currently supports only method execution join points (advising the execution of methods on Spring beans). Field interception is not implemented, although support for field interception could be added without breaking the core Spring AOP APIs. If you need to advise field access and update join points, consider a language such as AspectJ.
Spring AOP works with spring container managed beans and advicing method executions. The code shared here annotates a field and not the corresponding setter method. PCD defined is for the execution any Spring container managed bean method annotated with #Translate.
A class annotated with #Entity will not register its instance as a Spring managed bean. Do go through this StackOverflow Q&A
Pojo instance is not a Spring managed bean ( also pointed out by João Dias in his answer ).
Try this:
#Aspect
#Configuration
public class TranslateAspect {
#Pointcut("#annotation(com.full.packagename.to.annotation.Translate)")
public void anyTranslatableMethod() {
}
#Around("anyTranslatableMethod()")
public Object translate(ProceedingJoinPoint joinPoint) throws Throwable {
// ...
}
}
A working example here: https://github.com/asgarov1/springTricks/blob/main/aop/src/main/java/com/asgarov/aop/config/LoggingConfiguration.java
Try the following:
#Aspect
#Configuration
public class TranslateAspect {
#Around("#annotation(Translate)")
public Object translate(ProceedingJoinPoint joinPoint) throws Throwable {
Object result = joinPoint.proceed();
System.out.println(result); //Other logic
return result;
}
}
Mind the uppercase "T" in #Around("#annotation(Translate)").
UPDATE
Just noticed you are expecting the aspect to be applied to a class annotated with #Entity. These are Entities that are JPA entities but they are not Spring-managed Beans. Spring AOP only handles Spring-managed Beans so this is simply not possible.

Spring AOP with aspectj #annotation [duplicate]

This question already has an answer here:
Emulate annotation inheritance for interfaces and methods with AspectJ
(1 answer)
Closed 1 year ago.
I want to apply annotation with aspectJ. (Use Springboot 1.5.1, Mybatis 2.1.1)
So, I made custom annotation and AspectJ.. and apply it.
/** CustomAnnotation */
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface TestAnnotation {
String value();
}
/** AspectJ configuration */
#Component
#Aspect
public class AuditTrailAspect {
#Autowired
TestDAO dao;
#Around("#annotation(TestAnnotation)")
public Object doSomethingAround(ProceedingJoinPoint joinPoint) throws Throwable {
/* before proceed */
Object result = joinPoint.proceed();
/* after proceed */
return result;
}
}
/** Apply Annoataion at Repository */
#Repository
public interface TestDAO {
#TestAnnotation(value = "test")
int insertSomething(RequestDto dto);
}
(this code made simple, for question)
if pointcut expression apply 'execution' this code works fine in Repository(DAO)..
also if pointcut expression apply '#annotation' this code works other Component(Service.. Controller)
But, Why can't I apply custom annotation in Repository(DAO) with AspectJ?
please help.. Thank you!
Annotations on implemented interfaces cannot be inherited.
#Inherited causes annotations (only on class) to be inherited from superclasses , but no effect for interface implementations.
Note that this meta-annotation type has no effect if the annotated
type is used to annotate anything other than a class. Note also that
this meta-annotation only causes annotations to be inherited from
superclasses; annotations on implemented interfaces have no effect.

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.

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

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