AspectJ, how to get line number of a method invocation

483
February 16, 2017, at 02:39 AM

I am trying to write an aspect to catch all methods execution and retrieve the line number where the methods have been invoked. For example, if I have:

public class MyMainClass {
    public static void main(String[] args){
        MyArray m = new MyArray(5);
        m.setValue //...
        //...
    }
}

I would like to write a pointcut and an advice able to get the line where the method setValue has been called (i.e. line number 4) and not the source code line, where the method has been implemented (accessible with thisJoinPoint.getSourceLocation().getLine()).

Is there any way to do this? Thanks!

UPDATE: I just found out that this code:

StackTraceElement[] trace = Thread.currentThread().getStackTrace();
System.out.println("[AspectJ]LineNumber: " + trace[trace.length-1].getLineNumber());

prints the last method invocation line number, the problem is that it is not useful when there is method invocation inside a method implementation (because, in that case I should decrease trace elements position, but I don't know how to understand when to do that).

Answer 1

Next time, please share your aspect code if you have an AspectJ question, I had to guess what was going wrong in your case. But okay, this one was easy: If you get the source location from the executing method instead of the calling method, you have used an execution() pointcut, whereas you would have needed a call() pointcut, see also the first paragraph here.

BTW, In AspectJ you do not need to inspect stack traces manually, you just use thisJoinPoint.getSourceLocation(). That part of your initial idea was correct. You just had the wrong pointcut type.

Driver application + helper class:

package de.scrum_master.app;
public class MyMainClass {
  public static void main(String[] args) {
    MyArray m = new MyArray(5);
    m.setValue(2, 11);
  }
}
package de.scrum_master.app;
public class MyArray {
  public MyArray(int i) {
    // dummy
  }
  public void setValue(int i, int j) {
    // dummy
  }
}

Aspect in native AspectJ syntax:

package de.scrum_master.aspect;
import de.scrum_master.app.MyArray;
public aspect MyAspect {
  before() : call(* MyArray.setValue(..)) {
    System.out.println(thisJoinPoint + " -> " + thisJoinPoint.getSourceLocation());
  }
}

Console log:

call(void de.scrum_master.app.MyArray.setValue(int, int)) -> MyMainClass.java:6

I much prefer the native syntax, but if you prefer the weird, annotation-based @AspectJ syntax where you need the fully qualified class name inside the pointcut, this is the equivalent aspect:

package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class MyAspect {
  @Before("call(* de.scrum_master.app.MyArray.setValue(..))")
  public void logSourceLocation(JoinPoint thisJoinPoint) {
    System.out.println(thisJoinPoint + " -> " + thisJoinPoint.getSourceLocation());
  }
}

If you have any follow-up questions, feel free to ask, but please express yourself more clearly. It is quite hard to understand what exactly you are asking.

Rent Charter Buses Company
READ ALSO
update jlabel text after opening jdialog

update jlabel text after opening jdialog

I have to query database to get data to write on JLabel and to accelerate opening dialog

378
How to ensure a webapp is compatible with any context path

How to ensure a webapp is compatible with any context path

I'm working on an existing webapp that has always been deployed to root (ie

344
Edit Java Application's Memory from C#

Edit Java Application's Memory from C#

Is it possible to edit a java application running in javawexe with the

426
Hibernate cant create entity manager factory

Hibernate cant create entity manager factory

Hello my spring boot application wont start with MySql databaseMaybe my mappings are not OK?

535