键下留情积点口德 發表於 2026-4-1 09:00:00

MVC快速入门

<h2 id="前言">前言</h2>
<h3 id="什么是mvc">什么是MVC</h3>
<p>MVC英文是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计规范,本质上也是一种解耦。</p>
<p><img src="https://seven97-blog.oss-cn-hangzhou.aliyuncs.com/imgs/202407212156709.png" alt="" loading="lazy"></p>
<ul>
<li><strong>Model</strong>(模型)是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。</li>
<li><strong>View</strong>(视图)是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的。</li>
<li><strong>Controller</strong>(控制器)是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。</li>
</ul>
<h3 id="什么是springmvc">什么是SpringMVC</h3>
<p>而Spring Web MVC 则是一种基于Java 的实现了Web MVC 设计模式的请求驱动类型的轻量级Web 框架,即使用了MVC 架构模式的思想,将 web 层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是为了简化开 发,Spring Web MVC 也是要简化我们日常Web 开发的。</p>
<p>说白了,Spring MVC 就是 【接收请求】【响应数据】</p>
<p>Spring MVC 下一般把后端项目分为 Service 层(处理业务)、Dao 层(数据库操作)、Entity 层(实体类)、Controller 层(控制层,返回数据给前台页面)。</p>
<p>常用组件:</p>
<ul>
<li>
<p>前端控制器(DispatcherServlet):接收用户请求,给用户返回结果。</p>
</li>
<li>
<p>处理器映射器(HandlerMapping):根据请求的url路径,通过注解或者xml配置,寻找匹配的Handler。</p>
</li>
<li>
<p>处理器适配器(HandlerAdapter):Handler 的适配器,调用 handler 的方法处理请求。</p>
</li>
<li>
<p>处理器(Handler):执行相关的请求处理逻辑,并返回相应的数据和视图信息,将其封装到ModelAndView对象中。</p>
</li>
<li>
<p>视图解析器(ViewResolver):将逻辑视图名解析成真正的视图View。</p>
</li>
<li>
<p>视图(View):接口类,实现类可支持不同的View类型(JSP、FreeMarker、Excel等)</p>
</li>
</ul>
<h2 id="mvc案例">MVC案例</h2>
<h3 id="基于webxml">基于webxml</h3>
<p><img src="https://seven97-blog.oss-cn-hangzhou.aliyuncs.com/imgs/202602011306237.png" alt="" loading="lazy"></p>
<p>示例源码点击这里</p>
<h4 id="maven引入">maven引入</h4>
<pre><code class="language-xml">&lt;project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"&gt;
    &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
    &lt;parent&gt;
      &lt;groupId&gt;com.seven&lt;/groupId&gt;
      &lt;artifactId&gt;spring-demo&lt;/artifactId&gt;
      &lt;version&gt;1.0-SNAPSHOT&lt;/version&gt;
    &lt;/parent&gt;
    &lt;artifactId&gt;07-spring-mvc-helloworld&lt;/artifactId&gt;
    &lt;packaging&gt;war&lt;/packaging&gt;
    &lt;name&gt;07-spring-mvc-helloworld Maven Webapp&lt;/name&gt;
    &lt;url&gt;http://maven.apache.org&lt;/url&gt;
    &lt;properties&gt;
      &lt;maven.compiler.source&gt;8&lt;/maven.compiler.source&gt;
      &lt;maven.compiler.target&gt;8&lt;/maven.compiler.target&gt;
      &lt;spring.version&gt;5.3.37&lt;/spring.version&gt;
      &lt;servlet.version&gt;4.0.1&lt;/servlet.version&gt;
    &lt;/properties&gt;

    &lt;dependencies&gt;
      &lt;dependency&gt;
            &lt;groupId&gt;org.springframework&lt;/groupId&gt;
            &lt;artifactId&gt;spring-webmvc&lt;/artifactId&gt;
            &lt;version&gt;${spring.version}&lt;/version&gt;
      &lt;/dependency&gt;
      &lt;dependency&gt;
            &lt;groupId&gt;javax.servlet&lt;/groupId&gt;
            &lt;artifactId&gt;javax.servlet-api&lt;/artifactId&gt;
            &lt;version&gt;${servlet.version}&lt;/version&gt;
      &lt;/dependency&gt;

      &lt;dependency&gt;
            &lt;groupId&gt;javax.servlet&lt;/groupId&gt;
            &lt;artifactId&gt;jstl&lt;/artifactId&gt;
            &lt;version&gt;1.2&lt;/version&gt;
      &lt;/dependency&gt;
      &lt;dependency&gt;
            &lt;groupId&gt;taglibs&lt;/groupId&gt;
            &lt;artifactId&gt;standard&lt;/artifactId&gt;
            &lt;version&gt;1.1.2&lt;/version&gt;
      &lt;/dependency&gt;
    &lt;/dependencies&gt;

    &lt;build&gt;
      &lt;finalName&gt;07-spring-mvc-helloworld&lt;/finalName&gt;
    &lt;/build&gt;
