【Spring MVC学习笔记 三】深入实践Spring MVC控制器(下)

简介: 【Spring MVC学习笔记 三】深入实践Spring MVC控制器(下)

2 RestFul风格传参

什么是REST? REST(英文:Representational State Transfer,简称REST,意思:表述性状态转换,描述了一个架构样式的网络系统,比如web应用)。它是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件,它主要用于客户端和服务端交互类的软件。基于这个风格设计的软件可以更简介,更有层次,更易于实现缓存等机制。

什么是RESTFUL

什么是RESTFUL?满足REST约束条件和原则的应用程序或设计就是RESTFUL

  • 资源:互联网所有的事物都可以被抽象为资源
  • 资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作。分别对应 添加、 删除、修改、查询。

更简化的理解,可以认为url路径不是写死的而是可以作为参数传递的

@PathVariable注解

例如我们的请求路径上包含了具体的资源id,那么就会请求到该资源,当然也可以附加一些操作,例如增删改查

@RequestMapping("/restfulParam/{id}")
    public void restfulParam(@PathVariable("id") Long id) {
        System.out.println("restfulParam参数为" + id);
    }

请求的url如下:

http://localhost:8081/base_annotation/user/restfulParam/10

返回结果如下:

3 请求附加信息获取

如果我们想获取请求的一些附加信息,例如 获取请求头中的User-Agent和Cookie信息,可以这么去获取:

@RequestMapping("/headerInfo")
    public void headerInfo(@RequestHeader("User-Agent") String userAgent, @CookieValue("JSESSIONID") String cookieName) {
        System.out.println("User-Agent:" + userAgent);
        System.out.println("cookieName" + cookieName);
    }

请求的url如下:

http://localhost:8081/base_annotation/user/headerInfo

返回结果如下:

4 method属性指定请求类型

我们一般默认的请求方式是Get,但其实也可以有别的请求方式,例如我们用POST去请求接口

@RequestMapping(value="/javaBeanParamPost",method = {RequestMethod.POST})
    public void javaBeanParamPost(User user) {
        System.out.println(user);
    }

请求的url如下:

http://localhost:8081/base_annotation/user/javaBeanParamPost?id=1&username=tml&age=30&accountIds=50&accountIds=80

返回结果如下:

当然会报错,因为我们方法需要的是POST请求,浏览器请求的是Get,不支持。除了用以上的方式还可以使用如下的注解直接表明方法的属性:

@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping

5 请求数据乱码问题处理

之前我们遇到乱码的问题都是需要进行手动设置的:

request.setCharacterEncoding("UTF-8");

现在有了框架,整体进行拦截,GET请求框架已经帮我们做好了,针对POST请求我们只需要在web.xml中加入如下配置即可解决乱码问题:

<filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
        <!-- 如果有编码格式,设置强制使用上面我们设置的编码格式-->
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

Controller响应处理

聊完了请求我们再来聊聊响应,我们知道响应返回的一部分是页面,一部分是数据,所以我们分别聊聊数据的返回格式以及页面的跳转方式。

1 返回数据模型格式

我们先重新设置下userInfo.jsp文件来进行实验,其中Model 只有寥寥几个方法只适合用于储存数据,简化了新手对于Model对象的操作和理解;ModelMap 继承了 LinkedMap ,除了实现了自身的一些方法,同样的继承 LinkedMap 的方法和特性;ModelAndView 可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转。

<%--
  Created by IntelliJ IDEA.
  User: 13304
  Date: 2021/8/31
  Time: 11:47
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
modelMap: ${modelMap} <br>
model: ${model}<br>
modelAndView: ${modelAndView}<br>
</body>
</html>

1-1 返回ModelAndView

第一种就是我们使用的经典方式ModelAndView:

@RequestMapping("/getUserInfoByModelAndView")
    public ModelAndView getUserInfoByModelAndView(@RequestParam("name") String username,@RequestParam("age") String age) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("modelAndView", "username: "+username + " age: "+age+" "+LocalDate.now());
        modelAndView.setViewName("userInfo");
        return modelAndView;
    }

