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.
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);
}
}
Tweeted the code a bit as I cannot share the actual code. Hope I made the concept clear.
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) {
// ...
}
}
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;
}
}