&lt;/project&gt;
</code></pre>
<h4 id="业务代码编写">业务代码编写</h4>
<ul>
<li>entity的User类</li>
</ul>
<pre><code class="language-java">@Data
@AllArgsConstructor
public class User {

    private String name;

    private int age;
}
</code></pre>
<ul>
<li>dao层</li>
</ul>
<pre><code class="language-java">@Repository
public class UserDaoImpl {

    public List&lt;User&gt; findUserList() {
      return Collections.singletonList(new User("seven", 18));
    }

}
</code></pre>
<ul>
<li>service层</li>
</ul>
<pre><code class="language-java">@Service
public class UserServiceImpl {


    @Autowired
    private UserDaoImpl userDao;


    public List&lt;User&gt; findUserList() {
      return userDao.findUserList();
    }

}
</code></pre>
<ul>
<li>controller层</li>
</ul>
<pre><code class="language-java">@Controller
public class UserController {

    @Autowired
    private UserServiceImpl userService;


    @RequestMapping("/user")
    public ModelAndView list(HttpServletRequest request, HttpServletResponse response) {
      ModelAndView modelAndView = new ModelAndView();
      modelAndView.addObject("dateTime", new Date());
      modelAndView.addObject("userList", userService.findUserList());
      modelAndView.setViewName("userList"); // views目录下userList.jsp
      return modelAndView;
    }

}
</code></pre>
<h4 id="webapp下的webxml">webapp下的web.xml</h4>
<pre><code class="language-xml">&lt;!DOCTYPE web-app PUBLIC
      "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
      "http://java.sun.com/dtd/web-app_2_3.dtd" &gt;

&lt;web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1"&gt;
    &lt;display-name&gt;Archetype Created Web Application&lt;/display-name&gt;

    &lt;servlet&gt;
      &lt;servlet-name&gt;springmvc-demo&lt;/servlet-name&gt;
      &lt;servlet-class&gt;org.springframework.web.servlet.DispatcherServlet&lt;/servlet-class&gt;
      &lt;!-- 通过初始化参数指定SpringMVC配置文件的位置和名称 --&gt;
      &lt;init-param&gt;
            &lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;
            &lt;param-value&gt;classpath:springmvc.xml&lt;/param-value&gt;
      &lt;/init-param&gt;
      &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
    &lt;/servlet&gt;

    &lt;servlet-mapping&gt;
      &lt;servlet-name&gt;springmvc-demo&lt;/servlet-name&gt;
      &lt;url-pattern&gt;/&lt;/url-pattern&gt;
    &lt;/servlet-mapping&gt;

    &lt;filter&gt;
      &lt;filter-name&gt;encodingFilter&lt;/filter-name&gt;
      &lt;filter-class&gt;org.springframework.web.filter.CharacterEncodingFilter&lt;/filter-class&gt;
      &lt;init-param&gt;
            &lt;param-name&gt;encoding&lt;/param-name&gt;
            &lt;param-value&gt;UTF-8&lt;/param-value&gt;
      &lt;/init-param&gt;
      &lt;init-param&gt;
            &lt;param-name&gt;forceEncoding&lt;/param-name&gt;
            &lt;param-value&gt;true&lt;/param-value&gt;
      &lt;/init-param&gt;
    &lt;/filter&gt;

    &lt;filter-mapping&gt;
      &lt;filter-name&gt;encodingFilter&lt;/filter-name&gt;
      &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
    &lt;/filter-mapping&gt;

