(SOLUTION posted at the end of the Question)
I am building a Spring 4 MVC app. And it is completely configured using Java Annotations. There is no web.xml
. The app is configured by using instance of AbstractAnnotationConfigDispatcherServletInitializer
and WebMvcConfigurerAdapter
like so,
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"com.example.*"})
@EnableTransactionManagement
@PropertySource("/WEB-INF/properties/application.properties")
public class WebAppConfig extends WebMvcConfigurerAdapter {
...
}
and
public class WebAppInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
...
}
I am now trying to add a global/catch-all exception handler for 404 pages i.e. HttpStatus.NOT_FOUND
but no success. Below are some of the ways I tried.
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
@ControllerAdvice
public class GlobalExceptionHandlerController {
@ExceptionHandler
@ResponseStatus(HttpStatus.NOT_FOUND)
public ModelAndView handleException (NoSuchRequestHandlingMethodException ex) {
ModelAndView mav = new ModelAndView();
return mav;
}
@ExceptionHandler
@ResponseStatus(HttpStatus.NOT_FOUND)
public ModelAndView handleExceptiond (NoHandlerFoundException ex) {
ModelAndView mav = new ModelAndView();
return mav;
}
@ResponseStatus(HttpStatus.NOT_FOUND)
@ExceptionHandler(NoHandlerFoundException.class)
public void handleConflict() {
}
@ResponseStatus(HttpStatus.NOT_FOUND)
@ExceptionHandler(NoSuchRequestHandlingMethodException.class)
public void handlesdConflict() {
}
}
None of these methods get executed. I am at a loss as to how to handle this. I do not want to use web.xml
becasue then I would have to create one just for this.
SOLUTION
I did what @Sotirios mentioned here. Here is my portion WebAppInitializer
code that will set the dispatcher properly.
public class WebAppInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
...
@Override
protected void registerDispatcherServlet(ServletContext servletContext) {
String servletName = super.getServletName();
Assert.hasLength(servletName, "getServletName() may not return empty or null");
WebApplicationContext servletAppContext = super.createServletApplicationContext();
Assert.notNull(servletAppContext,
"createServletApplicationContext() did not return an application " +
"context for servlet [" + servletName + "]");
DispatcherServlet dispatcherServlet = new DispatcherServlet(servletAppContext);
//>>> START: My custom code, rest is exqact copy of the super class
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
//>>> END
ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
Assert.notNull(registration,
"Failed to register servlet with name '" + servletName + "'." +
"Check if there is another servlet registered under the same name.");
registration.setLoadOnStartup(1);
registration.addMapping(getServletMappings());
registration.setAsyncSupported(isAsyncSupported());
Filter[] filters = getServletFilters();
if (!ObjectUtils.isEmpty(filters)) {
for (Filter filter : filters) {
super.registerServletFilter(servletContext, filter);
}
}
super.customizeRegistration(registration);
}
...
}
Although I am not particularly happy with this solution because my only custom code is 1 line & rest is directly copied from super class. I wish there was a nicer way to do this.