Spring Boot Aspect #Around - spring-boot

I am using Spring Boot & try to log response time of every request.
For that purpose, I am trying to #Around Aspect.
Code :
#Aspect
#Component
public class XYZ {
#Around("execution(* org.springframework.web.servlet.DispatcherServlet.service(..))")
public void doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// start stopwatch
long startTime = System.currentTimeMillis();
System.out.println("Before");
pjp.proceed();
long endTime = System.currentTimeMillis();
// stop stopwatch
System.out.println("Me here");
}
}
The code gets compiled, but the issue is that when I am executing any controller method, nothing happens. I mean my SOP should get printed but they aren't.
What am i missing?

How about this
#Pointcut("#annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void requestMapping() {}
#Pointcut("within(path.to your.controller.package.*)")
public void myController() {}
#Around("requestMapping() || myController()")
public void logAround(ProceedingJoinPoint joinPoint) throws Throwable {
...............
joinPoint.proceed();
...............
}

Related

Why #Async is not working if i keep async method in same class

Here is my code to work with #Async with CompletableFuture in Spring Boot:
#RestController
#EnableAsync
public class DemoController {
#Autowired
UtilService service;
#GetMapping("/test/{count}")
public void test(#PathVariable int count) throws InterruptedException, ExecutionException {
List<CompletableFuture<String>> list = new ArrayList<>();
long start = System.currentTimeMillis();
for(int i=0; i< count; i++) {
//CompletableFuture<String> res = calculate(i);
CompletableFuture<String> res = service.calculate(i);
list.add(res);
}
List<String> res = list.stream().map(com -> com.join()).collect(Collectors.toList());
res.forEach(System.out:: println);
System.out.println("Elapsed time: " + (System.currentTimeMillis() - start));
}
#Async
public CompletableFuture<String> calculate(int counter) throws InterruptedException{
Thread.sleep(1000L);
System.out.println("------Util------counter->" + counter);
return CompletableFuture.completedFuture("Async -" + counter);
}
}
#Service
public class UtilService {
#Async
public CompletableFuture<String> calculate(int counter) throws InterruptedException{
Thread.sleep(1000L);
System.out.println("------Util------counter->" + counter);
return CompletableFuture.completedFuture("Async -" + counter);
}
}
When i keep the async method calculate() in the same contrller class, code is not executing in asynchronus way. it is running as a synchronus method.
But when i move the async method calculate() to a different component. It is working fine.
I didnt understand why there is behavour change.
if both methods (calling, caller) are in same class, code is running synchronusly.
if both methods (calling, caller) are in different classes, code is running in asynchronusly.
Could you any one explain me is there any reason behind.
For the same reason that the #Transactional Annotation only works in a Subclass and not the same Class.
Spring creates Proxies around your Services. Some Annotations only can be used when the proxy intercepts the call and the execute the code. Does Spring #Transactional attribute work on a private method?
Static code analysis (e.g. Sonar) should warn you about such Annotations that not used.

Spring-boot AOP advice with CompletableFuture