&lt;/web-app&gt;
</code></pre>
<h4 id="springmvcxml">springmvc.xml</h4>
<p>web.xml中配置初始化参数contextConfigLocation,路径是classpath:springmvc.xml,因此文件直接创建在resources目录下</p>
<pre><code class="language-xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"&gt;

    &lt;!-- 扫描注解 --&gt;
    &lt;context:component-scan base-package="com.seven.springmvchelloworld"/&gt;

    &lt;!-- 静态资源处理 --&gt;
    &lt;mvc:default-servlet-handler/&gt;

    &lt;!-- 开启SpringMVC注解 --&gt;
    &lt;mvc:annotation-driven/&gt;
    &lt;!-- 可以代替下边的配置,实际开发中一般使用上面的方式开启注解--&gt;
    &lt;!--注解映射器 --&gt;
        &lt;!--&lt;bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/&gt;--&gt;
        &lt;!--注解适配器 --&gt;
        &lt;!--&lt;bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/&gt;--&gt;
   

    &lt;!-- 视图解析器 --&gt;
    &lt;bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"&gt;
      &lt;property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/&gt;
      &lt;property name="prefix" value="/views/"/&gt;
      &lt;property name="suffix" value=".jsp"/&gt;
    &lt;/bean&gt;

&lt;/beans&gt;
</code></pre>
<h4 id="jsp视图">JSP视图</h4>
<p>创建userList.jsp</p>
<pre><code class="language-java">&lt;%@ page contentType="text/html;charset=UTF-8" language="java" %&gt;
&lt;%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %&gt;
&lt;!DOCTYPE html&gt;
&lt;html lang="zh-CN"&gt;
&lt;head&gt;
    &lt;meta charset="utf-8"&gt;
    &lt;meta http-equiv="X-UA-Compatible" content="IE=edge"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1"&gt;

    &lt;title&gt;User List&lt;/title&gt;

    &lt;!-- Bootstrap --&gt;
    &lt;link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css"&gt;

&lt;/head&gt;
&lt;body&gt;
&lt;div class="container"&gt;
    &lt;c:if test="${!empty userList}"&gt;
      &lt;table class="table table-bordered table-striped"&gt;
            &lt;tr&gt;
                &lt;th&gt;Name&lt;/th&gt;
                &lt;th&gt;Age&lt;/th&gt;
            &lt;/tr&gt;
            &lt;c:forEach items="${userList}" var="user"&gt;
                &lt;tr&gt;
                  &lt;td&gt;${user.name}&lt;/td&gt;
                  &lt;td&gt;${user.age}&lt;/td&gt;
                &lt;/tr&gt;
            &lt;/c:forEach&gt;
      &lt;/table&gt;
    &lt;/c:if&gt;
&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>之后就是使用tomcat部署测试了,这块就不说了</p>
<h3 id="纯注解版">纯注解版</h3>
<p>无需配置xml文件,依靠注解和配置类完成配置,注意需要注意满足sevlet3.0规范</p>
<p>具体源码点击这里</p>
<p>这个不做过多讲解,真实项目的用得较少。因为若是老项目,就是基于webxml的,若是新项目,则直接上springboot了。</p>
<h2 id="spring-mvc响应请求">Spring MVC响应请求</h2>
<h3 id="直接返回modelandview对象">直接返回ModelAndView对象</h3>
<p>ModelAndView对象将数据模型和视图信息封装在一起。</p>
<pre><code class="language-java">@RequestMapping("/order")
public ModelAndView getOrderPage() {
    // 1. 创建ModelAndView对象
    ModelAndView mav = new ModelAndView();
   
    // 2. 添加模型数据(相当于model.addAttribute)
    Order order = orderService.getLatestOrder();
    mav.addObject("currentOrder", order);
    mav.addObject("pageTitle", "订单详情");
   
    // 3. 设置视图名称
    mav.setViewName("orderDetail"); // 视图解析器会处理为完整路径
   
    return mav;
}
</code></pre>
<h3 id="返回视图名称页面跳转">返回视图名称(页面跳转)</h3>
<p>该方法返回 <code>"userDetail.jsp"</code>,并可以在页面上通过 <code>${user}</code>获取数据</p>
<pre><code class="language-java">@Controller
@RequestMapping("/user")
public class UserController {
   
    @RequestMapping("/detail")
    public String getUserDetail(Model model) {
      // 模拟查询用户信息
      User user = userService.findUserById(1);
      // 将数据添加到Model中,会自动存入请求域
      model.addAttribute("user", user);
      // 返回逻辑视图名,视图解析器会将其拼接为 "/WEB-INF/views/userDetail.jsp"
      return "userDetail.jsp";
    }
}
</code></pre>
<h3 id="使用map传递数据">使用Map传递数据</h3>
<p>该方法返回 <code>"userDetail.jsp"</code>,并可以在页面上通过 <code>${user}</code>获取数据</p>
<pre><code class="language-java">@Controller
@RequestMapping("/user")
public class UserController {
   
