SpringMVC4.x 笔记(4):控制器(Controller)功能实现

  1. Spring Mvc 中,在Java类上标注@Controller,即可使得该 Java 类成为一个能处理 HTTP 请求的控制器。

  2. 用户可以创建不限数量的控制器,分别处理不同的业务请求,如UserController、LogController等。每个控制器拥有多个处理请求的方法,每个方法负责不同的请求操作。

  3. Spring Mvc 框架使用 @RequestMapping 将请求映射到对应的控制器方法。在框架中,在初始化Bean过程中,会把每个控制器的方法封装成一个 RequestMappingInfo 对象,接收请求时,由请求映射处理器 handlerMappings 对请求作匹配映射到具体的 RequestMappingInfo 对象,转化成统一请求处理对象。(一般情况)

  4. 将请求映射到控制器处理方法的工作包含一系列映射规则,这些规则是根据请求中的各种信息制定的,具体包括请求URL、请求参数、请求方法、请求头这四个方面的信息项。

  5. 参数获取以及模型数据返回的所有功能实现,是基于框架的参数解析器(返回值解析器)实现的。简单理解,框架目前有多少参数解析器(返回值解析器)实现类型,这具有多少中实现参数获取、模型数据返回的方式。

使用 @RequestMapping 映射请求

通过请求URL进行映射

  1. @RequestMapping 使用value属性指定请求的URL路径,如@RequestMapping(value = "/user")
  • @RequestMapping 在类定义处指定的URL相对于应用的部署路径,而在方法定义处指定的URL则是相对于类定义处指定的URL。
  • @RequestMapping 在类定义处未标注,则方法上标注的URL则是对于应用部署路径的。
  1. 同一个控制器类的多个处理方法负责处理相同业务的不同操作,最佳实践设计应该是将这些操作请求安排在某一个相同的URL之下,如:都是用户user模块的不同业务方法,放在相同的/user/XXX下面。
@Controller
@RequestMapping(value = "/user")
public class UserController {

    @RequestMapping(value = "/getParam")
    public String getParam() {
        return "user";
    }

    @RequestMapping(value = "/getName")
    public String getName(String name) {
        return "user";
    }
}
  1. @RequestMapping 支持Ant风格(?、*、**)的 URL
/user/*/getUser:匹配 /user/aaa/getUser、/user/bbb/getUser 等 URL
/user/**/getUser:匹配 /user/getUser、/user/aaa/getUser、/user/aaa/bbb/getUser 等 URL
/user/getUser??:匹配 /user/getUserAA、/user/getUserBB 等 URL

使用 URI Template Patterns 风格

  1. @RequestMapping 带{XXX}占位符的 URL,并且通过 @PathVariable注解可以将占位符参数{XXX}绑定到控制器方法的入参@PathVariable("XXX")中。
  2. 类定义处的@RequestMapping的URL 如果使用占位符参数,也可以绑定到处理方法的入参中
    @RequestMapping("/getURITemplate/{name}")
    public String getURITemplate(@PathVariable("name") String name, Model model) {
        ... ...
    }
  1. 占位符还支持正则表达式:
@RequestMapping("/spring-web/{symbolicName:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{extension:\\.[a-z]+}")

通过请求参数、请求方法/请求头进行映射

  1. @RequestMapping 除了可以使用请求的URL映射请求外,还支持使用请求方法、请求头参数以及请求参数映射请求。如:
属性注释
value请求 URL
method请求方法
params请求参数
headers请求头
consumes接收的 Content-Type 类型
produces返回的 Content-Type 类型
    @RequestMapping(value = "/get", method = RequestMethod.GET, 
            headers = {"content-type=text/*"},
            params = {"name"},
            consumes = {"text/plain", "application/*"},
            produces = {"text/plain", "application/*"})
    public String get(@RequestParam("name") String name, ModelMap modelMap) {
        User user = userService.get(name);
        modelMap.addAttribute("user", user);
        return "user";
    }
  1. paramsheadersconsumesproduces 都支持简单的映射表达式,如:
