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 {
}
ConversionService
HttpMessageConverter
registrationsWebMvcConfigurer
WebMvcConfigurerAdapter
@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
methodsDelegatingWebMvcConfigurationSupport
WebMvcConfigurationSupport
WebMvcConfigurer
implementations@Autowired
WebMvcConfigurationSupport
WebMvcConfigurationSupport
@EnableWebMvc
WebMvcConfigurationSupport
@Bean
and other available methodsDefaultAnnotationHandlerMapping
, AnnotationMethodHandlerAdapter
, AnnotationMethodExceptionResolver
RequestMappingHandlerMapping
, RequestMappingHandlerAdapter
, ExceptionHandlerExceptionResolver
METHOD_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() {
}
}
HandlerInterceptor
HandlerExceptionResolver
HandlerMethod
abstractionHandlerMapping
selects a HandlerMethod
HandlerAdapter
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;
}
HandlerMethodReturnValueHandler
RedirectAttributes
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
propertysetIgnoreDefaultModelOnRedirect
FlashMap
RedirectView
FlashMapManager
tries to match request to FlashMap
instances@Resource
stereotype SPR-8406@ExceptionHandler
methods