1


I Have a problem with Spring AOP. I want to get the object of Student in a Pointcut. But my JoinPoints can have this object in any priority.
Look at the snipped below code for the two different JoinPoints and the Pointcut that I have Created:

public Student createStudent(String s, Student student) {...}
public Student updateStudent(Student student, String s) {...}

@Before("args(..,com.hadi.student.Student)") 
public void myAdvice(JoinPoint jp) {
    Student student = null;
    for (Object o : jp.getArgs()) {
        if (o instanceof Student) {
            student = (Student) o;
        }
    }
}

The above code works only for the first JoinPoint. So the question is how to create a Pointcut that will be executed for any situation of Student in input parameter.
I could not use below code, it throws runtimeException:
@Before("args(..,com.hadi.student.Student,..)")

I make the code easy to understand, actually my Poincut is so bigger than this. So please just answer with args way.

kriegaex
  • 63,017
  • 15
  • 111
  • 202
HadiMohammadi
  • 133
  • 3
  • 8

1 Answers1

6

I have answered similar questions several times, e.g. here:

Your case is a bit simpler because you just want to extract a parameter and not its annotation. So along the lines of those other two answers you would use a pointcut like this:

@Before("execution(* *(.., com.hadi.student.Student, ..))")

and then extract the argument in your advice via iterating over thisJoinPoint.getArgs() and checking for the right parameter type. This is slower and uglier than directly binding a method argument to an advice parameter via args(), but your only option for parameters in arbitrary positions because args(.., Student, ..) will yield an "ambiguous argument binding" error. This is because both AspectJ and Spring AOP cannot decide what should happen if there is more than one Student argument in your method. Which one should they choose?

Here is an MCVE in AspectJ (no Spring required, but works the same way there):

Helper class and driver application:

package de.scrum_master.app;

public class Student {
  private String name;

  public Student(String name) {
    this.name = name;
  }

  @Override
  public String toString() {
    return "Student [name=" + name + "]";
  }
}
package de.scrum_master.app;

public class Application {
  public void doSomething() {}

  public Student createStudent(String s, Student student) {
    return student;
  }

  public Student updateStudent(Student student, String s) {
    return student;
  }

  public void marryStudents(Student student1, Student student2) {}

  public static void main(String[] args) {
    Application application = new Application();
    application.doSomething();
    application.createStudent("x", new Student("John Doe"));
    application.updateStudent(new Student("Jane Doe"), "y");
    // What happens if we have multiple Student parameters?
    application.marryStudents(new Student("Jane"), new Student("John"));
  }
}

Aspect:

package de.scrum_master.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import de.scrum_master.app.Student;

@Aspect
public class MyAspect {
  @Before("execution(* *(.., de.scrum_master.app.Student, ..))")
  public void interceptMethodsWithStudentArgs(JoinPoint thisJoinPoint) throws Throwable {
    System.out.println(thisJoinPoint);
    for(Object arg : thisJoinPoint.getArgs()) {
      if (!(arg instanceof Student))
        continue;
      Student student = (Student) arg;
      System.out.println("  " + student);
    }
  }
}

Console log:

execution(Student de.scrum_master.app.Application.createStudent(String, Student))
  Student [name=John Doe]
execution(Student de.scrum_master.app.Application.updateStudent(Student, String))
  Student [name=Jane Doe]
execution(void de.scrum_master.app.Application.marryStudents(Student, Student))
  Student [name=Jane]
  Student [name=John]
kriegaex
  • 63,017
  • 15
  • 111
  • 202