| z, ? | toggle help (this) |
| space, → | next slide |
| shift-space, ← | previous slide |
| d | toggle debug mode |
| ## <ret> | go to slide # |
| c, t | table of contents (vi) |
| f | toggle footer |
| r | reload slides |
| n | toggle notes |
| p | run preshow |
@EnableXyz
@EnableWebMvc
@Configuration
public class WebConfig {
}ConversionServiceHttpMessageConverter registrationsWebMvcConfigurerWebMvcConfigurerAdapter
@EnableWebMvc // <-- What's behind?
@Configuration
public class WebConfig {
}@EnableWebMvc
@Retention(RetentionPolicy.RUNTIME)
@Import(DelegatingWebMvcConfiguration.class)
@Target(ElementType.TYPE)
public @interface EnableWebMvc {
}@EnableWebMvc
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}WebMvcConfigurationSupport@Bean methodsDelegatingWebMvcConfigurationSupportWebMvcConfigurationSupportWebMvcConfigurer implementations@AutowiredWebMvcConfigurationSupportWebMvcConfigurationSupport@EnableWebMvcWebMvcConfigurationSupport@Bean and other available methodsDefaultAnnotationHandlerMapping, AnnotationMethodHandlerAdapter, AnnotationMethodExceptionResolverRequestMappingHandlerMapping, RequestMappingHandlerAdapter, ExceptionHandlerExceptionResolverMETHOD_NOT_SUPPORTED) returned
// GET /foo Accept=text/html
@Controller
public FooController {
@RequestMapping(value = "/foo",
method=RequestMethod.GET,
headers="Accept=text/plain")
public @ResponseBody Foo getValue() {
}
}
// GET /foo Accept=text/html <- Accept header
@Controller
public FooController {
@RequestMapping(value = "/foo",
method=RequestMethod.GET,
headers="Accept=text/plain") // No match
public @ResponseBody Foo getValue() {
}
}HandlerMapping selects controllerHandlerAdapter narrows down the methodHandlerMapping typesSimpleUrlHandlerMapping + type-level @RequestMapping attributes@RequestMapping attributes deferred to HandlerAdapter
// GET /foo Accept=text/html
@Controller
public FooController {
@RequestMapping(value = "/foo",
method=RequestMethod.GET,
headers="Accept=text/plain")
public @ResponseBody Foo getValue() {
}
}
// GET /foo Accept=text/html
@Controller // (1) Controller selected based on "/foo"
public FooController {
@RequestMapping(value = "/foo",
method=RequestMethod.GET,
headers="Accept=text/plain")
public @ResponseBody Foo getValue() {
}
}
// GET /foo Accept=text/html
@Controller // (1) Controller selected based on "/foo"
public FooController {
// (2) Only to find later that method doesn't match
@RequestMapping(value = "/foo",
method=RequestMethod.GET,
headers="Accept=text/plain")
public @ResponseBody Foo getValue() {
}
}
// GET /foo Accept=text/html
@Controller // (1) Controller selected based on "/foo"
public FooController {
// (2) Only to find later that method doesn't match
// (3) Harder to reason at this stage about the error
@RequestMapping(value = "/foo",
method=RequestMethod.GET,
headers="Accept=text/plain")
public @ResponseBody Foo getValue() {
}
}HandlerInterceptorHandlerExceptionResolverHandlerMethod abstractionHandlerMapping selects a HandlerMethodHandlerAdapter invokes selected method
// GET /foo Accept=text/html
@Controller
public FooController {
@RequestMapping(value = "/foo",
method=RequestMethod.GET,
headers="Accept=text/plain")
public @ResponseBody Foo getValue() {
}
}
// GET /foo Accept=text/html
@Controller
public FooController {
// A single point of decision
@RequestMapping(value = "/foo",
method=RequestMethod.GET,
headers="Accept=text/plain")
public @ResponseBody Foo getValue() {
}
}
// GET /foo Accept=text/html
@Controller
public FooController {
// A single point of decision
// Result is 406 (NOT_ACCEPTABLE)
@RequestMapping(value = "/foo",
method=RequestMethod.GET,
headers="Accept=text/plain")
public @ResponseBody Foo getValue() {
}
}
// GET /foo Accept=text/html
@Controller
public FooController {
// A single point of decision
// Result is 406 (NOT_ACCEPTABLE)
@RequestMapping(value = "/foo",
method=RequestMethod.GET,
produces="text/plain") // <-- produces
public @ResponseBody Foo getValue() {
}
}HandlerInterceptor, HandlerExceptionResolver
@Controller
@RequestMapping(value = "/foo",
method = {RequestMethod.GET, RequestMethod.POST })
public class FooController {
@RequestMapping(method = RequestMethod.GET)
public String index() {
return "form";
}
@RequestMapping(method = RequestMethod.POST)
public String submit() {
return "success";
}
}
// Can you guess why {GET, POST} are on the type level?
@Controller
@RequestMapping(value = "/foo",
method = {RequestMethod.GET, RequestMethod.POST })
public class FooController {
@RequestMapping(method = RequestMethod.GET)
public String index() {
return "form";
}
@RequestMapping(method = RequestMethod.POST)
public String submit() {
return "success";
}
}
// Note the extra type-level HTTP methods!
@Controller
@RequestMapping(value = "/foo",
method = {RequestMethod.GET, RequestMethod.POST })
public class FooController {
@RequestMapping(method = RequestMethod.GET)
public String index() {
return "form";
}
@RequestMapping(method = RequestMethod.POST)
public String submit() {
return "success";
}
}
@Controller
@RequestMapping(value = "/foo")
public class FooController {
@RequestMapping(method = RequestMethod.GET)
public String index() {
return "form";
}
@RequestMapping(method = RequestMethod.POST)
public String submit() {
return "success";
}
}HandlerMethod:@RequestMapping@ModelAttribute@InitBinder@ExceptionHandler
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter parameter);
Object resolveArgument(
MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception;
}HandlerMethodArgumentResolver
public interface HandlerMethodReturnValueHandler {
boolean supportsReturnType(MethodParameter returnType);
void handleReturnValue(
Object returnValue,
MethodParameter returnType,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws Exception;
}HandlerMethodReturnValueHandlerRedirectAttributes Example
@RequestMapping(method=POST)
public String save(Entity entity, Errors errors,
RedirectAttributes redirectAttrs){
// ...
redirectAttrs.addAttribute("id", entity.getId);
redirectAttrs.addAttribute("date", new Date());
// ...
redirectAttrs.addFlashAttribute("message", "Yay!");
return "redirect:/someUrl";
}Model?Model has the right content for renderingRedirectAttributes when redirecting for better control
@RequestMapping(method=POST)
public String save(Entity entity, Errors errors,
Model model,
RedirectAttributes redirectAttrs){
// Use Model if not redirecting (e.g. Ajax request)
// Use RedirectAttrs otherwise
}RedirectAttributes for all redirectsRedirectAttributes
@RequestMapping(method=POST)
public String save(Entity e, Errors errors, Model model){
// ...
return "redirect:/someUrl";
}RedirectAttributes for all redirectsRequestMappingHandlerAdapter propertysetIgnoreDefaultModelOnRedirectFlashMapRedirectViewFlashMapManager tries to match request to FlashMap instances@Resource stereotype SPR-8406@ExceptionHandler methods