首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Spring MVC 實戰全棧教程:從 HelloWorld 到企業級整合

     編輯:Java 綜合教學

Spring MVC 實戰全棧教程:從 HelloWorld 到企業級整合

本教程以實戰項目為主線,系統講解 Spring MVC 核心配置、常用注解、參數綁定、數據校驗、文件上傳下載、RESTful API、異常處理、攔截器、國際化、與 Spring IOC 整合等企業級開發全棧技能。

第一階段:快速構建第一個 Spring MVC 應用

1. 依賴管理

核心 Jar:spring-webmvc、spring-web、spring-context、spring-beans、spring-core、commons-logging、Servlet API、Jackson(JSON 支持)。

2. web.xml 核心配置

<!-- DispatcherServlet:前端控制器 -->
<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern> <!-- 攔截所有請求 -->
</servlet-mapping>

<!-- Spring IOC 容器啟動(可選,整合時必需) -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>

3. Spring MVC 配置文件 (springmvc-servlet.xml)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
       xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="https://www.springframework.org/schema/context"
       xmlns:mvc="https://www.springframework.org/schema/mvc"
       xsi:schemaLocation="...">

    <!-- 掃描 Controller 包 -->
    <context:component-scan base-package="com.example.controller" />

    <!-- 靜態資源默認處理 -->
    <mvc:default-servlet-handler />

    <!-- 註解驅動:參數綁定、格式化、驗證、MessageConverter 自動裝配 -->
    <mvc:annotation-driven />

    <!-- 視圖解析器:JSP 前綴/後綴 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

4. Controller 編寫

@Controller
@RequestMapping("/mvc")
public class HelloController {
    @RequestMapping("/hello")
    public String hello() {
        return "hello"; // 視圖名 -> /WEB-INF/views/hello.jsp
    }
}

5. 視圖頁面

src/main/webapp/WEB-INF/views/hello.jsp:<body>Hello World</body>

6. 訪問驗證

啟動服務器,訪問 https://localhost:8080/項目名/mvc/hello,顯示 "Hello World"。

第二階段:核心注解與參數綁定大全

注解作用關鍵屬性
@Controller標識控制器 Bean,掃描註冊到 Spring 容器-
@RequestMapping映射請求 URL、方法、參數、Headervalue, method, params, headers, consumes, produces
@RequestBody讀取請求體,通過 HttpMessageConverter 反序列化為對象required
@ResponseBody將返回對象序列化寫入響應體-
@ModelAttribute方法級:預執行綁定模型;參數級:從模型獲取並綁定請求參數value (模型鍵名)
@RequestParam綁定請求參數到方法參數value, required, defaultValue
@PathVariable綁定 URL 佔位符到方法參數value, required
@ExceptionHandler標識異常處理方法value (異常類數組)
@ControllerAdvice全局異常處理、數據綁定、模型增強basePackages, annotations
@InitBinder註冊自定義屬性編輯器(如日期格式化)value (命令對象名)
@Valid / @Validated觸發 JSR-303 Bean Validation 驗證-
@SessionAttributes將模型屬性存入 Session 共享value (屬性名數組)

第三階段:進階數據綁定與類型轉換

1. 自動匹配基本類型/POJO

// 請求參數 name=jayjay&age=20 自動綁定
@RequestMapping("/person")
public String toPerson(String name, double age) { ... }

// POJO 自動裝箱:請求參數自動填充屬性
@RequestMapping("/person1")
public String toPerson(Person p) { ... } // Person 需有無參構造器、setter

2. 日期類型轉換 (@InitBinder)

