SpringMVC核心功能详解:从RESTful到JSON数据处理

一、RESTful风格支持

1、 RESTful概述

REST(Representational State Transfer,表现层状态转移)是一种软件架构风格,其核心思想是通过HTTP动词来描述操作,URL定位资源。

传统URL模式:

RESTful风格:

RESTful风格通过统一的URL和不同的HTTP请求方法实现对资源的CRUD操作,URL中使用占位符传递参数。

2、 @PathVariable注解

SpringMVC通过@PathVariable注解实现RESTful风格URL的参数绑定:

java 复制代码
@Controller
@RequestMapping("/account")
public class AccountController {
    
    @RequestMapping(value="/findAccount7/{id}")
    public String findAccount11(@PathVariable Integer id, Model model){
        model.addAttribute("msg", "接收到的ID是:" + id);
        return "success";
    }
}

前端访问:

java 复制代码
<a href="/account/findAccount7/123">RESTful传参</a>

**运行结果:**​ 页面显示"接收到的ID是:123"

注意: ​ URL中的占位符名称{id}必须与@PathVariable参数名一致,或者通过@PathVariable("id")指定。

二、请求参数乱码问题处理

1、 POST请求乱码解决方案

POST请求提交的表单数据需要统一编码处理,SpringMVC提供了CharacterEncodingFilter过滤器:

web.xml配置:

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>
</filter>
<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

**过滤器作用原理:**​ CharacterEncodingFilter会在请求到达DispatcherServlet之前,对请求和响应的字符编码进行统一设置,确保中文参数能正确解析。

2、 GET请求乱码解决方案

GET请求的参数是附在URL后的,Tomcat默认使用ISO-8859-1编码,需要单独配置:

pom.xml中配置Tomcat插件:

XML 复制代码
<plugin>
    <groupId>org.apache.tomcat.maven</groupId>
    <artifactId>tomcat7-maven-plugin</artifactId>
    <version>2.2</version>
    <configuration>
        <port>8080</port>
        <path>/</path>
        <!--关键配置:按UTF-8进行URL编码-->
        <uriEncoding>UTF-8</uriEncoding>
    </configuration>
</plugin>

注意: ​ 实际部署时,Tomcat服务器的server.xml配置文件中也需要设置URIEncoding="UTF-8"

三、自定义类型转换器

1、使用场景

当SpringMVC内置的类型转换器无法满足需求时,需要自定义转换器。常见场景包括:

  1. 字符串到日期类型的转换

  2. 特殊格式字符串到复杂对象的转换

  3. 前端特殊格式数据到后台Java类型的转换

示例场景: ​ 表单提交的日期字符串"2023-10-12"需要转换为java.util.Date对象:

表单代码:

html 复制代码
<form action="account/saveAccount" method="post">
    账户名称:<input type="text" name="name"><br/>
    开户日期:<input type="text" name="date"><br/>
    <input type="submit" value="保存">
</form>

POJO类:

java 复制代码
public class Account implements Serializable {
    private Integer id;
    private String name;
    private Date date;  // 需要从字符串转换
    // getter/setter...
}

如果不配置转换器,会报错:

<img src="assets/image-20211012203811849.png" alt="类型转换错误" style="zoom:67%;" />

2、 实现自定义转换器

步骤1:创建转换器类实现Converter接口

java 复制代码
import org.springframework.core.convert.converter.Converter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateConverter implements Converter<String, Date> {
    @Override
    public Date convert(String source) {
        try {
            if(source == null || source.trim().isEmpty()) {
                return null;
            }
            DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
            return format.parse(source);
        } catch (Exception e) {
            throw new RuntimeException("日期格式转换失败,格式应为:yyyy-MM-dd");
        }
    }
}

步骤2:SpringMVC配置文件中注册转换器

XML 复制代码
<!-- 开启注解驱动,并指定转换服务 -->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>

<!-- 配置ConversionServiceFactoryBean,注册自定义转换器 -->
<bean id="conversionService" 
      class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="com.hg.converter.DateConverter"></bean>
            <!-- 可以注册多个转换器 -->
            <!-- <bean class="com.hg.converter.OtherConverter"></bean> -->
        </set>
    </property>
</bean>

**转换器原理:**​ SpringMVC在参数绑定时,会先检查是否有匹配的Converter,如果有则使用自定义转换器,否则使用默认转换器。

四、使用ServletAPI接收参数

虽然SpringMVC提供了便捷的参数绑定机制,但有时仍需要直接操作ServletAPI:

java 复制代码
@Controller
@RequestMapping("/account")
public class AccountController {

    @RequestMapping("/findAccount8")
    public String findAccount8(HttpServletRequest request, 
                               HttpServletResponse response,
                               HttpSession session) {
        // 1. 获取请求参数
        String username = request.getParameter("name");
        String age = request.getParameter("age");
        
        // 2. 设置请求属性
        request.setAttribute("msg", username + " " + age);
        
        // 3. 操作Session
        session.setAttribute("user", username);
        
        // 4. 操作响应
        // response.setContentType("text/html;charset=utf-8");
        
        return "success";
    }
}