请求的url为:

http://localhost:8081/base_annotation/user/getUserInfoByModelAndView?name=tml&age=30

返回的结果为:

1-2 返回String类型-通过Model

第二种我们使用Model接口,视图不需要设置:

@RequestMapping("/getUserInfoByStringModel")
    public String getUserInfo(@RequestParam("name") String username,@RequestParam("age") String age, ModelMap modelMap) {
        modelMap.addAttribute("modelMap", "username"+username + "age"+age+LocalDate.now());
        return "userInfo";
    }

请求的url为:

http://localhost:8081/base_annotation/user/getUserInfoByStringModel?name=tml&age=30

返回的结果为:

1-3 返回String类型-通过ModelMap

最后一种我们使用ModelMap:

http://localhost:8081/base_annotation/user/getUserInfoByStringModelMap?name=tml&age=30

请求的url为:

@RequestMapping("/getUserInfoByStringModelMap")
    public String getUserInfoByStringModelMap(@RequestParam("name") String username,@RequestParam("age") String age, Model model) {
        model.addAttribute("model", "username: "+username + " age: "+age+" "+LocalDate.now());
        return "userInfo";
    }

返回的结果为:

除了提到的这几个Model,参数为Map以及List也可以传递,底层就是使用request.setAttribute()在赋值

2 返回页面跳转方式

学习Servlet的时候我们就知道页面的跳转有两种:请求转发和重定向。二者的区别就不再赘述了,在我的另一篇博客中有详细介绍:【Java Web编程 八】深入理解Servlet常用对象,之前是怎么实现的呢?

@Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //请求转发的写法
        RequestDispatcher requestDispatcher=request.getRequestDispatcher("/first.jsp");
        requestDispatcher.forward(request,response);
        //重定向的写法
        response.sendRedirect("/first.jsp");
    }

那么有了Spring MVC之后又是怎么实现的呢?

2-1 返回到指定页面

首先来看一个标准的返回到指定页面:

@RequestMapping("/getUserInfoByStringModel")
    public String getUserInfo(@RequestParam("name") String username, @RequestParam("age") String age, ModelMap modelMap) {
        modelMap.addAttribute("modelMap", "username: " + username + " age: " + age + " " + LocalDate.now());
        return "userInfo";
    }

2-2 请求转发到新页面

然后我们看下请求转发是如何实现的:

@RequestMapping("requestDispatch")
    public String requestDispatch(@RequestParam("name") String username,@RequestParam("age") String age, ModelMap modelMap) {
        modelMap.addAttribute("modelMap", "username: "+username + " age: "+age+" "+LocalDate.now());
        return "forward:/index.jsp";
    }

请求的url为:

http://localhost:8081/base_annotation/user/requestDispatch?name=tml&age=30

返回结果为:

可以看到共享对象被保留在了request的作用域内。

2-3 URL重定向到新页面

我们再来看下重定向如何实现:

@RequestMapping("requestRedirect")
    public String requestRedirect(@RequestParam("name") String username,@RequestParam("age") String age, ModelMap modelMap) {
        modelMap.addAttribute("modelMap", "username: "+username + " age: "+age+" "+LocalDate.now());
        return "redirect:/index.jsp";
    }

请求的url为:

http://localhost:8081/base_annotation/user/requestRedirect?name=tml&age=30

返回结果为:

可以看到共享对象由于发了两次请求丢失了。

项目结构及代码清单

整体实践完成后的项目结构如下图所示:

核心的实践代码如下UserController

