Add new features on @ControllerAdvice

Prior to this commit, @ControllerAdvice annotated beans would
assist all known Controllers, by applying @ExceptionHandler,
@InitBinder, and @ModelAttribute.

This commit updates the @ControllerAdvice annotation,
which accepts now base package names, assignableTypes,
annotations and basePackageClasses.

If attributes are set, only Controllers that match those
selectors will be assisted by the annotated class.
This commit does not change the default behavior when
no value is set, i.e. @ControllerAdvice().

Issue: SPR-10222
This commit is contained in:
Brian Clozel
2013-10-11 19:28:28 +02:00
committed by Rossen Stoyanchev
parent be4e5d2841
commit c4a8bf9c4d
7 changed files with 450 additions and 15 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -204,6 +204,35 @@ public class ExceptionHandlerExceptionResolverTests {
assertEquals("TestExceptionResolver: IllegalStateException", this.response.getContentAsString());
}
@Test
public void resolveExceptionControllerAdviceHandler() throws Exception {
AnnotationConfigApplicationContext cxt = new AnnotationConfigApplicationContext(MyControllerAdviceConfig.class);
this.resolver.setApplicationContext(cxt);
this.resolver.afterPropertiesSet();
IllegalStateException ex = new IllegalStateException();
HandlerMethod handlerMethod = new HandlerMethod(new ResponseBodyController(), "handle");
ModelAndView mav = this.resolver.resolveException(this.request, this.response, handlerMethod, ex);
assertNotNull("Exception was not handled", mav);
assertTrue(mav.isEmpty());
assertEquals("BasePackageTestExceptionResolver: IllegalStateException", this.response.getContentAsString());
}
@Test
public void resolveExceptionControllerAdviceNoHandler() throws Exception {
AnnotationConfigApplicationContext cxt = new AnnotationConfigApplicationContext(MyControllerAdviceConfig.class);
this.resolver.setApplicationContext(cxt);
this.resolver.afterPropertiesSet();
IllegalStateException ex = new IllegalStateException();
ModelAndView mav = this.resolver.resolveException(this.request, this.response, null, ex);
assertNotNull("Exception was not handled", mav);
assertTrue(mav.isEmpty());
assertEquals("DefaultTestExceptionResolver: IllegalStateException", this.response.getContentAsString());
}
private void assertMethodProcessorCount(int resolverCount, int handlerCount) {
assertEquals(resolverCount, this.resolver.getArgumentResolvers().getResolvers().size());
@@ -288,4 +317,51 @@ public class ExceptionHandlerExceptionResolverTests {
}
}
@ControllerAdvice("java.lang")
@Order(1)
static class NotCalledTestExceptionResolver {
@ExceptionHandler
@ResponseBody
public String handleException(IllegalStateException ex) {
return "NotCalledTestExceptionResolver: " + ClassUtils.getShortName(ex.getClass());
}
}
@ControllerAdvice("org.springframework.web.servlet.mvc.method.annotation")
@Order(2)
static class BasePackageTestExceptionResolver {
@ExceptionHandler
@ResponseBody
public String handleException(IllegalStateException ex) {
return "BasePackageTestExceptionResolver: " + ClassUtils.getShortName(ex.getClass());
}
}
@ControllerAdvice
@Order(3)
static class DefaultTestExceptionResolver {
@ExceptionHandler
@ResponseBody
public String handleException(IllegalStateException ex) {
return "DefaultTestExceptionResolver: " + ClassUtils.getShortName(ex.getClass());
}
}
@Configuration
static class MyControllerAdviceConfig {
@Bean public NotCalledTestExceptionResolver notCalledTestExceptionResolver() {
return new NotCalledTestExceptionResolver();
}
@Bean public BasePackageTestExceptionResolver basePackageTestExceptionResolver() {
return new BasePackageTestExceptionResolver();
}
@Bean public DefaultTestExceptionResolver defaultTestExceptionResolver() {
return new DefaultTestExceptionResolver();
}
}
}

View File

@@ -25,7 +25,9 @@ import org.junit.Test;
import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.mock.web.test.MockHttpServletResponse;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.context.support.StaticWebApplicationContext;
@@ -184,6 +186,21 @@ public class RequestMappingHandlerAdapterTests {
assertEquals("gAttr2", mav.getModel().get("attr2"));
}
@Test
public void modelAttributePackageNameAdvice() throws Exception {
this.webAppContext.registerSingleton("mapa", ModelAttributePackageAdvice.class);
this.webAppContext.registerSingleton("manupa", ModelAttributeNotUsedPackageAdvice.class);
this.webAppContext.refresh();
HandlerMethod handlerMethod = handlerMethod(new SimpleController(), "handle");
this.handlerAdapter.afterPropertiesSet();
ModelAndView mav = this.handlerAdapter.handle(this.request, this.response, handlerMethod);
assertEquals("lAttr1", mav.getModel().get("attr1"));
assertEquals("gAttr2", mav.getModel().get("attr2"));
assertEquals(null,mav.getModel().get("attr3"));
}
private HandlerMethod handlerMethod(Object handler, String methodName, Class<?>... paramTypes) throws Exception {
Method method = handler.getClass().getDeclaredMethod(methodName, paramTypes);
@@ -237,4 +254,21 @@ public class RequestMappingHandlerAdapterTests {
}
}
@ControllerAdvice({"org.springframework.web.servlet.mvc.method.annotation","java.lang"})
private static class ModelAttributePackageAdvice {
@ModelAttribute
public void addAttributes(Model model) {
model.addAttribute("attr2", "gAttr2");
}
}
@ControllerAdvice("java.lang")
private static class ModelAttributeNotUsedPackageAdvice {
@ModelAttribute
public void addAttributes(Model model) {
model.addAttribute("attr3", "gAttr3");
}
}
}