Exception handling through spring AOP + Aspectj
Asked Answered
C

4

6

In my project I have a domain layer which is basically POJO and a Spring controller / service layer that is sitting on top of the domain layer. I also have an AOP layer which is sitting between the service and domain.

My domain layer is throwing business exceptions which are now being handled in the service layer.

However I want to change it so that exception thrown from domain layer will be handled in the AOP layer. AOP layer will some kind of error response and send it back to spring controller/ web service layer.

I can create a IBizResponse and make two subclasses/interfaces of it perhaps a SuccessResponse and an ErrorResponse and make my domain layer methods return IBizResponse. However I am not able to figure out how to make AOP return the ErrorResponse object to the service layer.

Cere answered 17/7, 2014 at 7:17 Comment(0)
B
9

See After throwing advice section of https://docs.spring.io/spring/docs/4.1.0.RELEASE/spring-framework-reference/htmlsingle/#aop-introduction-defn

After throwing advice runs when a matched method execution exits by throwing an exception. It is declared using the @AfterThrowing annotation:

Examples

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;

@Aspect
public class AfterThrowingExample {

   @AfterThrowing("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
   public void doRecoveryActions() {
     // ...
    }

}



import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;

@Aspect
public class AfterThrowingExample {

    @AfterThrowing(
    pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
    throwing="ex")
    public void doRecoveryActions(DataAccessException ex) {
       // ...
     }

}
Bromine answered 17/7, 2014 at 8:16 Comment(0)
D
9

I came across the same scenario where I had to return an error response DTO in case of any exception handling. Inside @Aspect class,

@Aspect
@Component
public class MyAspect{

    private static final Logger LOGGER = LoggerFactory.getLogger(MyAspect.class);

    @Pointcut("execution(* com.linda.dao.strategy.*.*(..))")
    public void strategyMethods() { }

    @Pointcut("execution(* com.linda.controller.*.*(..)) || execution(* com.linda.Manager.*(..))")
    public void controllerMethods(){  }

    @Around("strategyMethods()")
    public Object profileStrategyMethods(ProceedingJoinPoint pjp) throws Throwable {

        long start = System.currentTimeMillis();
        Object output = null;
        LOGGER.info("Class:"+pjp.getTarget().getClass()+" entry -> method ->"+pjp.getSignature().getName());
        try{
            output = pjp.proceed();
            long elapsedTime = System.currentTimeMillis() - start;
            LOGGER.info("Method execution time: " + elapsedTime + " milliseconds.");
            LOGGER.info("Class:"+pjp.getTarget().getClass()+" exit -> method ->"+pjp.getSignature().getName());
        }catch(Throwable t){
            throw new InternalServerException(t.getMessage());  
        }

        return output;
    }

    @AfterThrowing(pointcut="execution(* com.linda.dao.strategy.*.*(..)) || execution(* com.linda.controller.*.*(..)) || execution(* com.linda.Manager.*(..))",throwing = "ex")
    public void doRecoveryActions(JoinPoint joinPoint, Throwable ex) {

        Signature signature = joinPoint.getSignature();
        String methodName = signature.getName();
        String stuff = signature.toString();
        String arguments = Arrays.toString(joinPoint.getArgs());
        LOGGER.error("Write something in the log... We have caught exception in method: "
                + methodName + " with arguments "
                + arguments + "\nand the full toString: " + stuff + "\nthe exception is: "
                + ex.getMessage());
    }
}

Defined another class for exception handling like below:

@ControllerAdvice
public class ExceptionLogAdvice {

    @ExceptionHandler(InternalServerException.class)
    @ResponseStatus(HttpStatus.BAD_GATEWAY)
    @ResponseBody
    public ResponseEntity<Object> handleValidationException(final InternalServerException internalServerException){

        ErrorResponseDTO dto = constructErrorResponse(internalServerException);
        return ResponseEntity.status(HttpStatus.BAD_GATEWAY).body(dto);
    }
}

Tweaked the code a bit as I cannot share the actual code. Hope I made the concept clear.

Dementia answered 22/11, 2017 at 8:34 Comment(0)
C
2

Considering com.sc.bs.impl.* is business/domain layer package and intercepted it in AOP layer using @Around annotation. Code snippet:

@Around("execution(* com.sc.bs.impl..*.*(..))")
public Object exceptionHandlerWithReturnType(ProceedingJoinPoint joinPoint) throws Throwable{
    try {
        obj = joinPoint.proceed();
    } catch(Exception ex) {
        throw ex;
    }
}
Cere answered 11/4, 2015 at 7:33 Comment(2)
Your try catch is a little useless isn't it?Fenella
Yeah, but i used for some other purpose like auditing and transaction commit and roll back.Cere
T
1

Consider using global exception handler by @ExceptionHandler and @ControllerAdvice

Create a CustomExceptionHandler. Add @ControllerAdvice annotation at class level. And add @ExceptionHandler annotation for each exception you want to handle. Remember the order of the @ExceptionHandlers matters — once it matches to one exception, it will not continue to check following handlers.

// in CustomExceptionHandler

@ControllerAdvice
public class CustomExceptionHandler{

    @ExceptionHandler(value = {NullPointerException.class})
    public ResponseEntity<ErrorResponse> handleDemoNotFoundException(NullPointerExceptione){
        return new ResponseEntity(ErrorResponse.builder().message("Null Pointer").build(), HttpStatus.OK);
    }

    @ExceptionHandler(value = {Exception.class})
    public ResponseEntity handleException(Exception e){
        return new ResponseEntity(ErrorResponse.builder().message(e.getMessage()).build(), HttpStatus.OK);
    }

}

My other classes for reference and testing

// ErrorResponse Class. I am using lombok

@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ErrorResponse {
    private String message;
}

// controller
@RestController
public class DemoController {

    @GetMapping("/exception")
    public ResponseEntity<MyResponse> errorDemoEndPoint() throws NullPointerException {
        MyResponse response = MyResponse.builder().build();
        throw new NullPointerException("Null Pointer");
        return ResponseEntity.ok(response);
    }
}

Tibia answered 29/9, 2022 at 20:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.