一、@Controller 、@RestController 和 @ControllerAdvice
1. @Controller
@Controller 用于标记在一个类上,使用它标记的类就是一个SpringMVC Controller 对象。分发处理器将会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping 注解。
使用@Controller 注解,在对应的方法上,视图解析器可以解析return 的jsp,html页面,并且跳转到相应页面;
若返回json等内容到页面,则需要加@ResponseBody注解。
2. @RestController
@RestController注解相当于@ResponseBody + @Controller合在一起的作用。
返回json数据不需要在方法前面加@ResponseBody注解了,但使用@RestController这个注解,就不能返回jsp,html页面,视图解析器无法解析jsp,html页面,返回的内容就是return 里的内容。
若使用@RestController,又想返回页面,则需要使用ModelAndView:
public ModelAndView login(){ ModelAndView mv = new ModelAndView("index"); return mv; }
3. @ControllerAdvice
@ControllerAdvice,是Spring3.2后提供的新注解,从名字上可以看出大体意思是控制器增强。
ControllerAdvice拆分开来就是Controller Advice,关于Advice,其是用于封装一个切面所有属性的,包括切入点和需要织入的切面逻辑。这里ContrllerAdvice也可以这么理解,其抽象级别应该是用于对Controller进行“切面”环绕的,而具体的业务织入方式则是通过结合其他的注解来实现的(@ControllerAdvice并不是使用AOP的方式来织入业务逻辑的,而是Spring内置对其各个逻辑的织入方式进行了内置支持)。@ControllerAdvice是在类上声明的注解,其用法主要有三点:
(1) 全局异常处理,结合方法型注解@ExceptionHandler,用于捕获Controller中抛出的指定类型的异常,从而达到不同类型的异常区别处理的目的
@ControllerAdvice(basePackages = "mvc") public class SpringControllerAdvice { @ExceptionHandler(RuntimeException.class) public ModelAndView runtimeException(RuntimeException e) { e.printStackTrace(); return new ModelAndView("error"); } }
在该接口中抛出了RuntimeException,那么理论上,这里的异常捕获器就会捕获该异常,然后返回默认的error视图。以下是UserController的代码:
@RequestMapping(value = "/detail", method = RequestMethod.GET) public ModelAndView detail(@RequestParam("id") long id) { ModelAndView view = new ModelAndView("user"); User user = userService.detail(id); view.addObject("user", user); throw new RuntimeException("mock user detail exception."); }
在UserController中抛出的异常能够被SpringControllerAdvice捕获。
这里需要注意的是,统一处理异常的controller需要放在和普通controller同级的包下,或者在ComponentScan的包下。在处理异常的时候可以使用System.out的方式,也可以使用logger.info,或者可以入到数据库中。
(2) 全局数据绑定,结合方法型注解@InitBinder,用于request中自定义参数解析方式进行注册,从而达到自定义指定格式参数的目的;
对于@InitBinder,该注解的主要作用是绑定一些自定义的参数。一般情况下我们使用的参数通过@RequestParam,@RequestBody或者@ModelAttribute等注解就可以进行绑定了,但对于一些特殊类型参数,比如Date,它们的绑定Spring是没有提供直接的支持的,我们只能为其声明一个转换器,将request中字符串类型的参数通过转换器转换为Date类型的参数,从而供给@RequestMapping标注的方法使用。
@ControllerAdvice(basePackages = "mvc") public class SpringControllerAdvice { @InitBinder public void globalInitBinder(WebDataBinder binder) { binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd")); } }
@InitBinder标注的方法注册的Formatter在每次request请求进行参数转换时都会调用,用于判断指定的参数是否为其可以转换的参数。以下是UserController的代码:
@RequestMapping(value = "/detail", method = RequestMethod.GET) public ModelAndView detail(@RequestParam("id") long id, Date date) { System.out.println(date); ModelAndView view = new ModelAndView("user"); User user = userService.detail(id); view.addObject("user", user); return view; }
(3) 全局数据预处理,结合方法型注解@ModelAttribute,表示其标注的方法将会在目标Controller方法执行之前执行。
使用@ModelAttribute,如果声明在方法上,并且结合@ControllerAdvice,该方法将会在@ControllerAdvice所指定的范围内的所有接口方法执行之前执行,并且@ModelAttribute标注的方法的返回值还可以供给后续会调用的接口方法使用。
@ControllerAdvice(basePackages = "mvc") public class SpringControllerAdvice { @ModelAttribute(value = "message") public String globalModelAttribute() { System.out.println("global model attribute."); return "this is from model attribute"; } }
这里需要注意的是,该方法提供了一个String类型的返回值,而@ModelAttribute中指定了该属性名称为message,这样在Controller层就可以接收该参数了,如下是UserController(普通的@Controller):
@RequestMapping(value = "/detail", method = RequestMethod.GET) public ModelAndView detail(@RequestParam("id") long id, @ModelAttribute("message") String message) { System.out.println(message); ModelAndView view = new ModelAndView("user"); User user = userService.detail(id); view.addObject("user", user); return view; }
@ModelAttribute标注的方法的执行是在所有拦截器的preHandle()方法执行之后才会执行。
4. @ControllerAdvice和@RestControllerAdvice的区别
//@ControllerAdvice和@RestControllerAdvice都可以指向控制器的一个子集 // 指向所有带有注解@RestController的控制器 @ControllerAdvice(annotations = RestController.class) public class AnnotationAdvice {}
// 指向所有指定包中的控制器 @ControllerAdvice("org.example.controllers") public class BasePackageAdvice {}
// 指向所有带有指定签名的控制器 @ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class}) public class AssignableTypesAdvice {}
@ControllerAdvice和@RestControllerAdvice的区别 类似于 @RestController 与 @Controller的区别。
二、@RequestBody 和 @ResponseBody
1. @RequestBody
@RequestBody是作用在形参列表上,用于将前台发送过来固定格式的数据【xml 格式或者 json等】封装为对应的 JavaBean 对象,封装时使用到的一个对象是系统默认配置的 HttpMessageConverter进行解析,然后封装到形参上。
public Object login(@RequestBody User loginUuser, HttpSession session)
2.