I have been wring Aspect for logging purpose. Now i am able to use before and after advice. But is it possible to call an advice after execution of certain line of business logic. Here is my current code and i want to substitute my code with advice. How to do that?
@ComponentScan @EnableCaching @EnableAutoConfiguration(exclude = {MetricFilterAutoConfiguration.class, MetricRepositoryAutoConfiguration.class}) public class Application { private final Logger log = LoggerFactory.getLogger(Application.class); @Inject private Environment env; @Inject private AppConfig appConfig; public void myBusinessLogicMethod(){ if (myVariable == 0) { log.info("No Spring profile configured, running with default configuration"); //rest of the business logic here } else { log.info("Running with number profile(s) : {}",myVariable); // //rest of the business logic here }
My Aspect class
@Aspect @Order(0) public class LoggingAspect { private final Logger log = LoggerFactory.getLogger(this.getClass()); @Inject private HttpServletRequest request; @Inject private HttpServletResponse response; @Inject private Environment env; @Pointcut("(within(com.repository.*) || within(com.service.*) || " + "within(com.web.rest.*)) && " + "!@annotation(com.aop.logging.NoLogging)") public void loggingPoincut() { } @Before("within(com.web.rest.*) && " + "!@annotation(com.aop.logging.NoLogging)") public void beforeRest(JoinPoint point) throws UnknownHostException { String ipAddress = getIpAddress(); if (log.isDebugEnabled()) { log.debug(">>>>>> From IP {}", isIpAvailble(ipAddress)); log.debug("Enter: {}.{}() with argument[s] = {}", point.getSignature().getDeclaringTypeName(), point.getSignature().getName(), Arrays.toString(point.getArgs())); } } @After("within(com.web.rest.*) && " + "!@annotation(com.aop.logging.NoLogging)") public void afterRest(JoinPoint point) throws UnknownHostException { if (log.isDebugEnabled()) { log.debug("Exit: {}.{}()", point.getSignature().getDeclaringTypeName(), point.getSignature() .getName()); log.debug("<<<<<< Rest Call Finished {} ", response.getStatus()); } } }
How can i replace tightly coupled log with an advice in my aspect class.
Advertisement
Answer
The simple answer is: With Spring AOP you can intercept method calls, not single lines of code. Even if you could, it would be a maintenance nightmare because code inside a method changes frequently. Even if you have a stable API of public methods, they are black boxes and you (or one of your colleagues) can change their internal implementation at any time.
But the solution is simple: Apply “Clean Code” principles (I hope you have read the book or know about the software craftsmanship movement from other sources already), i.e. use short methods, factoring out more complex code into smaller, re-useable, well named and maintainable pieces with low complexity. Factor out methods to the granularity level you need for logging, e.g.
public void myBusinessLogicMethod() { if (myVariable == 0) smallerBusinessLogicA(myVariable, someParameter); else smallerBusinessLogicB(myVariable); }
Then target those factored out methods with your logging pointcut and log their names, parameters and/or results (whatever you need). In order for this to work with Spring AOP, you need to be careful to
- either factor out the helper methods into other Spring component classes because Spring AOP does not support self-invocation interception
- or self-inject the bean so you can call the helper methods using the injected bean (i.e. you use the AOP proxy and not the real implementation class underneath that does not know anything about AOP)
- or switch from Spring AOP to full AspectJ which does not use proxies and works with self-invocation.
Another difference between Spring AOP and AspectJ is that the former can only target non-private methods, while AspectJ has no such limitation.