    @RequestMapping("/detail")
    public String getUserDetail(Map&lt;String, User&gt; map) {
      // 模拟查询用户信息
      User user = userService.findUserById(1);
      // 将数据添加到Model中,会自动存入请求域
      map.addAttribute("user", user);
      // 返回逻辑视图名,视图解析器会将其拼接为 "/WEB-INF/views/userDetail.jsp"
      return "userDetail.jsp";
    }
}
</code></pre>
<h3 id="返回void">返回void</h3>
<p>这种方式绕过了SpringMVC的视图解析,提供了最大灵活性,但需要自行处理响应细节,与Servlet API耦合度高,一般不推荐作为主要方式</p>
<pre><code class="language-java">@RequestMapping("/raw")
public void handleRawResponse(HttpServletResponse response) throws IOException {
    // 设置响应类型和编码
    response.setContentType("text/plain; charset=UTF-8");
    // 直接通过HttpServletResponse输出
    PrintWriter out = response.getWriter();
    out.write("这是一个直接输出的响应");
    out.flush();
}
</code></pre>
<h3 id="重定向跳转">重定向跳转</h3>
<p>redirect:会让浏览器地址栏变为新的URL。注意,重定向是两次请求,原始请求域(request scope)中的数据会丢失。若要传递参数,可使用 RedirectAttributes</p>
<pre><code class="language-java">@PostMapping("/submit")
public String submitForm(LoginForm form) {
    // ... 处理表单提交逻辑,如保存数据
    boolean isSuccess = loginService.processLogin(form);
   
    // 重定向到另一个地址,防止用户刷新浏览器导致表单重复提交
    return "redirect:/login/success"; // 浏览器会向 "/login/success" 发起新请求
}

@GetMapping("/success")
public String successPage() {
    return "success"; // 展示成功页面
}
</code></pre>
<h3 id="使用httpservletresponse">使用HttpServletResponse</h3>
<p>此方式适用于文件下载、输出特定二进制内容等需要精细控制输出流的场景。它完全绕过了SpringMVC的视图解析机制</p>
<pre><code class="language-java">@RequestMapping("/download")
public void downloadFile(HttpServletResponse response) throws IOException {
    // 设置响应头,告诉浏览器这是一个要下载的PDF文件
    response.setContentType("application/pdf");
    response.setHeader("Content-Disposition", "attachment; filename=\"document.pdf\"");
   
    // 获取文件流(此处为模拟)
    byte[] fileContent = getFileContent();
   
    // 通过ServletResponse的输出流直接写入数据
    ServletOutputStream out = response.getOutputStream();
    out.write(fileContent);
    out.flush();
}
</code></pre>
<h3 id="直接返回数据如json">直接返回数据(如JSON)</h3>
<p>@ResponseBody注解是核心,它告诉Spring将方法返回值直接写入响应流。若项目中配置了消息转换器(如Jackson),可直接返回对象,Spring会自动将其转为JSON</p>
<pre><code class="language-java">@Controller
@RequestMapping("/api")
public class ApiController {

    @RequestMapping(value = "/user", produces = "application/json;charset=UTF-8")
    @ResponseBody // 关键注解:表明返回值直接作为HTTP响应体,不进行视图解析
    public String getUserAsJson() {
      User user = new User("张三", 25);
      // 手动将对象转为JSON字符串(需Jackson等库)
      ObjectMapper mapper = new ObjectMapper();
      try {
            return mapper.writeValueAsString(user);
      } catch (JsonProcessingException e) {
            return "{\"error\": \"转换失败\"}";
      }
      // 更佳实践:直接返回对象,配置消息转换器自动转JSON(见后续说明)
    }
}
</code></pre>
<h2 id="sringmvc接收数据">SringMVC接收数据</h2>
<h3 id="基本数据类型接收">基本数据类型接收</h3>
<pre><code class="language-java">@RestController
@RequestMapping("/api/user")
public class UserController {
   
    /**
   * 接收单个基本类型参数
   * GET /api/user/detail?id=123
   */
    @GetMapping("/detail")
    public String getUserDetail(@RequestParam("id") Long userId) {
      // @RequestParam将请求参数"id"映射到方法参数userId
      return "用户ID: " + userId;
    }
   
    /**
   * 参数可选,设置默认值
   * GET /api/user/list 或 /api/user/list?page=2
   */
    @GetMapping("/list")
    public String getUserList(
            @RequestParam(value = "page", required = false, defaultValue = "1") Integer page,
            @RequestParam(value = "size", required = false, defaultValue = "10") Integer size) {
      // required=false表示参数可选,defaultValue设置默认值
      return String.format("第%d页,每页%d条", page, size);
    }
   