@InitBinder
public void initBinder(WebDataBinder binder) {
    // 註冊日期編輯器:yyyy-MM-dd 格式
    binder.registerCustomEditor(Date.class, 
        new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
}

@RequestMapping("/date")
public String date(Date date) { ... } // 請求參數 date=2023-10-01 自動轉換

3. 向前台傳遞數據

// 方式1:Map/Model/ModelMap
@RequestMapping("/show")
public String showPerson(Map<String, Object> map) {
    map.put("p", new Person("jayjay", 20));
    return "show"; // JSP 中 ${p.name} 獲取
}

// 方式2:ModelAndView
@RequestMapping("/show2")
public ModelAndView showPerson2() {
    ModelAndView mv = new ModelAndView("show");
    mv.addObject("p", new Person("jayjay", 20));
    return mv;
}

第四階段:AJAX 與 JSON 交互

1. 返回 JSON (@ResponseBody + Jackson)

@Controller
@RequestMapping("/json")
public class JsonController {
    @ResponseBody
    @RequestMapping("/user")
    public User getUser() {
        User u = new User();
        u.setId(1);
        u.setName("jayjay");
        u.setBirth(new Date());
        return u; // 自動序列化為 JSON
    }
}

需導入 Jackson 依賴:jackson-databind、jackson-core、jackson-annotations。<mvc:annotation-driven> 會自動裝配 MappingJackson2HttpMessageConverter。

2. 前端 jQuery 調用

$(function() {
    $("#btn").click(function() {
        $.post("mvc/getPerson", {name: $("#name").val()}, function(data) {
            alert(data); // 接收純文本或 JSON
        });
    });
});

第五階段:重定向與文件上傳下載

1. 重定向

@RequestMapping("/redirect")
public String redirect() {
    return "redirect:hello"; // 重定向到 /mvc/hello
    // 或 redirect:/otherPath、redirect:https://example.com
}

2. 文件上傳

依賴:commons-fileupload、commons-io

配置 MultipartResolver:

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="104857600" /> <!-- 100MB -->
</bean>

Controller 處理:

@RequestMapping(value = "/upload", method = RequestMethod.POST)
public String upload(HttpServletRequest req) throws Exception {
    MultipartHttpServletRequest mreq = (MultipartHttpServletRequest) req;
    MultipartFile file = mreq.getFile("file");
    String fileName = file.getOriginalFilename();
    // 生成唯一文件名:時間戳 + 原後綴
    String newName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) 
                   + fileName.substring(fileName.lastIndexOf('.'));
    // 保存到項目部署路徑/upload/
    FileOutputStream fos = new FileOutputStream(
        req.getSession().getServletContext().getRealPath("/") + "upload/" + newName);
    fos.write(file.getBytes());
    fos.close();
    return "hello";
}

前端表單:enctype="multipart/form-data",method="post",<input type="file" name="file">

3. 文件下載

@RequestMapping("/download")
public void download(HttpServletResponse response, String fileName) throws Exception {
    // 設置響應頭
    response.setContentType("application/octet-stream");
    response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
    
    // 讀取文件寫入響應流
    FileInputStream fis = new FileInputStream(realPath + "upload/" + fileName);
    OutputStream os = response.getOutputStream();
    byte[] buffer = new byte[1024];
    int len;
    while ((len = fis.read(buffer)) != -1) {
        os.write(buffer, 0, len);
    }
    fis.close();
    os.flush();
    os.close();
}

前端:window.open("mvc/download?fileName=" + encodeURIComponent(name)) 解決中文亂碼。

第六階段:RESTful 風格 API 設計

1. RESTful Controller

@Controller
@RequestMapping("/rest")
public class RestController {
    @RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
    public String get(@PathVariable("id") Integer id) { ... }

    @RequestMapping(value = "/user/{id}", method = RequestMethod.POST)
    public String post(@PathVariable("id") Integer id) { ... }

    @RequestMapping(value = "/user/{id}", method = RequestMethod.PUT)
    public String put(@PathVariable("id") Integer id) { ... }

    @RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)
    public String delete(@PathVariable("id") Integer id) { ... }
}

2. 瀏覽器表單模擬 PUT/DELETE (HiddenHttpMethodFilter)

web.xml 配置:

<filter>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

前端表單:隱藏字段 _method=PUT/DELETE,method="post",Filter 會轉換請求方法。

第七階段:異常處理體系

類型實現方式適用場景
局部異常Controller 內 @ExceptionHandler 方法返回 ModelAndView特定 Controller 業務異常
全局異常 (注解)@ControllerAdvice 類 + @ExceptionHandler 方法全應用統一異常處理、日志、友好頁面
全局異常 (配置)SimpleMappingExceptionResolver Bean 映射異常類到視圖名無侵入式配置、傳統項目

