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
CodePudding user response:
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)
.