    /**
   * 简化写法:参数名与方法参数名一致时可省略@RequestParam
   * GET /api/user/simple?name=张三&amp;age=25
   */
    @GetMapping("/simple")
    public String simpleParams(String name, Integer age) {
      // 当请求参数名与方法参数名一致时,可以省略@RequestParam
      return "姓名: " + name + ", 年龄: " + age;
    }
}
</code></pre>
<h3 id="接收路径参数">接收路径参数</h3>
<pre><code class="language-java">@RestController
@RequestMapping("/api/product")
public class ProductController {
   
    /**
   * 接收路径参数
   * GET /api/product/1001/category/2001
   */
    @GetMapping("/{productId}/category/{categoryId}")
    public String getProductInfo(
            @PathVariable("productId") Long productId,
            @PathVariable("categoryId") Long categoryId) {
      // @PathVariable从URL路径中提取参数
      return String.format("产品ID: %d, 分类ID: %d", productId, categoryId);
    }
   
    /**
   * 正则表达式限制路径参数格式
   * GET /api/product/2023-10-25
   */
    @GetMapping("/{date:\\d{4}-\\d{2}-\\d{2}}")
    public String getProductsByDate(@PathVariable String date) {
      // 使用正则表达式限制日期格式
      return "查询日期: " + date;
    }
}
</code></pre>
<h3 id="对象接收自动绑定">对象接收(自动绑定)</h3>
<h4 id="接收简单对象参数">接收简单对象参数</h4>
<pre><code class="language-java">/**
* 用户查询参数对象
*/
public class UserQueryParams {
    private String username;
    private String email;
    private Integer age;
    private Date createTime;
   
    // 必须提供getter和setter方法
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
   
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
   
    public Integer getAge() { return age; }
    public void setAge(Integer age) { this.age = age; }
   
    public Date getCreateTime() { return createTime; }
    public void setCreateTime(Date createTime) { this.createTime = createTime; }
   
    @Override
    public String toString() {
      return String.format("UserQuery{username='%s', email='%s', age=%d}",
                           username, email, age);
    }
}

@RestController
@RequestMapping("/api/users")
public class UserController {
   
    /**
   * 对象接收 - GET请求
   * GET /api/users/search?username=张三&amp;email=zhang@example.com&amp;age=25
   */
    @GetMapping("/search")
    public String searchUsers(UserQueryParams params) {
      // Spring自动将请求参数绑定到对象属性
      return "查询参数: " + params.toString();
    }
   
    /**
   * 对象接收 - POST表单提交
   * POST /api/users/create
   * Content-Type: application/x-www-form-urlencoded
   * Body: username=李四&amp;email=li@example.com&amp;age=30
   */
    @PostMapping("/create")
    public String createUser(UserQueryParams user) {
      return "创建用户: " + user.toString();
    }
}
</code></pre>
<h4 id="接收嵌套对象参数">接收嵌套对象参数</h4>
<pre><code class="language-java">/**
* 地址信息
*/
public class Address {
    private String province;
    private String city;
    private String street;
    // getter/setter省略...
}

/**
* 用户信息(包含嵌套对象)
*/
public class UserInfo {
    private String name;
    private Integer age;
    private Address address; // 嵌套对象
   
    // getter/setter...
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
   
    public Integer getAge() { return age; }
    public void setAge(Integer age) { this.age = age; }
   
    public Address getAddress() { return address; }
    public void setAddress(Address address) { this.address = address; }
}

@RestController
@RequestMapping("/api/profile")
public class ProfileController {
   
    /**
   * 接收嵌套对象参数
   * GET /api/profile/update?name=王五&amp;age=28&amp;address.province=北京&amp;address.city=北京市
   */
    @PostMapping("/update")
    public String updateProfile(UserInfo userInfo) {
      // 使用点号语法接收嵌套对象属性
      return String.format("用户: %s, 年龄: %d, 地址: %s-%s",
            userInfo.getName(), userInfo.getAge(),
            userInfo.getAddress().getProvince(),
            userInfo.getAddress().getCity());
    }
}
</code></pre>
<h3 id="数组接收">数组接收</h3>
<p>数组在表单中的应用</p>
<pre><code class="language-java">&lt;!-- 前端HTML表单示例 --&gt;
&lt;form action="/api/products/batch-delete" method="post"&gt;
    &lt;!-- 多个同名的checkbox --&gt;
    &lt;input type="checkbox" name="productIds" value="1001"&gt; 产品A
    &lt;input type="checkbox" name="productIds" value="1002"&gt; 产品B
    &lt;input type="checkbox" name="productIds" value="1003"&gt; 产品C
    &lt;button type="submit"&gt;批量删除&lt;/button&gt;