第八階段:攔截器機制

1. 自定義攔截器

public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) {
        System.out.println("preHandle: 權限檢查、日志記錄");
        return true; // false 終止後續執行
    }
    @Override
    public void postHandle(HttpServletRequest req, HttpServletResponse res, Object handler, ModelAndView mv) {
        System.out.println("postHandle: 視圖渲染前,可修改模型數據");
    }
    @Override
    public void afterCompletion(HttpServletRequest req, HttpServletResponse res, Object handler, Exception ex) {
        System.out.println("afterCompletion: 視圖渲染後,資源清理、耗時統計");
    }
}

2. 配置映射路徑

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/mvc/**" />
        <bean class="com.example.interceptor.MyInterceptor" />
    </mvc:interceptor>
</mvc:interceptors>

第九階段:表單驗證與國際化 (Hibernate Validator)

1. 實體類驗證注解

public class User {
    private int id;
    
    @NotEmpty(message = "{NotEmpty.user.name}")
    private String name;
    
    @Past(message = "{Past.user.birth}")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birth;
    // getter/setter/toString
}

2. JSP 表單綁定錯誤顯示

<form:form action="form/add" method="post" modelAttribute="user">
    id: <form:input path="id" /><form:errors path="id" /><br/>
    name: <form:input path="name" /><form:errors path="name" /><br/>
    birth: <form:input path="birth" /><form:errors path="birth" />
    <input type="submit" value="submit" />
</form:form>

3. Controller 驗證處理

@Controller
@RequestMapping("/form")
public class FormController {
    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public String add(@Valid User u, BindingResult br) {
        if (br.getErrorCount() > 0) {
            return "addUser"; // 回顯表單與錯誤
        }
        return "showUser";
    }
    @RequestMapping(value = "/add", method = RequestMethod.GET)
    public String addForm(Map<String, Object> map) {
        map.put("user", new User()); // 必須預置模型對象
        return "addUser";
    }
}

4. 錯誤消息國際化

src/main/resources/locale.properties (默認) / locale_zh_CN.properties (中文)

# locale.properties
NotEmpty.user.name=Name cannot be empty
Past.user.birth=Birth must be a past date

# locale_zh_CN.properties
NotEmpty.user.name=姓名不能為空
Past.user.birth=出生日期必須是過去日期

配置 MessageSource:

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basename" value="locale" />
</bean>

5. 頁面國際化顯示

<%@ taglib prefix="fmt" uri="https://java.sun.com/jsp/jstl/fmt" %>
<fmt:message key="username" /> <!-- 顯示賬號/Account -->
<fmt:message key="password" /> <!-- 顯示密碼/Password -->

瀏覽器語言切換自動生效。

第十階段:Spring IOC 與 Spring MVC 深度整合

核心原則:容器分離,職責單一

  • 父容器 (ContextLoaderListener):掃描 Service、DAO、Component,**排除** @Controller/@ControllerAdvice
  • 子容器 (DispatcherServlet):僅掃描 @Controller/@ControllerAdvice

父容器配置

<context:component-scan base-package="com.example">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
    <context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>

子容器配置

<context:component-scan base-package="com.example">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
    <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>

整合驗證

Controller 中 @Autowired 注入 Service,Service 標註 @Component。父容器創建 Service,子容器創建 Controller,子容器可引用父容器 Bean,實現依賴注入。

總結:Spring MVC 運行流程圖解

  1. 請求到達 DispatcherServlet
  2. HandlerMapping 尋找匹配的 Handler (Controller 方法)
  3. HandlerAdapter 適配調用 Handler
  4. 參數解析、數據綁定、類型轉換、驗證
  5. 執行業務邏輯,返回 ModelAndView/視圖名/對象
  6. ViewResolver 解析視圖
  7. 視圖渲染模型數據生成響應
  8. 攔截器 afterCompletion 收尾

掌握以上棧,你將具備構建健壯、可維護、高性能 Spring MVC 應用的全棧能力。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved