Spring AOP: capture method only called from another method - spring

Guess I have these methods:
#Service
public class Service1 {
private #Autowired Service2 service2;
public void method1() {
this.service2.method2();
}
}
#Service2
public class Service2 {
public void method2() {
// Do something
}
}
I'd like to know how to capture Service2.method2() call, when it's called from Service1.method1()).
Any ideas?

You can either use AspectJ from within Spring instead of Spring AOP vis compile-time or load-time weaving and then a pointcut like
execution(* my.package.Service2.method2()) &&
cflow(execution(* my.package.Service2.method2()))
or, similar to what #Damith said, use either of
Thread.currentThread().getStackTrace(),
new Exception().getStackTrace() or
the Java 9 StackWalker API
in order to navigate the call stack manually and find the information you are looking for. My suggestion is to use AspectJ, not just because of its elegance but also because of its efficiency.

Related

Is it possible to add PointCut to ModelAndView method?

I tried to use PointCut to perform some post action after ModelAndView.setViewName, but it seems that it never triggers:
#Aspect
#Component
public class TestAspect {
private Logger logger = LoggerFactory.getLogger(this.getClass());
#Pointcut("execution(* org.springframework.web.servlet.ModelAndView.*(..))")
public void testPointCut() {
}
#After("testPointCut()")
public void afterPointCut(JoinPoint joinPoint) {
logger.debug("afterPointCut");
}
}
If I change the execution part to some class of my own, this point cut works.
So what is the correct way to add PointCut to ModelAndView?
I am not a Spring user, but what I know about Spring AOP is that you can only apply it to Spring components. The class ModelAndView is not derived from any Spring core component class or annotated by anything making it such, it is a simple POJO. As such you cannot target it by Spring AOP pointcuts. You should rather target something within the reach of Spring AOP.
The alternative would be to unpack the big gun and use full AspectJ LTW (load-time weaving) which is not limited to Spring components.

Where to write Common code in spring boot

I want to write common code which should be execute before every method,
Where can I place this code in spring.
Thanks in advance.
What you ask is not trivial but Aspect Oriented Programming (AoP) is one way to achieve that. This description assumes that you are somewhat familiar with the Proxy class, the InvocationHandler interface and the Interceptor pattern in general. As I said, not a totally trivial matter.
Define the logic that you what to be executed before every method, or some method or whatever. Usually it is some kind of Interceptor, this is an example:
public class TimeProfilerInterceptor implements MethodInterceptor {
#Getter
private final TimeStatistics statistics = new TimeStatistics();
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
StopWatch watch = new StopWatch();
try {
watch.start();
Object returnValue = invocation.proceed();
return returnValue;
}
finally {
// etc...
}
}
}
Define a place where your logic is wired to your methods. In this example, the place is a Spring component that extends AbstractBeanFactoryAwareAdvisingPostProcessor and implements InitializingBean. The afterPropertiesSet method is called by Spring once the initialization of the bean is done. The method uses the Advice class from spring-aop to identify the pointcuts i.e. the methods that must be wrapped by the interceptor. In this case, it is an annotation based pointcut, meaning that it matches every method that has a certain custom annotation on it (TimeProfiled).
#Component
public class TimeProfiledAnnotationPostProcessor
extends AbstractBeanFactoryAwareAdvisingPostProcessor
implements InitializingBean {
#Autowired
TimeProfilerInterceptor timeProfilerInterceptor;
#Override
public void afterPropertiesSet() throws Exception {
this.setProxyTargetClass(true);
Advice advice = timeProfilerInterceptor;
Pointcut pointcut = new AnnotationMatchingPointcut(null, TimeProfiled.class);
this.advisor = new DefaultPointcutAdvisor(pointcut, advice);
}
}
Define your custom annotation to use where needed.
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface TimeProfiled {
}
Tell Spring to initiate the wrapping mechanism at startup via the following annotation upon a Spring Configuration or a SpringBootApplication:
#ComponentScan(basePackageClasses = TimeProfiledAnnotationPostProcessor.class)
#EnableAspectJAutoProxy(proxyTargetClass = true)
You can change the pointcut so that it matches other methods with other criteria, there is an entire syntax to do that, a world in itself, this is just a small example...
You should have a look at Spring AOP. With Spring AOP you can write Aspects which can be common code which is executed before/after a method. The following example is a simple Aspect:
#Aspect
public class EmployeeAspect {
#Before("execution(public String getName())")
public void getNameAdvice(){
System.out.println("Executing Advice on getName()");
}
#Before("execution(* your.package.name.*.get*())")
public void getAllAdvice(){
System.out.println("Service method getter called");
}
}
Within the #Before() annotation you can specify the exact method which is surrounded with the Aspect or you use the wildcard * to specify more methods. For this, you should be familiar with Pointcut expressions.

How to intercept a single method using SpringBoot AOP

