Search code examples
javaspring-bootspring-aop

Is it possible to build a @AfterThrowing advice on any execution with a given exception type?


So I have a working example of an advice that should run after an exception is thrown with a specific annotation above it. So ExtendedErrorHandling is the annotation used above a class and/or method. When that method returns a specific error of type ExtendedHttpResponseException, the advice should run.

However, the original idea was to do this without annotations.

AS-IS:

@AfterThrowing(pointcut = "execution(* *(..)) && (extendedThrowingInMethod() || extendedThrowingInClass())", throwing = "ex")

TO-BE:

@AfterThrowing(pointcut = "execution(* *(..))", throwing = "ex")

When we try to achieve this, spring refuses to start, with a very generic error message "tomcat could not startup", but also hinting that the advice isn't ok.

The error:

Caused by: org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
    at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:142)
    at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.<init>(TomcatWebServer.java:104)
    at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:473)
    at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:206)
    at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory$$FastClassBySpringCGLIB$$9c83fa9f.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:783)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753)
    at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:64)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698)
    at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory$$EnhancerBySpringCGLIB$$78134ef1.getWebServer(<generated>)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:182)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:160)
    ... 78 more
Caused by: java.lang.IllegalStateException: StandardEngine[Tomcat].StandardHost[localhost].TomcatEmbeddedContext[] failed to start
    at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.rethrowDeferredStartupExceptions(TomcatWebServer.java:187)
    at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:126)
    ... 96 more

The aspect completely:

@Aspect
@Component
public class ExtendedHttpResponseExceptionCatcherAdvisor {

@Pointcut("@annotation(com.atlascopco.common.dto.handlers.annotations.ExtendedErrorHandling)")
public void extendedThrowingInMethod() {

}

@Pointcut("@within(com.atlascopco.common.dto.handlers.annotations.ExtendedErrorHandling)")
public void extendedThrowingInClass() {

}

@AfterThrowing(pointcut = "execution(* *(..)) && (extendedThrowingInMethod() || extendedThrowingInClass())", throwing = "ex")
public void handleThrownKnownExceptions(ExtendedHttpResponseException ex) {
    ResponseContext responseContext = ResponseContextHolder.getResponseContext();
    GenericHttpResponseModel responseModel = responseContext.getResponseModel();
    responseModel.getErrors().add(ex);
    responseContext.setResponseModel(responseModel);
}

}

So the questions here are: Why does spring crash in the TO-BE situation? And is it possible to achieve the TO-BE situation without any problems, or do we run against some limitations?

Thanks for your help in advance!


Solution

  • Thanks to @R.G.

    The solution is to be more specific when it comes to class definition. EG:

    execution(* com.your.project..*(..))