How to intercept setter method with AOP - java

I want to do something with a field BEFORE it gets the value assigned with a setter. I created the annotation and the aspect, but I don't know how to get the setter. This is my code:
#Aspect
public class AnnotationAspect{
#Pointcut("#annotation(annotationVariableName)")
public void annotationPointCutDefinition(Annotation annotationVariableName){
}
#Pointcut("execution(* *(..))")
public void atExecution(){}
#Around("annotationPointCutDefinition(annotationVariableName) && atExecution()")
public Object aroundAdvice(ProceedingJoinPoint joinPoint, Annotation annotationVariableName) throws Throwable {
Object returnObject = null;
returnObject = joinPoint.proceed();
return returnObject;
}
}
The annotation #Annotation is applied to a field in scala, so the method is "field =" (I don't know if this can change something).
Can anyone help me?
Thanks!
EDIT
This is the annotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface Annotation{
}
And an example code:
class Test{
#Annotation
var testing: String =_
}

Related

Get the annotation on method from the instance that the method created

Here is an example
#Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public #interface Annotation {
}
#Configuration
public class Configuration {
#Bean
#Annotation
public Test getTest() {
return new Test();
}
}
public class Test() {
public void test() {
// how can get the annotation `#Annotation` here?
}
}
Here is what I have tried getClass().getAnnotations() but this returns empty array. I can see why since getClass() return Test.class which does not have the annotation. How can I get the method that creates this instance and then get the annotation?
You could, in theory, inspect the current Thread stack to determine the name of your caller, then look up the class definition, locate the method, and read its annotations:
var t = Thread.currentThread().getStackTrace()[2];
var className = t.getClassName();
Class<?> clazz;
try {
clazz = Test.class.getClassLoader().loadClass(className);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Caller was loaded by a different ClassLoader :-(");
}
for (var method : clazz.getDeclaredMethods()) {
if (method.getName().equals(t.getMethodName())) {
return method.getAnnotation(YourAnnotation.class).value();
}
}
throw new RuntimeException("Method not found - I might have found the wrong class definition");
However:
inspecting the stack is rather slow, in particular if the stack is deep
inspecting the stack is brittle with respect to refactorings (people don't expect that factoring out code into a utility method will change behaviour)
the compiler can not check that the caller provides the required annotation
this only works reliably if all code is loaded by the same ClassLoader
this can not distinguish overloaded methods
This is therefore a rather brittle hack. Are you sure that there is no better option? For instance, requiring the caller to pass the value as a method parameter would have none of these shortcomings ...
You can use ConfigurableListableBeanFactory to get metadata about any Bean by name. Use BeanNameAware interface to retrieve Bean name.
#Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public #interface CustomAnnotation {
}
#org.springframework.context.annotation.Configuration
public static class ContextConfiguration {
#Bean(name = "TEST")
#CustomAnnotation
public TestObject getTest() {
return new TestObject();
}
}
public class TestObject implements BeanNameAware {
private String beanName;
#Autowired
ConfigurableListableBeanFactory beanFactory;
#Override
public void setBeanName(String name) {
this.beanName = name;
}
public void test() {
CustomAnnotation customAnnotation = (CustomAnnotation) getBeanAnnotation(beanName, CustomAnnotation.class);
}
private Annotation getBeanAnnotation(String beanName, java.lang.Class<? extends Annotation> clazz) {
Annotation annotation = null;
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
if( beanDefinition != null && beanDefinition.getSource() instanceof StandardMethodMetadata) {
StandardMethodMetadata metadata = (StandardMethodMetadata) beanDefinition.getSource();
annotation = Arrays.stream(metadata.getIntrospectedMethod().getDeclaredAnnotations()).filter(annot -> annot.annotationType().equals(clazz)).findFirst().orElse(null);
}
return annotation;
}
}

Is there a #class tag in Aspectj like #annotation tag?

There is a #annotation tag in Spring AOP Aspectj and we can easily annotation object. E.g:
Custom Annotation:
public #interface Authorize
#Before("#annotation(authorize)")
public void adviseAnnotatedMethods(JoinPoint joinPoint, Authorize authorize) {
System.out.println(authorize);
}
I wonder whether there is a #class tag like #annotation?
#Before("#class(customClass)")
public void adviseAnnotatedMethods(JoinPoint joinPoint, CustomClass customClass {
System.out.println(customClass);
}
Actually, in here, my aim is to reach CustomClass object. I know that I can reach it from jointPoint.getArgs() as checking (object instanceOf CustomClass). But I wonder there is a easy way to do it?
Update: full code
Exact Code:
I have a controller method:
#GetMapping("user/waiting/{accountId}/{userId}")
public ResponseEntity<?> getWaitingItems(#Custom CustomUser user) {
...
}
I have an annotation like below:
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.PARAMETER, ElementType.TYPE})
public #interface Custom {}
CustomUser:
public class CustomUser {
private Long accountId;
private Long userId;
private User user;
}
Aspectj methods:
#Pointcut("execution(* *(.., #Custom (*), ..))")
void annotatedCustom() {}
#Before("annotatedCustom()")
public void adviseMethodsOfAnnotatedClass(JoinPoint joinPoint) {
CustomUser cu = getArg(joinPoint);
...handle it.
}
private CustomClass getArg(JoinPoint joinPoint) {
for (Object a : joinPoint.getArgs()) {
if (a instanceof CustomClass) {
return (CustomClass) a;
}
}
return null;
}