前端访问:

html 复制代码
<a href="/account/findAccount8?name=张三&age=25">ServletAPI接收参数</a>

支持的ServletAPI类型:

  • HttpServletRequest- 获取请求信息

  • HttpServletResponse- 操作响应

  • HttpSession- 会话管理

  • ServletContext- 应用上下文

  • InputStream/OutputStream- 流操作

  • Reader/Writer- 字符流操作

五、数据传递方式

1、 ModelAndView传递数据

ModelAndView对象可以同时封装模型数据和视图名称:

java 复制代码
@RequestMapping("/findAccount9")
public ModelAndView findAccount9() {
    // 创建ModelAndView对象
    ModelAndView mv = new ModelAndView();
    
    // 添加模型数据(相当于request.setAttribute)
    mv.addObject("message", "欢迎使用SpringMVC");
    mv.addObject("userCount", 100);
    
    // 设置视图名称
    mv.setViewName("success");
    
    return mv;
}

简化写法:

java 复制代码
@RequestMapping("/findAccount9")
public ModelAndView findAccount9(ModelAndView mv) {
    mv.addObject("msg", "欢迎你 springmvc");
    mv.setViewName("success");
    return mv;
}

2、 Model传递数据

Model接口更轻量,只负责数据传递:

java 复制代码
@RequestMapping("/findAccount10")
public String findAccount10(Model model) {
    // 添加单个属性
    model.addAttribute("msg", "欢迎你 springmvc");
    
    // 添加多个属性
    Map<String, Object> data = new HashMap<>();
    data.put("username", "张三");
    data.put("age", 25);
    model.addAllAttributes(data);
    
    return "success";
}

Model的常见实现:

  • ExtendedModelMap

  • BindingAwareModelMap

  • ModelMap

3、 使用ServletAPI传递数据

直接操作HttpServletRequest传递数据:

java 复制代码
@RequestMapping("/findAccount11")
public String findAccount11(HttpServletRequest request, 
                            HttpServletResponse response) {
    // 设置请求属性
    request.setAttribute("msg", "欢迎你 springmvc");
    request.setAttribute("time", new Date());
    
    // 也可以从请求中获取参数
    String username = request.getParameter("username");
    
    return "success";
}

六、JSON数据处理

1、 添加JSON依赖

SpringMVC默认使用Jackson处理JSON,需要添加依赖:

XML 复制代码
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.0</version>
</dependency>
<!-- 可选:JSON视图解析器 -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.9.0</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.9.0</version>
</dependency>

2、 核心注解

@RequestBody

**作用:**​ 将HTTP请求体中的JSON字符串转换为Java对象

**位置:**​ 方法参数前

**支持类型:**​ 基本类型、POJO、Map、List等

@ResponseBody

**作用:**​ 将方法返回值转换为JSON格式响应

**位置:**​ 方法上或类上

**支持类型:**​ 任意Java对象

3、 JSON处理示例

示例1:接收和返回JSON
java 复制代码
@Controller
@RequestMapping("/account")
public class AccountController {
    
    // 处理JSON请求,返回JSON响应
    @RequestMapping(value = "/saveAccount2", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, Object> saveAccount2(@RequestBody Account account) {
        Map<String, Object> result = new HashMap<>();
        result.put("status", 200);
        result.put("message", "保存成功");
        result.put("data", account);
        return result;
    }
    
    // 返回List的JSON格式
    @RequestMapping("/listAccounts")
    @ResponseBody
    public List<Account> listAccounts() {
        List<Account> accounts = new ArrayList<>();
        accounts.add(new Account(1, "张三", 1000.0f));
        accounts.add(new Account(2, "李四", 2000.0f));
        return accounts;
    }
}
示例2:RESTful风格API
java 复制代码
@RestController  // 相当于@Controller + 所有方法都添加@ResponseBody
@RequestMapping("/api/users")
public class UserRestController {
    
    // GET /api/users/1
    @GetMapping("/{id}")
    public User getUser(@PathVariable Integer id) {
        User user = userService.findById(id);
        return user;
    }
    
    // POST /api/users
    @PostMapping
    public Map<String, Object> createUser(@RequestBody User user) {
        userService.save(user);
        Map<String, Object> result = new HashMap<>();
        result.put("success", true);
        result.put("message", "用户创建成功");
        return result;
    }
    
    // PUT /api/users/1
    @PutMapping("/{id}")
    public Map<String, Object> updateUser(@PathVariable Integer id, 
                                          @RequestBody User user) {
        user.setId(id);
        userService.update(user);
        Map<String, Object> result = new HashMap<>();
        result.put("success", true);
        result.put("message", "用户更新成功");
        return result;
    }
    
