Reputation: 147
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.
Upvotes: 7
Views: 59126
Reputation: 31
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);
}
}
Upvotes: 1
Reputation: 364
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.
Upvotes: 9
Reputation: 1252
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) {
// ...
}
}
Upvotes: 9
Reputation: 147
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;
}
}
Upvotes: 2