package com.example.base_annotation.controller;
import com.example.base_annotation.controller.dto.Account;
import com.example.base_annotation.controller.dto.User;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.Date;
@Controller
@RequestMapping("/user")
public class UserController {
    @RequestMapping("requestDispatch")
    public String requestDispatch(@RequestParam("name") String username, @RequestParam("age") String age, ModelMap modelMap) {
        modelMap.addAttribute("modelMap", "username: " + username + " age: " + age + " " + LocalDate.now());
        return "forward:/index.jsp";
    }
    @RequestMapping("requestRedirect")
    public String requestRedirect(@RequestParam("name") String username, @RequestParam("age") String age, ModelMap modelMap) {
        modelMap.addAttribute("modelMap", "username: " + username + " age: " + age + " " + LocalDate.now());
        return "redirect:/index.jsp";
    }
    @RequestMapping("/getUserInfoByStringModel")
    public String getUserInfo(@RequestParam("name") String username, @RequestParam("age") String age, ModelMap modelMap) {
        modelMap.addAttribute("modelMap", "username: " + username + " age: " + age + " " + LocalDate.now());
        return "userInfo";
    }
    @RequestMapping("/getUserInfoByStringModelMap")
    public String getUserInfoByStringModelMap(@RequestParam("name") String username, @RequestParam("age") String age, Model model) {
        model.addAttribute("model", "username: " + username + " age: " + age + " " + LocalDate.now());
        return "userInfo";
    }
    @RequestMapping("/getUserInfoByModelAndView")
    public ModelAndView getUserInfoByModelAndView(@RequestParam("name") String username, @RequestParam("age") String age) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("modelAndView", "username: " + username + " age: " + age + " " + LocalDate.now());
        modelAndView.setViewName("userInfo");
        return modelAndView;
    }
    @RequestMapping("/servletParam")
    public void servletParam(HttpServletRequest request, HttpServletResponse responser, HttpSession session) {
        System.out.println(request.getParameter("username"));
        System.out.println(request);
        System.out.println(responser);
        System.out.println(session);
    }
    @RequestMapping("/simpleParamMatch")
    public void simpleParamMatch(String username, int age) {
        System.out.println("username:" + username);
        System.out.println("age:" + age);
    }
    @RequestMapping("/simpleParamDiff")
    public void simpleParamDiff(@RequestParam("name") String username, @RequestParam(value = "age") Integer age) {
        System.out.println("username:" + username);
        System.out.println("age:" + age);
    }
    @RequestMapping("/restfulParam/{id}")
    public void restfulParam(@PathVariable("id") Long id) {
        System.out.println("restfulParam参数为" + id);
    }
    @RequestMapping("/arrayParam")
    public void arrayParam(Long[] accountIds) {
        System.out.println(Arrays.asList(accountIds));
    }
    @RequestMapping("/listParam")
    public void listParam(User user) {
        System.out.println(user.getAccountIds());
    }
    @RequestMapping("/dateParam")
    public void dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date) {
        System.out.println(date);
    }
    @RequestMapping("/javaBeanParam")
    public void javaBeanParam(User user) {
        System.out.println(user);
    }
    @RequestMapping("/javaBeanModelAttributeParam")
    public String javaBeanModelAttributeParam(@ModelAttribute("userAllies") User user) {
        System.out.println(user);
        return "userInfo";
    }
    @RequestMapping("/headerInfo")
    public void headerInfo(@RequestHeader("User-Agent") String userAgent, @CookieValue("JSESSIONID") String cookieName) {
        System.out.println("User-Agent:" + userAgent);
        System.out.println("cookieName" + cookieName);
    }
    @InitBinder("user")
    public void initBinderUserType(WebDataBinder binder) {
        binder.setFieldDefaultPrefix("user.");
    }
    @InitBinder("account")
    public void initBinderCatType(WebDataBinder binder) {
        binder.setFieldDefaultPrefix("account.");
    }
    @RequestMapping("/multiModelParam")
    public void multiModelParam(User user, Account account) {
        System.out.println(user);
        System.out.println(account);
    }
    @RequestMapping(value = "/javaBeanParamPost", method = {RequestMethod.POST})
    public void javaBeanParamPost(User user) {
        System.out.println(user);
    }
}

总结一下

整体深入的实践了一遍Spring MVC的控制器,其实就是比照Servlet之前的实现方式进行了一下类比实现,这样可以看出框架的方便之处,为什么说Spring MVC和Spring兼容好呢?还记得学习Spring注解开发时候的Controller么,不就是一种Compnent么。再去从实践的角度看框架,整体上看一个Controller就像我们的某个小模块,这个小模块的各种方法是模块的实现,这和现实也比较契合,每个方法就像之前的每个Servlet里的doGet或doPost,现在我们不需要再为了一个模块写一堆Servlet了,用一个映射的方法代替就行了。再看入参,入参可以统一接受过滤器处理整体解决乱码问题,并且对各种入参类型都有很好的兼容,各种注解代替了我们的各种数据转换实现,给我们省心不少,返回结果页面的显示和跳转也很方便了,使用预置的Model不需要再将对象放到作用域了,因为框架已经替我们做好了,而请求转发和重定向也仅仅只是在返回的字符串上加个单词即可,这就是框架的魅力吧。

相关文章
|
8天前
|
XML Java 数据格式
🌱 深入Spring的心脏:Bean配置的艺术与实践 🌟
本文深入探讨了Spring框架中Bean配置的奥秘,从基本概念到XML配置文件的使用,再到静态工厂方式实例化Bean的详细步骤,通过实际代码示例帮助读者更好地理解和应用Spring的Bean配置。希望对你的Spring开发之旅有所助益。
53 3
|
9天前
|
设计模式 前端开发 Java
步步深入SpringMvc DispatcherServlet源码掌握springmvc全流程原理
通过对 `DispatcherServlet`源码的深入剖析,我们了解了SpringMVC请求处理的全流程。`DispatcherServlet`作为前端控制器,负责请求的接收和分发,处理器映射和适配负责将请求分派到具体的处理器方法,视图解析器负责生成和渲染视图。理解这些核心组件及其交互原理,有助于开发者更好地使用和扩展SpringMVC框架。
24 4
|
24天前
|
XML Java 数据格式
Spring Core核心类库的功能与应用实践分析
【12月更文挑战第1天】大家好,今天我们来聊聊Spring Core这个强大的核心类库。Spring Core作为Spring框架的基础,提供了控制反转(IOC)和依赖注入(DI)等核心功能,以及企业级功能,如JNDI和定时任务等。通过本文,我们将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring Core,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
51 14
|
20天前
|
缓存 Java 数据库连接
Spring框架中的事件机制:深入理解与实践
Spring框架是一个广泛使用的Java企业级应用框架,提供了依赖注入、面向切面编程(AOP)、事务管理、Web应用程序开发等一系列功能。在Spring框架中,事件机制是一种重要的通信方式,它允许不同组件之间进行松耦合的通信,提高了应用程序的可维护性和可扩展性。本文将深入探讨Spring框架中的事件机制,包括不同类型的事件、底层原理、应用实践以及优缺点。
49 8
|
25天前
|
负载均衡 Java 开发者
深入探索Spring Cloud与Spring Boot:构建微服务架构的实践经验
深入探索Spring Cloud与Spring Boot:构建微服务架构的实践经验
83 5
|
22天前
|
XML 前端开发 安全
Spring MVC:深入理解与应用实践
Spring MVC是Spring框架提供的一个用于构建Web应用程序的Model-View-Controller(MVC)实现。它通过分离业务逻辑、数据、显示来组织代码,使得Web应用程序的开发变得更加简洁和高效。本文将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring MVC,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
49 2
|
26天前
|
安全 Java 数据安全/隐私保护
如何使用Spring Boot进行表单登录身份验证:从基础到实践
如何使用Spring Boot进行表单登录身份验证:从基础到实践
43 5
|
26天前
|
监控 Java 数据安全/隐私保护
如何用Spring Boot实现拦截器:从入门到实践
如何用Spring Boot实现拦截器:从入门到实践
47 5
|
27天前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
103 2
|
27天前
|
前端开发 Java 开发者
Spring MVC中的控制器:@Controller注解全解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序控制层的核心。它不仅简化了控制器的定义,还提供了灵活的请求映射和处理机制。本文将深入探讨`@Controller`注解的用法、特点以及在实际开发中的应用。
69 0