params = {"name"}:请求参数必须包含 name 参数
params = {"!name"}:请求参数不能包含 name 参数
params = {"name=sam"}:请求参数必须包含 name 参数,并且其值为 sam
params = {"name!=sam"}:请求参数必须包含 name 参数,并且其值不能为 sam

@RequestMapping请求方法组合注解

  1. @GetMapping@PostMapping@RequestMapping和请求方法RequestMethod.XXX组合在一起的组合注解。
@RequestMapping(method = RequestMethod.GET)
public @interface GetMapping {}

@RequestMapping(method = RequestMethod.POST)
public @interface PostMapping {}
  1. 其他用法不变
    @GetMapping(value = "/getMapping",
            headers = {"content-type=text/*"},
            params = {"name"},
            consumes = {"text/plain", "application/*"},
            produces = {"text/plain", "application/*"})
    public String getMapping(String name, ModelMap modelMap) {
    }

请求处理方法参数绑定(参数获取)

使用@RequestParam绑定请求参数

  1. 在方法参数前面添加@RequestParam注解,其有三个属性:
  • value:参数名称
  • required:是否必须
  • defaultValue:默认参数值
    @RequestMapping(value = "/getNameAnnotation")
    public String getNameAnnotation(@RequestParam(value = "name", required = false, defaultValue = "eason") String name) {
       ... ...
    }

使用@CookieValue绑定请求中的Cookie值

  1. 使用@CookieValue 可以让处理方法入参绑定某个 Cookie 的值,它和 @RequestParam 注解有三个一样属性。
  2. 属性 value 指定的是Cookie的名称。
    @RequestMapping(value = "/getCookieValue")
    public String getCookieValue(@CookieValue(value = "JSESSIONID", required = false) String sessionId) {
        System.out.println(sessionId);
        ... ...
    }

使用 @RequestHeader 绑定请求报文头的属性值

  1. HTTP请求报文包含若干报文头属性,可以使用@RequestHeade将报文头属性绑定到处理方法的入参中
  2. 它和 @RequestParam 注解有三个一样属性。
    @RequestMapping(value = "/getRequestHeader")
    public String getRequestHeader(@RequestHeader("Accept-Encoding") String encoding) {
        System.out.println(encoding);
        ... ...
    }

使用参数/表单对象绑定请求参数值

  1. 直接使用参数/表单对象,Spring Mvc 会按照请求参数名和参数名、表单对象属性名匹配的方式,自动填充参数与属性。支持级联的属性名,如user.nameuser.address.street
    # 访问地址:/user/getName?name=sam
    @RequestMapping(value = "/getName")
    public String getName(String name) {
        System.out.println(name);
        ... ...
    }
    
    # 访问地址:/user/getObject?name=sam&birthday=2018-09-23
    @RequestMapping(value = "/getObject")
    public String getObject(User user, ModelMap modelMap) {
        System.out.println(user);
        ... ...
    }
  1. 在使用表单对象的时候,会涉及到参数绑定格式化以及数据转换的问题,需要InitBinder支持,如时间类型的转换:
    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        dateFormat.setLenient(false);
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
    }

    @InitBinder
    protected void initBinder2(WebDataBinder binder) {
        binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
    }

  1. 参数绑定,详细参考 SpringMVC4.x 笔记(5):方法的数据绑定

使用 Servlet API 对象作为入参

  1. 可以使用 Servlet API 的类作为处理方法的入参。如 HttpServletRequest、HttpSession
    @RequestMapping(value = "/getParam")
    public String getParam(HttpServletRequest request) {
        String name = request.getParameter("name");
        ... ...
    }

    @RequestMapping(value = "/getParam/session")
    public String getParam(HttpSession session) {
        session.setAttribute("sessionId", "123456");
        ... ...
    }
  1. 在使用 Servlet API 的类作为入参时,Spring Mvc 框架会自动将Web 层对应的 Servlet 对象传递给处理方法的入参,且可以同时使用Servlet API 和其他符合要求的入参。

  2. 【注意】在Spring Mvc 框架中,HttpServletRequest已经作为一个特殊性Bean存在于容器中,可以不使用方法级别的参数,而直接使用注入方式实现:

    @Autowired
    private HttpServletRequest httpServletRequest;

    @RequestMapping(value = "/getParam")
    public String getParam() {
        String name = httpServletRequest.getParameter("name");
        ... ...
    }