&lt;/form&gt;
</code></pre>
<pre><code class="language-java">@RestController
@RequestMapping("/api/products")
public class ProductController {
   
    /**
   * 批量删除产品
   * POST /api/products/batch-delete
   * Body: productIds=1001&amp;productIds=1002&amp;productIds=1003
   */
    @PostMapping("/batch-delete")
    public String batchDeleteProducts(@RequestParam Long[] productIds) {
      return "删除的产品ID: " + Arrays.toString(productIds);
    }
}
</code></pre>
<h3 id="集合接收通过包装对象">集合接收(通过包装对象)</h3>
<p>SpringMVC 不能直接在方法参数中接收集合,但可以通过对象包装的方式来接收。</p>
<pre><code class="language-java">/**
* 包装类,包含集合属性
*/
public class BatchOperation {
    private List&lt;Long&gt; ids;
    private List&lt;String&gt; names;
   
    // getter/setter...
    public List&lt;Long&gt; getIds() { return ids; }
    public void setIds(List&lt;Long&gt; ids) { this.ids = ids; }
   
    public List&lt;String&gt; getNames() { return names; }
    public void setNames(List&lt;String&gt; names) { this.names = names; }
}

@RestController
@RequestMapping("/api/batch")
public class BatchController {
   
    /**
   * 接收集合参数 - 通过包装对象
   * POST /api/batch/process
   * 请求体格式1: ids=1&amp;ids=2&amp;ids=3
   * 请求体格式2: names=Alice&amp;names=Bob&amp;names=Charlie
   */
    @PostMapping("/process")
    public String processBatch(BatchOperation operation) {
      StringBuilder result = new StringBuilder();
      if (operation.getIds() != null) {
            result.append("ID列表: ").append(operation.getIds());
      }
      if (operation.getNames() != null) {
            result.append("名称列表: ").append(operation.getNames());
      }
      return result.toString();
    }
}
</code></pre>
<h3 id="自定义转换器">自定义转换器</h3>
<ul>
<li>日期格式转换器</li>
</ul>
<pre><code class="language-java">/**
* 自定义日期转换器
* 将字符串转换为Date对象
*/
@Component
public class StringToDateConverter implements Converter&lt;String, Date&gt; {
   
    private static final String[] DATE_PATTERNS = {
      "yyyy-MM-dd",
      "yyyy/MM/dd",
      "yyyy-MM-dd HH:mm:ss",
      "yyyy/MM/dd HH:mm:ss"
    };
   
    @Override
    public Date convert(String source) {
      if (source == null || source.trim().isEmpty()) {
            return null;
      }
      
      // 尝试多种日期格式
      for (String pattern : DATE_PATTERNS) {
            try {
                SimpleDateFormat format = new SimpleDateFormat(pattern);
                format.setLenient(false); // 严格模式
                return format.parse(source);
            } catch (ParseException e) {
                // 尝试下一种格式
                continue;
            }
      }
      
      throw new IllegalArgumentException("无效的日期格式: " + source +
            ",支持的格式: " + Arrays.toString(DATE_PATTERNS));
    }
}
</code></pre>
<ul>
<li>枚举类型转换器</li>
</ul>
<pre><code class="language-java">/**
* 用户状态枚举
*/
public enum UserStatus {
    ACTIVE("活跃"),
    INACTIVE("非活跃"),
    DELETED("已删除");
   
    private final String description;
   
    UserStatus(String description) {
      this.description = description;
    }
   
    public String getDescription() {
      return description;
    }
}

/**
* 字符串到枚举转换器
*/
@Component
public class StringToUserStatusConverter implements Converter&lt;String, UserStatus&gt; {
   
    @Override
    public UserStatus convert(String source) {
      if (source == null || source.trim().isEmpty()) {
            return null;
      }
      
      // 不区分大小写匹配
      for (UserStatus status : UserStatus.values()) {
            if (status.name().equalsIgnoreCase(source)) {
                return status;
            }
      }
      
      // 也支持中文描述匹配
      for (UserStatus status : UserStatus.values()) {
            if (status.getDescription().equals(source)) {
                return status;
            }
      }
      
      throw new IllegalArgumentException("无效的用户状态: " + source);
    }
}
</code></pre>
<ul>
<li>注册自定义转换器</li>
</ul>
<pre><code class="language-java">@Configuration
public class WebConfig implements WebMvcConfigurer {
   