AOP Spring #AfterReturning not calling the aspects method properly

I have an annotation.
#Target(value = {ElementType.METHOD, ElementType.TYPE})
#Retention(value = RetentionPolicy.RUNTIME)
#Inherited
#Documented
public #interface MyCustomAnnotation{
}
My Aspect class is like that
#Component
#Aspect
public class MyCustomAsspect{
#AfterReturning(
pointcut="#annotation(MyCustomAnnotation)",
returning="retVal")
public void publishMessage(JoinPoint jp, Object retVal) throws Throwable {
}
}
My Service class is
#Service
public class ServiceClass{
#MyCustomAnnotation
public Object someMethod(){
return new Object();
}
}
Above are mentioned classes i am not sure why my aspect not working. I am new to Spring AOP . Please help me it shall be very thankful.
Issue is due to pointcut declaration. As spring documentation says
#annotation - limits matching to join points where the subject of the
join point (method being executed in Spring AOP) has the given
annotation
So I order to make this work
#Aspect
public class MyCustomAsspect{
#AfterReturning(
pointcut="execution(public * *(..)) and #annotation(MyCustomAnnotation)",
returning="retVal")
public void publishMessage(JoinPoint jp, Object retVal) throws Throwable {
}
}

How can Spring AOP matches annotation on interface method?

I use mybatis. My question is how can Spring AOP matches annotation on interface method? Because I want to put some param in annotation and then handle them in afterReturning method.
my annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface CacheClear {
String key() default "";
}
in mapper class:
#CacheClear
List<BizInst> selectAllBizInsts();
and in my aspect:
when use "execute..." it works
#AfterReturning("execution(public * com.dao.*.select*(..))")
public void doAfterReturning(){
System.out.println("after returning");
}
but when use "#annotation(...)" it doesn't work
#AfterReturning("#annotation(com.annotation.CacheClear)")
public void doAfterReturning(){
System.out.println("after returning");
}
You can do something like that for selecting your public dao methods annotated with CacheClear annotation:
#Pointcut("execution(#com.yourPackage.CacheClear * *(..))")
public void methodAnnotatedWithCacheClear( ) {}
#Pointcut("execution(public * com.dao.*.select*(..))")
public void publicDAOMethod() {}
#AfterReturning(pointcut = "methodAnnotatedWithCacheClear() && publicDAOMethod()", returning = "result")
public void doStuff(JoinPoint joinPoint, Object result) {

pass result of annotation work to annotated method

Annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Multipart {
Class acceptClass();
}
Annotated method:
#Multipart (acceptClass = SomeClass.class)
public void someMethod(SomeClass a){
//do stuff..
}
MultipartAspect:
#Aspect
public class MultipartAspect {
#Autowired(required=true)
private HttpServletRequest request;
#Pointcut(value = "#annotation(Multipart)", argNames = "multipart")
public void before(JoinPoint jp, Multipart multipart) {}
#Before("before()")
public SomeClass doStuffBeforeThing() {
SomeClass sc = new SomeClass(); //object of passed class
//do something..
return sc; //return this to annotated method(somemethod)
}
}
I want before method works execute annotation, create object of passed class(SomeClass) and the pass object of this class to annotated method. Could I do this?
You should use #Around advice instead of #Before.

Categories

Resources