    // DELETE /api/users/1
    @DeleteMapping("/{id}")
    public Map<String, Object> deleteUser(@PathVariable Integer id) {
        userService.delete(id);
        Map<String, Object> result = new HashMap<>();
        result.put("success", true);
        result.put("message", "用户删除成功");
        return result;
    }
}

4、 前端AJAX调用示例

HTML页面:

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <title>JSON测试</title>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
    <h2>JSON数据交互测试</h2>
    
    <!-- 测试按钮 -->
    <input type="button" value="测试保存账户" onclick="saveAccount()"/>
    <input type="button" value="测试获取用户列表" onclick="getUserList()"/>
    
    <!-- 结果显示区域 -->
    <div id="result"></div>
    
    <script>
        // 保存账户
        function saveAccount() {
            const accountData = {
                id: 1,
                name: "张三",
                money: 999.0,
                date: "2023-10-12"
            };
            
            axios.post('/account/saveAccount2', accountData)
                .then(response => {
                    if(response.data.status === 200) {
                        const account = response.data.msg;
                        document.getElementById("result").innerHTML = 
                            `保存成功:${account.name} - ${account.money}`;
                    }
                })
                .catch(error => {
                    console.error('请求失败:', error);
                    document.getElementById("result").innerHTML = "请求失败";
                });
        }
        
        // 获取用户列表
        function getUserList() {
            axios.get('/api/users')
                .then(response => {
                    const users = response.data;
                    let html = "<h3>用户列表:</h3><ul>";
                    users.forEach(user => {
                        html += `<li>${user.name} - ${user.money}</li>`;
                    });
                    html += "</ul>";
                    document.getElementById("result").innerHTML = html;
                })
                .catch(error => {
                    console.error('请求失败:', error);
                });
        }
    </script>
</body>
</html>

5、 JSON相关配置

SpringMVC配置文件中添加JSON支持:

XML 复制代码
<!-- 开启注解驱动,默认已包含JSON支持 -->
<mvc:annotation-driven>
    <!-- 配置消息转换器 -->
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <!-- 设置字符编码 -->
            <property name="defaultCharset" value="UTF-8"/>
            <!-- 支持的媒体类型 -->
            <property name="supportedMediaTypes">
                <list>
                    <value>application/json;charset=UTF-8</value>
                    <value>text/html;charset=UTF-8</value>
                </list>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

6、 常见问题处理

1. 日期格式处理
java 复制代码
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
2. 忽略某些字段
java 复制代码
@JsonIgnore  // 忽略该字段
private String password;

@JsonIgnoreProperties({"password", "salt"})  // 类级别忽略
public class User {
    // ...
}
3. 字段别名
java 复制代码
@JsonProperty("user_name")
private String username;

总结

从RESTful风格到JSON数据处理的完整流程,SpringMVC提供了全面的Web开发支持:

  1. RESTful设计:通过URL设计和HTTP方法实现资源操作

  2. 参数绑定:支持基本类型、POJO、集合、RESTful参数等多种绑定方式

  3. 编码处理:统一解决GET/POST请求的乱码问题

  4. 类型转换:内置转换器+自定义转换器满足各种场景需求

  5. 数据传递:多种方式在Controller和View之间传递数据

  6. JSON处理:完整的JSON请求/响应支持,适用于前后端分离开发

这些功能共同构成了SpringMVC强大的Web开发能力,使得开发者能够高效、优雅地构建企业级Web应用。

相关推荐
代龙涛2 小时前
WordPress 主题开发指南:模板文件、函数与页面选型规则
开发语言·后端·php·wordpress
三水不滴2 小时前
Elasticsearch 实战系列(二):SpringBoot 集成 Elasticsearch,从 0 到 1 实现商品搜索系统
经验分享·spring boot·笔记·后端·elasticsearch·搜索引擎
Amour恋空2 小时前
Nacos服务发现与配置
java·后端·服务发现
uzong2 小时前
为什么是你来做?面试中犀利问题的底层逻辑是什么和标准回答模版
后端·面试
Sailing2 小时前
🚀AI 写代码越来越快,但我开始不敢上线了
前端·后端·面试
程序员鱼皮3 小时前
万字干货 | OpenClaw 进阶玩法大全:技能 / 多 Agent / 省钱 / 安全,50+ 实战技巧一次学会
前端·后端·ai编程
人道领域3 小时前
Day | 07 【苍穹外卖 :用户端添加购物车】
java·开发语言·数据库·后端·苍穹外卖
树獭叔叔3 小时前
PyTorch学习阶段一:前向传播 - Tensor 的内存模型与高性能算子
后端·aigc·openai
CoderLiu3 小时前
Agent 沙箱架构深度解析:从 Pattern 选型到生产级框架设计
前端·人工智能·后端