The <mvc:annotation-driven/> element implicitly registers a ExceptionHandlerExceptionResolver bean. This class has an initExceptionHandlerAdviceCache() method, which scans beans in context to find those whose class type is annotated using @ControllerAdvice .
He does this by first calling ControllerAdviceBean.findAnnotatedBeans(ApplicationContext) . Inside this method, ApplicationContext#getBeanDefinitionNames() . The javadoc of this method states
Does not consider any hierarchy in which the factory may participate
To clarify what this means. When you declare a ContextLoaderListener in a deployment descriptor, it loads what we call the root or ApplicationContext and makes it available in ServletContext . When you declare a DispatcherServlet , it creates its own ApplicationContext servlet and uses any ApplicationContext that it finds in the ServletContext attributes loaded by the ContextLoaderListener as the parent in this context. Hierarchy looks like this
Root ApplicationContext
Each ApplicationContext has access to beans in parent contexts, but not vice versa.
The above method does not recommend using beans in parent contexts and therefore only has access to beans in the current ApplicationContext ( BeanFactory really).
As such, if your
<context:component-scan .../>
declared in the root of the ApplicationContext , as I assume, from the name app-config , but
<mvc:annotation-driven />
is declared in the ApplicationContext servlet, again taking from mvc-config , then ExceptionHandlerExceptionResolver looks for @ControllerAdvice beans it won't find. It looks for beans in the servlet context, but they are not there, they are in the root context.
Sotirios delimanolis
source share