    @Autowired
    private StringToDateConverter stringToDateConverter;
   
    @Autowired
    private StringToUserStatusConverter stringToUserStatusConverter;
   
    /**
   * 注册自定义转换器
   */
    @Override
    public void addFormatters(FormatterRegistry registry) {
      registry.addConverter(stringToDateConverter);
      registry.addConverter(stringToUserStatusConverter);
    }
}
</code></pre>
<ul>
<li>在控制器中使用自定义转换</li>
</ul>
<pre><code class="language-java">@RestController
@RequestMapping("/api/converter")
public class ConverterController {
   
    /**
   * 使用自定义日期转换器
   * GET /api/converter/date?date=2023-10-25
   */
    @GetMapping("/date")
    public String handleDateParam(@RequestParam Date date) {
      // Spring会自动使用我们注册的StringToDateConverter
      SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日");
      return "转换后的日期: " + format.format(date);
    }
   
    /**
   * 使用自定义枚举转换器
   * GET /api/converter/status?status=ACTIVE
   * GET /api/converter/status?status=活跃
   */
    @GetMapping("/status")
    public String handleStatusParam(@RequestParam UserStatus status) {
      return "用户状态: " + status.getDescription();
    }
   
    /**
   * 在对象中使用自定义转换
   * GET /api/converter/user?name=张三&amp;createTime=2023-10-25 14:30:00&amp;status=INACTIVE
   */
    @GetMapping("/user")
    public String handleUserObject(UserQuery userQuery) {
      // UserQuery对象中包含Date和UserStatus属性
      return String.format("用户: %s, 创建时间: %s, 状态: %s",
            userQuery.getName(),
            userQuery.getCreateTime(),
            userQuery.getStatus().getDescription());
    }
}

/**
* 用户查询对象(包含需要自定义转换的属性)
*/
class UserQuery {
    private String name;
    private Date createTime;      // 需要自定义转换
    private UserStatus status;    // 需要自定义转换
   
    // getter/setter...
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
   
    public Date getCreateTime() { return createTime; }
    public void setCreateTime(Date createTime) { this.createTime = createTime; }
   
    public UserStatus getStatus() { return status; }
    public void setStatus(UserStatus status) { this.status = status; }
}
</code></pre>
<h2 id="springmvc其它使用">SpringMVC其它使用</h2>
<h3 id="视图解析器添加前后缀">视图解析器添加前后缀</h3>
<p>配置视图解析器可以免去重复书写视图文件路径的前后缀。</p>
<ul>
<li>xml 或者Java Config</li>
</ul>
<pre><code class="language-xml"> &lt;!-- 视图解析器 --&gt;
    &lt;bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"&gt;
      &lt;property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/&gt;
      &lt;property name="prefix" value="/views/"/&gt;
      &lt;property name="suffix" value=".jsp"/&gt;
    &lt;/bean&gt;
</code></pre>
<pre><code class="language-java">@Configuration
public class ViewConfig {
   
    @Bean
    public ViewResolver viewResolver() {
      InternalResourceViewResolver resolver = new InternalResourceViewResolver();
      // 设置所有视图文件所在的公共前缀
      resolver.setPrefix("/views/");
      // 设置视图文件的公共后缀
      resolver.setSuffix(".jsp");
      return resolver;
    }
}
</code></pre>
<ul>
<li>控制器代码</li>
</ul>
<p>此配置后,控制器返回的 <code>"index"</code>会被自动补全为 <code>/views/index.jsp</code>,极大简化了视图管理</p>
<pre><code class="language-java">@Controller
public class HomeController {
   
    @RequestMapping("/home")
    public String home() {
      // 控制器中只需返回逻辑视图名 "index"
      // 视图解析器会自动拼接为 "/WEB-INF/views/index.jsp"
      return "index";
    }
}
</code></pre>
<h3 id="中文乱码问题处理">中文乱码问题处理</h3>
<pre><code class="language-xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                           http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0"&gt;

    &lt;!-- 1. 配置字符编码过滤器(必须放在第一个) --&gt;
    &lt;filter&gt;
      &lt;filter-name&gt;characterEncodingFilter&lt;/filter-name&gt;
      &lt;filter-class&gt;org.springframework.web.filter.CharacterEncodingFilter&lt;/filter-class&gt;
      &lt;init-param&gt;
            &lt;!-- 设置编码为UTF-8 --&gt;
            &lt;param-name&gt;encoding&lt;/param-name&gt;
            &lt;param-value&gt;UTF-8&lt;/param-value&gt;
      &lt;/init-param&gt;
      &lt;init-param&gt;
            &lt;!-- 强制请求和响应都使用UTF-8编码 --&gt;
            &lt;param-name&gt;forceEncoding&lt;/param-name&gt;
            &lt;param-value&gt;true&lt;/param-value&gt;
      &lt;/init-param&gt;
    &lt;/filter&gt;
   
    &lt;filter-mapping&gt;
      &lt;filter-name&gt;characterEncodingFilter&lt;/filter-name&gt;
      &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
    &lt;/filter-mapping&gt;