使用IO对象作为入参

Spring Mvc 支持控制器的处理方法使用InputStream、OutputStream、Reader、Writer等作为方法的入参,框架将获取ServletRequestInputStream/Reader或者ServletResponseOutputStream/Writer,然后传递给控制器的处理方法

    @RequestMapping(value = "/getIO")
    public void getIO(OutputStream os) throws IOException {
        // 读取类路径下的图片文件
        ClassPathResource resource = new ClassPathResource("/image.jpg");
        // 将图片写到输出流中
        FileCopyUtils.copy(resource.getInputStream(), os);
    }

使用矩阵变量绑定参数

  1. Spring Mvc 框架支持在URL中包含name-value的矩阵形式规范,其通过注解 @MatrixVariable 实现。
  2. 在 Matrix Variable中:
  • 多个变量可以分号;分隔。如 name=sam;mobile=123456
  • 一个变量多个值,可以使用逗号,分隔。如 name=sam,name=eason
    /**
     * 矩阵变量:/user/getMatrixVariable/sam;mobile=123
     */
    @RequestMapping("/getMatrixVariable/{name}")
    public String getMatrixVariable(@PathVariable String name,
                                    @MatrixVariable(required = false) String mobile,
                                    Model model) {
        System.out.println(mobile);
        User user = userService.get(name);
        model.addAttribute("user", user);
        return "user";
    }
  1. 该功能默认是关闭的(不同的版本略有不同),如需要使用则必须开启,且配合 URI Template Patterns 使用。开启的方式如下:
    # JavaConfig 配置实现
    requestMappingHandlerMapping.setRemoveSemicolonContent(false);
    
    # XMl 配置方式实现
    <mvc:annotation-driven enable-matrix-variables="true"/>

其他类型的参数

  1. 控制器方法的参数还支持其他类型的参数:如Local
  2. 多种不同的参数绑定实现形式,都是通过框架的参数解析器完成的,支持自定义参数解析器,从而实现私有的参数实现形式。

处理模型数据

  1. 对于MVC框架来说,模型数据是最重要的,因为控制(C)是为了产生模型数据(M),而视图(V)则是为了渲染模型数据

  2. Spring Mvc 框架使用@RequestMapping 将请求绑定到处理方法上,使用合适的参数绑定方式将请求消息绑定到入参中。

  3. 将模型数据展示给视图,Spring Mvc 提供了多种途径输出模型数据:

类型注释
ModelAndView请求处理方法返回值类型为ModelAndView时,直接添加模型数据放入该对象
@ModelAttribute在方法入参标注该注解后,入参的对象就会放到数据模型中。
ModelMapModel如果方法入参为ModelModelMapMap,则当方法返回时,该入参对象中数据自动添加到模型中
@SessionAttributes将模型中的某个属性暂存到HttpSession 中,以便多个请求之间可以共享这个属性

ModelAndView

  1. 请求处理方法返回值类型为ModelAndView时,该对象包含视图信息和模型数据,框架直接使用视图对模型数据进行渲染。
  2. 在方法处理中,直接使用ModelAndView对象的方法添加模型属性。
    @RequestMapping(value = "/returnModelAndView")
    public ModelAndView returnModelAndView(String name) {
        User user = userService.get(name);

        ModelAndView modelAndView = new ModelAndView();
        # 添加模型数据
        modelAndView.addObject("user", user);
        # 添加视图名称
        modelAndView.setViewName("user");
        return modelAndView;
    }