I'm trying to output a log message whenever the function someFunction() gets invoked.
This is my Aspect:
#Aspect
#Component
public class MyAspect {
private static final Logger LOGGER = Logger.getLogger(MyAspect.class.getName());
#Pointcut("execution(com.practice.AOP.someFunction())")
public void outputLogMessage() {
LOGGER.info("someFunction has been invoked");
}
}
The method i'm trying to intercept, someFunction(), is in the com.practice.AOP class. When I invoke it (shown below), my Advice (the log message) doesn't output, nor do I get an error. What am I doing wrong? Is Pointcut even the way to go?
#SpringBootApplication
public class AOP {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
someFunction();
}
public static void someFunction() {
//should invoke the log message
}
}
Spring AOP only works on Spring beans, and only on public instance methods of those spring beans invoked from the outside (i.e. this.publicMethod() style invocations will not work, as they are not going through the proxies that Spring AOP creates to advise your spring beans.
If that is not enough for you, for instance if you need to advise not just spring beans but non-spring managed code as well, or static methods like in your example, you will need to switch to native AspectJ support, either by compile time weaving, or by load time weaving.

Is spring boot aspect working on method inside the scheduled method

For spring boot application.
I have my aspect listen on my private or public method inside my scheduled method.
But it doesn't work. However, the aspect can listen on my scheduled method.
Here is an example on my github.
https://github.com/benweizhu/spring-boot-aspect-scheduled
Does any know the answer or why? or how to resolve it?
Thanks
Aspects will not work on calling other methods within the same class as it cannot be proxied.
It means that self-invocation is not going to result in the advice associated with a method invocation getting a chance to execute.
Okay, so what is to be done about this? The best approach (the term best is used loosely here) is to refactor your code such that the self-invocation does not happen
note on proxying private methods :
Due to the proxy-based nature of Spring’s AOP framework, protected methods are by definition not intercepted, neither for JDK proxies (where this isn’t applicable) nor for CGLIB proxies (where this is technically possible but not recommendable for AOP purposes). As a consequence, any given pointcut will be matched against public methods only!
If your interception needs include protected/private methods or even constructors, consider the use of Spring-driven native AspectJ weaving instead of Spring’s proxy-based AOP framework. This constitutes a different mode of AOP usage with different characteristics, so be sure to make yourself familiar with weaving first before making a decision.
refer : How can I log private methods via Spring AOP?
change the code as below:
#Component
public class Test{
public void reportInPrivateMethod() {
System.out.println("private method");
}
public void reportInPublicMethod() {
System.out.println("public method");
}
}
Now invoke this method :
#Component
public class ScheduledTasks {
#Autowired
private Test test;
private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
#Scheduled(fixedRate = 5000)
public void reportCurrentTime() {
test.reportInPrivateMethod();
test.reportInPublicMethod();
log.info("The time is now {}", dateFormat.format(new Date()));
}
}
Modify the aspects as per the changes :
#Aspect
#Component
public class Monitor {
#AfterReturning("execution(* com.zeph.aop.ScheduledTasks.reportCurrentTime())")
public void logServiceAccess(JoinPoint joinPoint) {
System.out.println("Completed: " + joinPoint);
}
#AfterReturning("execution(* com.zeph.aop.Test.reportInPrivateMethod())")
public void logServiceAccessPrivateMethod() {
System.out.println("Completed PRIVATE :");
}
#AfterReturning("execution(* com.zeph.aop.Test.reportInPublicMethod())")
public void logServiceAccessPublicMethod() {
System.out.println("Completed PUBLIC: ");
}
}

Spring AOP: Annotation on any method called x not working

I am getting started with AOP for the first time.
I have my first aspect as follows:
#Aspect
public class SyncLoggingAspect {
private final Logger logger = Logger.getLogger(this.getClass());
#Before("execution(public * *(..))")
public void anyPublic() {
System.out.println("HIT POINTCUT");
}
}
This sucessfully proceeds to be invoked on any method call which is public. However when I change it to this:
#Before("execution(public * doPoll(..))")
public void anyPublic() {
System.out.println("HIT POINTCUT");
}
I would expect it to work on any public method called "doPoll", yet when a method such as this is called nothing happens:
public class GmailContactPoller extends ContactPoller<GoogleUser, ContactPusher<GoogleUser>> {
Logger logger = Logger.getLogger(this.getClass());
#Override
public List<? extends ContactPusher<GoogleUser>> doPoll() throws PollException {
...
}
}
Is there something I am missing with the EL syntax? Or is this something to do with the inheritance hierarchy? The superclass method of doPoll is abstract in an abstract class called Poller. Does not having an interface cause problems?
Edit: I just noticed my IDE enables spring aspect tooling, and now I have the following compiler warning by the method:
"Description Resource Path Location Type
advice defined in datasync.aop.aspects.SyncLoggingAspect has not been applied [Xlint:adviceDidNotMatch] SyncLoggingAspect.java /DataSync/src/main/datasync/aop/aspects"
Spring AOP Proxies and aspectJ had some differences mainly:
Spring AOP only works on public methods.
Spring AOP does not work for self invocations.
You can have a look to sections 8.4 & 8.5 of Spring's Documentation for more information.
Currently you have two solutions:
refactor your code to remove the need for self invocation
use AspectJ rather than Spring AOP proxies either at compile time or load time.
Try:
#Before("execution(public * *.doPoll(..))")
public void anyPublic() {
System.out.println("HIT POINTCUT");
}

Resources