&lt;/web-app&gt;
</code></pre>
<h3 id="静态资源处理">静态资源处理</h3>
<p>默认情况下,DispatcherServlet 会拦截所有请求,包括静态资源请求,这会导致静态资源无法正常访问。因此,我们需要配置 Spring MVC 以允许容器直接提供静态资源。</p>
<p>有两种主要方式来处理静态资源:</p>
<ol>
<li>使用 <code>&lt;mvc:resources /&gt;</code>标签(XML 配置)</li>
<li>使用 <code>WebMvcConfigurer</code>的 <code>addResourceHandlers</code>方法(Java 配置)</li>
</ol>
<p>另外,还可以使用 <code>&lt;mvc:default-servlet-handler /&gt;</code>来允许容器默认的 Servlet 处理静态资源。</p>
<ul>
<li>XML 配置方式-使用默认Servlet处理(简单方式)</li>
</ul>
<pre><code class="language-xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/mvc
      http://www.springframework.org/schema/mvc/spring-mvc.xsd"&gt;

    &lt;!-- 启用默认Servlet处理静态资源 --&gt;
    &lt;mvc:default-servlet-handler/&gt;
   
    &lt;!-- 启用注解驱动 --&gt;
    &lt;mvc:annotation-driven/&gt;
   
    &lt;!-- 控制器扫描 --&gt;
    &lt;context:component-scan base-package="com.example.controller"/&gt;
&lt;/beans&gt;
</code></pre>
<ul>
<li>XML 配置方式-使用资源映射(推荐方式)</li>
</ul>
<pre><code class="language-xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/mvc
      http://www.springframework.org/schema/mvc/spring-mvc.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd"&gt;

    &lt;!-- 启用注解驱动 --&gt;
    &lt;mvc:annotation-driven/&gt;
   
    &lt;!-- 控制器扫描 --&gt;
    &lt;context:component-scan base-package="com.example.controller"/&gt;
   
    &lt;!-- 静态资源映射配置 --&gt;
    &lt;mvc:resources mapping="/static/**" location="/static/"/&gt;
    &lt;mvc:resources mapping="/css/**" location="/css/"/&gt;
    &lt;mvc:resources mapping="/js/**" location="/js/"/&gt;
    &lt;mvc:resources mapping="/images/**" location="/images/"/&gt;
    &lt;mvc:resources mapping="/uploads/**" location="file:/var/uploads/"/&gt;
   
    &lt;!-- 带版本控制的资源映射 --&gt;
    &lt;mvc:resources mapping="/resources/**" location="/resources/" cache-period="3600"/&gt;
   
    &lt;!-- WebJars支持 --&gt;
    &lt;mvc:resources mapping="/webjars/**" location="classpath:/META-INF/resources/webjars/"/&gt;
&lt;/beans&gt;
</code></pre>
<ul>
<li>JavaConfig 配置方式</li>
</ul>
<pre><code class="language-java">import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
   
    /**
   * 配置静态资源处理
   */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
      // 1. 类路径下的静态资源
      registry.addResourceHandler("/static/**")
                .addResourceLocations("classpath:/static/")
                .setCachePeriod(3600); // 缓存1小时
      
      // 2. Web根目录下的资源
      registry.addResourceHandler("/css/**")
                .addResourceLocations("/css/");
      
      registry.addResourceHandler("/js/**")
                .addResourceLocations("/js/");
      
      registry.addResourceHandler("/images/**")
                .addResourceLocations("/images/");
      
      // 3. 外部文件系统资源
      registry.addResourceHandler("/uploads/**")
                .addResourceLocations("file:/var/uploads/");
      
      // 4. WebJars支持
      registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
}
</code></pre>


</div>
<div id="MySignature" role="contentinfo">
    <p>本文来自在线网站:seven的菜鸟成长之路,作者:seven,转载请注明原文链接:www.seven97.top</p><br><br>
来源:https://www.cnblogs.com/sevencoding/p/19788264
頁: [1]
查看完整版本: MVC快速入门