Model、ModelMap、Map

  1. 如果方法入参为ModelModelMapMap,则当方法返回时,框架会在该入参对象中数据自动添加到模型中。
  2. 在方法处理中,只需要添加模型数据到ModelModelMapMap对象中即可。
    @GetMapping(value = "/returnString")
    public String returnString(String name, ModelMap modelMap) {
        User user = userService.get(name);
        modelMap.addAttribute("user", user);
        return "user";
    }
    
    @RequestMapping(value = "/returnModel")
    public String returnModel(String name, Model model) {
        User user = userService.get(name);
        model.addAttribute("user", user);
        return "user";
    }
    
    @RequestMapping(value = "/returnMap")
    public String returnMap(String name, Map<String, Object> map) {
        User user = userService.get(name);
        map.put("user", user);
        return "user";
    }

@ModelAttribute

  1. 在方法入参标注@ModelAttribute注解后,框架会将该参数对象放到数据模型中。
    # 框架将请求信息绑定到 User 对象中
    @RequestMapping(value = "/returnModelAttribute")
    public String returnModelAttribute(@ModelAttribute User user) {
        user.setName("Eason");
        # 以user为Key将 User 对象直接会被框架放入模型数据中
        user = userService.get(user.getName());
        # 返回视图名
        return "user";
    }
  1. 除了在方法参数上使用@ModelAttribute注解,还可以在方法定义上使用该注解。Spring Mvc框架在调用目标方法前,会先逐个调用在方法级别上标注了@ModelAttribute注解的方法,并将这些方法的返回值添加到模型中。
    # 注解标注在方法上。
    # 调用这个 Controller 类中的任何一个请求处理方法前,框架都会先执行该方法,并将返回值以user为键添加到模型中
    @ModelAttribute("user")
    public User getUser() {
        return userService.get("eason");
    }

    # 没有任何入参数据,但是框架会把 @ModelAttribute("user") 模型数据给到这个方法
    @RequestMapping(value = "/returnModelAttributeMethod")
    public String returnModelAttribute(ModelMap modelMap) {
        System.out.println("参数: " + modelMap.get("user"));
        return "user";
    }

@SessionAttributes

  1. 在多个请求之间共用某个模型属性数据,可以在控制器类中标注@SessionAttributes注解,框架会将模型中对应的属性暂存到HttpSession
  2. @SessionAttributes 支持多个属性,如valuetype等。
  3. SessionStatus入参,可以控制会话的一些属性功能。
@Controller
@RequestMapping("user/session")
# 标注注解。 框架会将本处理器类中任何处理方法中属性名为user的模型属性透明的暂存到 HttpSession 中
@SessionAttributes("user") 
public class UserSessionController {

    @RequestMapping(value = "/get")
    public String get(User user, Model model) {
        user = userService.get(user.getName());

        #  一般默认是 request 请求域的
        #  @SessionAttributes 则会把属性名为user的这个数据放入 session 请求域
        model.addAttribute("user", user);

        return "user";
    }

     # 在多个请求之间共用某个模型属性数据,隐含模型自动从HttpSession 中获取到这个模型属性
    @RequestMapping(value = "/return")
    public String get(ModelMap modelMap, SessionStatus sessionStatus) {

        # 读取模型中的数据
        User user = (User) modelMap.get("user");
        if (Objects.nonNull(user)) {
            user.setName("Rabbi");

            # 清除本处理器对应的会话属性
            sessionStatus.setComplete();
        }
        return "user";
    }
}

转发与重定向模型数据传输

  1. 一般情况下,请求控制方法返回字符串类型的值会被解析成逻辑视图名称,可以使用forwardredirect前缀进行转发与重定向的特殊处理。
  2. forward 请求是与当前请求同属于一个请求;redirect请求会让浏览器重新发起一个新的请求
  3. forward 请求实现:forward: + / + ClassUrl + methodUrl,属于同一次请求,数据模型会传递
@Controller
@RequestMapping("user/forwardAndRedirect")
public class UserForwardAndRedirectController {

    # forward: 同一次请求,数据模型会传递
    @RequestMapping(value = "/get")
    public String get(User user) {
        System.out.println("参数: " + user);
        return "user";
    }