I try to log with AOP for a CompletableFuture controller. #Before advice is working OK. But with #AfterReturning it is not working correctly in the exception case. I also tried with #AfterThrowing, which is not working either. When an exception occurs, my #AfterReturning advice also is not triggered and #AfterThrowing is never reached.
How can I use an AOP advice with exceptions in this case?
JController:
#RestController
public class JController extends BaseExceptionHandler {
#GetMapping(produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public CompletableFuture<BaseResponse> search() {
final CompletableFuture result = asyncProcessor.process(request);
return result;
}
}
BaseExceptionHandler:
public class BaseExceptionHandler {
#ExceptionHandler(Exception.class)
public ResponseEntity handleException(final Exception exception) {
return new ResponseEntity<>(new ErrorResponse(Message.INTERNAL_SERVER_ERROR, StatusCode.UNKNOWN_ERROR), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
AOP Class
#AfterReturning(value = "execution(* com.xxx.xxx.controller.*.*(..))", returning = "result")
public void outgoingSuccess(final JoinPoint joinPoint, final CompletableFuture result) {
LOGGER.debug("After Returning method: " + joinPoint.getTarget().getClass().getSimpleName());
}
#AfterThrowing("execution(* com.xxx.xxx.controller.*.*(..))")
public void outgoingError(final JoinPoint joinPoint) {
LOGGER.debug("After Throwing method: " + joinPoint.getTarget().getClass().getSimpleName());
}

Logging elapsed time of execution in SpringBoot rest API

It could be a simple solution but I am unable to get it done.
I need to log the overall execution time for my request in SpringBoot Rest API.
Request always enters to MainController always and can exit from two places-
Main Restcontroller same method or
ExceptionHandlerController handler method
I have created one custom annotation and injecting it to both main Controller and ExceptionController methods and getting elapsed time for individual methods.
So here is the problem. I need to add these individual times to calculate the total time which I don't want.
Is there any other way to log this information easily.
Aspect class:
#Aspect
#Component
public class PointsAspect {
private final static Logger logger = LoggerFactory.getLogger(PointsAspect.class);
#Around("#annotation(annotation)")
public Object logMethod(final ProceedingJoinPoint proceedingJoinPoint, final LogAround annotation)
throws Throwable {
final long start = System.currentTimeMillis();
Object obj;
try {
logger.debug("Starting...! Method Name - " +proceedingJoinPoint.getSignature().getName());
obj = proceedingJoinPoint.proceed();
} finally {
logger.debug("Exiting...! Method Name - " +proceedingJoinPoint.getSignature().getName() +"Execution Time in Milliseconds:> "+ String.valueOf(System.currentTimeMillis()-start));
}
return obj;
}
}
Marker Interface:
#Target({ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface LogAround {
}
And this is how I am injecting it:
**ExceptionHandlerController.java**
#LogAround
#ExceptionHandler(HttpMessageNotReadableException.class)
public GenericFailureResponse missingRequestBodyException(HttpServletResponse response,
HttpServletRequest request, Exception ex) throws IOException {
GenericFailureResponse failureResponse = new GenericFailureResponse();
//TODO: exception logic
return failureResponse;
}
**MainController.java**
#LogAround
public String getTotalPoints(#RequestParam(required=true) long memberNo,
HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
//TODO : some logic
return "something";
}
You can use a simple filter.
#Component
public class LogTimeFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
long startTime = System.currentTimeMillis();
chain.doFilter(request, response);
long duration = System.currentTimeMillis() - startTime;
System.out.println("Request take " + duration + " ms");
}
}

Spring Aspect on Converter

I created simple Aspect an annotation for measuring time of execution of annotated method.
When I annotate method of a simple Spring Bean, inject bean, and run it like bean.annotatedMethod(), everything works fine.
However, when I annotate convert() method on Spring Converter, annotation is ignored. I'm guessing the reason is that convert() is called internally by Spring's ConversionService, and somehow Aspects are not respected. Is there any way to get it to work?
Annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface LogExecTime {
}
Aspect, which I register in Spring:
#Aspect
#Component
public class LogTimeAspect {
#Around(value = "#annotation(annotation)")
public Object LogExecutionTime(final ProceedingJoinPoint joinPoint, final LogExecTime annotation) throws Throwable {
final long startMillis = System.currentTimeMillis();
try {
System.out.println("Starting timed operation");
final Object retVal = joinPoint.proceed();
return retVal;
} finally {
final long duration = System.currentTimeMillis() - startMillis;
System.out.println("Call to " + joinPoint.getSignature() + " took " + duration + " ms");
}
}
}
This works fine:
#Component
public class Operator {
#LogExecTime
public void operate() throws InterruptedException {
System.out.println("Performing operation");
Thread.sleep(1000);
}
}
#Bean
protected Void test(Operator o) {
o.operate();
return null;
}
But here, annotation is ignored:
public class SampleConverter implements Converter<SourceType, ResultType> {
#Override
#LogExecTime
public ImmutableData convert(#Nonnull ClassifiedEvent result) {
...
}
}
ConversionService conversionService;
...
conversionService.convert(source, ResultType.class));
Solved by comment of #EssexBoy, my converter was not a spring managed bean.

Run aspect on proxy object

I have following simple service:
#Service
public class TestServiceImpl implements TestService {
#Override
public void countExternal(Integer arg1) {
System.out.println("test - lock external");
count(arg1, new Integer(1));
}
public void count(Integer arg1, Integer arg2) {
System.out.println("test - lock internal");
}
}
that implements my simple interface:
public interface TestService {
void countExternal(Integer arg1);
}
Here's the aspect that I am using to do some validation etc. during count method:
#Aspect
#Component
public class TestAdvicer {
#Around("execution(* count(..))")
public Object advice(ProceedingJoinPoint joinPoint) throws Throwable {
// do som magic here
return joinPoint.proceed();
}
}
In my Spring configuration I have included autoproxying:
#EnableAspectJAutoProxy(proxyTargetClass = true)
Unfortunately, my TestAdvicer is never executed since count method is invoked from countExternal method. count method is executed on Proxy object and because of that advice didn't run.
Do you know how can I run my advice on Proxy object? What is the best way to solve this problem?

Resources