javareflectionaopaspectjpointcut

Access user-defined object state using java reflection in @Aspect type class


I have a class A which declare a static logger instance and then used in the method of the class.

class A {
  static Log log = LogFactory.getLog(A.class);
   person p = new Person();
   public void methodA(){
     // log.info("logging using apache commons logger");
      }
  }

I have another class B which is @Aspect type. Its method may trigger based on execution of A's method.

@Aspect
 class B{

  @Before(--execution of methodA() pointcut expression--)
   public void methodB(JoinPoint jp){
   // How to get the class A's logger instance to continue logging from same logger instance. As it will print class name   A in logs
        }

  }

I want to use the logger instance used in class A to log some message.

How can I get the object's state in Aspect class from joinPoint with reflection.

I am not using spring framework, trying with aspectjrt apectweaver jars


Solution

  • You can either use jp.getTarget() to get the target instance for a non-static method or simply bind target(targetObject) to an advice method parameter. I am showing you the latter. Here is my MCVE:

    Log4J configuration:

    log4j.rootLogger=DEBUG, consoleAppender
    log4j.appender.consoleAppender=org.apache.log4j.ConsoleAppender
    log4j.appender.consoleAppender.layout=org.apache.log4j.PatternLayout
    log4j.appender.consoleAppender.layout.ConversionPattern=[%t] %-5p %c %x - %m%n
    

    Helper class:

    package de.scrum_master.app;
    
    public class Person {}
    

    Driver application class:

    package de.scrum_master.app;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    public class Application {
      private static Log log = LogFactory.getLog(Application.class);
      private Person p = new Person();
    
      public void methodA() {}
    
      public static void main(String[] args) {
        Application application = new Application();
        application.methodA();
      }
    }
    

    Aspect:

    package de.scrum_master.aspect;
    
    import java.lang.reflect.Modifier;
    import java.util.Arrays;
    
    import org.apache.commons.logging.Log;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    
    @Aspect
    public class MyAspect {
      @Before("execution(* methodA()) && target(targetObject)")
      public void logMethodExecution(JoinPoint jp, Object targetObject) {
        // Filter for static logger, make it accessible and use it for logging 
        Arrays.stream(targetObject.getClass().getDeclaredFields())
          .filter(field -> field.getType().equals(Log.class) && Modifier.isStatic(field.getModifiers()))
          .forEach(field -> {
            try {
              field.setAccessible(true);
              ((Log) field.get(null)).info(jp + " -> logging using apache commons logger");
            } catch (IllegalArgumentException | IllegalAccessException e) {
              e.printStackTrace();
            }
          });
      }
    }
    

    Console log:

    [main] INFO  de.scrum_master.app.Application  - execution(void de.scrum_master.app.Application.methodA()) -> logging using apache commons logger
    

    This kind of reflection stuff is quite ugly and might not work the way you wish in JMS-style modular applications without configurative precautions, if aspect and target class are not in the same module. At least, you could make your loggers public in order to avoid using field.setAccessible(true).