    @RequestMapping(value = "/forward")
    public String forward(User user, ModelMap modelMap) {
        # 隐藏的数据模型,会把 user 对象放入数据模型,这时直接就可以从 ModelMap 中获取
        System.out.println("参数: " + modelMap.get("user"));
        return "forward:/user/forwardAndRedirect/get";
    }
}
  1. redirect 请求实现:
  • 同一个控制器类中重定向: redirect + MethodUrl,不要/开头
  • 不同的控制器类重定向:redirect + / + ClassUrl + MethodUrl,必须/开头
  • 重定向请求,是两个请求,需要使用 RedirectAttributes#addFlashAttribute 处理模型数据的传递
@Controller
@RequestMapping("user/forwardAndRedirect")
public class UserForwardAndRedirectController {

    @RequestMapping(value = "/getName")
    public String getName(User user) {
        System.out.println("参数: " + user);
        return "user";
    }

    @RequestMapping(value = "/redirect")
    public String redirect(User user, RedirectAttributes model) {
        System.out.println("参数: " + user);
        // 重定向,两个请求,需要使用 RedirectAttributes#addFlashAttribute 方法
        model.addFlashAttribute("user", user);
        return "redirect:getName";
    }

    @RequestMapping(value = "/redirect2")
    public String redirect2(User user, RedirectAttributes model) {
        System.out.println("参数: " + user);
        model.addFlashAttribute("user", user);
        return "redirect:/user/forwardAndRedirect/getName";
    }

}

使用 HttpMessageConverter 获取参数与返回数据模型

  1. HttpMessageConverter 是Spring 的一个重要接口,负责将请求信息转换为一个对象,将对象输出为响应信息

  2. HttpMessageConverter 接口有大量的实现类:如

  • StringHttpMessageConverter:支持将请求信息转换为字符串
  • MappingJackson2HttpMessageConverter:利用Jackson读写JSON数据

使用 HttpMessageConverter

  1. 使用HttpMessageConverter 将请求信息装换并绑定到处理方法的参数,Spring Mvc 提供了两种方式:
方式注释
注解 @ResponseBody@RequestBody1. 对处理方法进行标注。
2. @RestController 注解组合了 @Controller@ResponseBody 两个注解
RequestEntityResponseEntity作为处理方法的入参或者返回值
  1. 使用注解 @ResponseBody@RequestBody 的例子
    # 将请求报文体转换为字符串绑定到  @RequestBody 入参中
    # 匹配 StringHttpMessageConverter 
    @RequestMapping(value = "/RequestBody")
    public String RequestBody(@RequestBody String name) {
        System.out.println(name);
        return "user";
    }
    
    # 将数据传输到响应流中
    # 匹配 ByteArrayHttpMessageConverter
    @ResponseBody
    @RequestMapping(value = "/responseBody/{name}")
    public byte[] responseBodyByte(@PathVariable("name") String name) {
        System.out.println(name);
        return name.getBytes();
    }
  1. 使用类 RequestEntityResponseEntity 的例子
    @RequestMapping(value = "/requestEntity")
    public String RequestBody(RequestEntity<String> entity) {
        System.out.println(entity.getBody());
        return "user";
    }

    @RequestMapping(value = "/responseEntity")
    public ResponseEntity<byte[]> responseEntity() {
        return new ResponseEntity("Sam".getBytes(), HttpStatus.OK);
    }

处理 XML/JSON 类型数据

  1. Spring Mvc 提供了可以处理XML和JSON格式的请求/响应消息的 HttpMessageConverter
类型HttpMessageConverter类型
XMLMarshallingHttpMessageConverterJaxb2RootElementHttpMessageConverter
JSONGsonHttpMessageConverterMappingJackson2HttpMessageConverterFastJsonHttpMessageConverter4
  1. 【注意】@ResponseBody@RequestBody注解默认只添加了一部分HttpMessageConverter不足以支持XML/JSON的处理,需要@EnableWebMvc注解或者<mvc:annotation-driven/>标签的支持

参考

  1. 官方文档
  2. 笔记源码
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值