速通Spring MVC ,一篇就够!

速通Spring MVC ,一篇就够!

文章目录

  • 速通Spring MVC ,一篇就够!
    • 模块一:基础准备与环境搭建
      • 1.1 概念阐述
      • 1.2 Spring 核心入门:IoC 与 DI
        • 关键注解详解 (Spring Core 部分)
      • 1.3 Spring MVC 项目骨架搭建
        • 使用 Maven 创建 Web 项目并添加依赖
        • 使用 Java 配置方式 (Servlet 3.0+ 标准方式)
      • 1.4 最佳实践与注意事项 (模块一)
    • 模块二:Spring MVC 核心原理与工作流程
      • 2.1 概念阐述
      • 2.2 核心组件与工作流程解析
        • Spring MVC 工作流程详解 (从请求到响应)
      • 2.3 最佳实践与注意事项 (模块二)
    • 模块三:控制器 (Controller) 基础与请求处理
      • 3.1 概念阐述
      • 3.2 关键注解详解 (Controller 部分)
      • 3.3 代码示例 (模块三)
        • 1. Controller 层 (UserController.java)
        • 2. Service 层模拟 (UserService.java, UserServiceImpl.java)
        • 3. Model 模拟 (User.java)
        • 4. Spring 配置 (XML 或 JavaConfig)
        • 5. 视图技术 (JSP 代码片段)
      • 3.4 最佳实践与注意事项 (模块三)
    • 模块四:数据绑定与视图技术
      • 4.1 概念阐述
      • 4.2 核心组件与工作流程 (数据绑定与视图)
        • 数据绑定流程简化
        • 视图解析与渲染流程简化
      • 4.3 代码示例 (模块四)
        • @ModelAttribute 用于方法参数的详细说明
        • 视图解析器配置回顾
        • Thymeleaf 视图示例 (替代 JSP)
      • 4.4 最佳实践与注意事项 (模块四)
    • 模块五:RESTful 服务与数据交互
      • 5.1 概念阐述
      • 5.2 关键注解详解 (RESTful 部分)
      • 5.3 核心组件与工作流程 (数据交互)
        • RESTful 请求处理流程简化
      • 5.4 代码示例 (模块五)
        • 1. Controller 层 (UserRestController.java)
        • 2. Service 层模拟
        • 3. Model 模拟
        • 4. Spring 配置
        • 5. 依赖 (Maven pom.xml)
      • 5.5 最佳实践与注意事项 (模块五)
    • 模块六:进阶概念与实践应用
      • 6.1 概念阐述
      • 6.2 关键注解详解 (进阶部分)
      • 6.3 核心组件与工作流程 (进阶)
      • 6.4 代码示例 (模块六)
        • 1. 异常处理 (AppExceptionHandler.java)
        • 2. 重定向与转发
        • 3. 文件上传 (使用 StandardServletMultipartResolver)
        • 4. Spring MVC 拦截器 (LoginInterceptor.java)
        • 5. 整合数据访问层
        • 6. 了解 Spring Boot
      • 6.5 最佳实践与注意事项 (模块六)
      • 6.5 最佳实践与注意事项 (模块六)
    • 快速项目上手实践
    • 企业级实用技术讲解

模块一:基础准备与环境搭建

本模块回顾 Spring MVC 所需的基础知识,介绍核心概念 IoC/DI,并指导如何搭建基础的 Spring MVC 开发环境。

1.1 概念阐述

• Java 语言基础回顾: 学习 Spring MVC 至少需要掌握 Java 的基本语法、面向对象编程思想(类、对象、继承、多态、封装)、集合框架(List, Set, Map)以及对多线程有初步了解。Web 开发中常涉及并发处理。
• Web 基础回顾:
• HTTP 协议: 理解请求 (Request) 和响应 (Response) 的基本结构、常见的 HTTP 方法 (GET, POST, PUT, DELETE 等)、状态码 (200, 404, 500 等)。
• Servlet 原理基础: 理解 Servlet 在 Java Web 应用中的作用,它是处理客户端请求、生成服务器响应的核心组件。Servlet 生命周期 (init, service, destroy) 以及 web.xml 的基本配置(如 < servlet>, < servlet-mapping>)。Spring MVC 的核心 DispatcherServlet 就是一个特殊的 Servlet。
• MVC 设计模式原理: MVC (Model-View-Controller) 是一种软件架构模式,将应用程序分为三个相互关联的部分:
• Model (模型): 负责处理业务逻辑和数据。可以是简单的数据对象(POJO),也可以是负责数据访问和业务处理的服务层。
• View (视图): 负责呈现用户界面。显示模型中的数据,并将用户输入(如表单提交)发送给控制器。在 Web 应用中,视图通常是 HTML 页面,可以使用 JSP, Thymeleaf, FreeMarker 等模板技术。
• Controller (控制器): 作为模型和视图之间的协调者。接收用户输入,调用模型进行业务处理,选择合适的视图来显示结果。控制器不处理业务逻辑,只负责请求的转发和调度。

+-------------+      +----------------+      +-----------+
|    User     | ---->|   Controller   |<---->|   Model   |
+-------------+      +----------------+      +-----------+
       ^                     |                     ^
       |                     |                     |
       |                     v                     |
       |            +---------------+              |
       +------------|     View      |<--------------+
                    +---------------+

说明:用户操作视图触发请求 -> Controller 接收请求并调用 Model -> Model 处理数据/业务逻辑 -> Controller 选择 View 并传递 Model 数据 -> View 渲染数据并呈现给用户。

1.2 Spring 核心入门:IoC 与 DI

Spring 框架的核心是 IoC 容器,它负责管理应用程序中对象的生命周期和依赖关系。
• IoC (Inversion of Control - 控制反转): 指的是对象创建和依赖关系的管理不是由对象本身负责,而是交给容器来完成。传统的开发中,一个对象如果依赖另一个对象,需要自己去创建或查找被依赖的对象。IoC 将这个控制权反转给了容器。
• DI (Dependency Injection - 依赖注入): 是实现 IoC 的一种方式。容器在创建对象时,将其依赖的其他对象注入进来。这样,对象之间解耦,不再需要硬编码地查找或创建依赖。
• Spring 容器 (ApplicationContext): Spring 提供了多种容器实现,ApplicationContext 是常用的高级接口,提供了更丰富的功能(如国际化、事件发布等)。容器负责实例化、配置和组装 Bean。
• Bean 的定义与配置: Bean 是 Spring 容器管理的对象。Bean 的定义描述了如何创建和配置一个对象。可以通过 XML 或 JavaConfig 方式配置 Bean。
• 依赖注入的实现方式: Spring 支持多种注入方式,最常用的是:
• 构造器注入: 通过 Bean 的构造器参数注入依赖。
• Setter 方法注入: 通过 Bean 的 Setter 方法注入依赖。
• 字段注入: 直接在字段上使用注解注入(不推荐,影响可测试性)。

关键注解详解 (Spring Core 部分)

• @Component: 标记一个类为 Spring 组件。Spring 容器会自动扫描并将其注册为 Bean。它是 @Controller, @Service, @Repository 的通用原型注解。
• @Service: 标记一个类为业务逻辑层组件。是 @Component 的一个特化,更具语义性,通常用于 Service 层。
• @Repository: 标记一个类为数据访问层组件。是 @Component 的一个特化,更具语义性,通常用于 DAO/Repository 层。Spring 为 Repository 提供了一些额外特性(如异常转换)。
• @Autowired: 最常用的依赖注入注解。 可以用在构造器、Setter 方法、字段上。Spring 会根据类型在容器中查找匹配的 Bean 进行注入。
• @Autowired 默认是按类型匹配。如果同类型有多个 Bean,会进一步按名称匹配(变量名)。
• 可以使用 @Autowired(required = false) 使依赖成为可选。
• @Qualifier: 当使用 @Autowired 按类型注入时,如果容器中有多个相同类型的 Bean,可以使用 @Qualifier 指定按名称注入哪个 Bean。
• 示例:@Autowired @Qualifier(“mySpecificBean”) private MyInterface myDependency;
• @Resource: (JSR-250 标准注解,Spring 也支持) 默认按名称注入。如果指定了 name 属性,则按名称注入;如果没有指定 name,则先按名称(字段名/Setter名)查找,找不到再按类型查找。

1.3 Spring MVC 项目骨架搭建

使用构建工具 (Maven 或 Gradle) 创建一个 Web 项目,并添加 Spring MVC 核心依赖。

使用 Maven 创建 Web 项目并添加依赖

在 IDE 中创建 Maven Project,选择 maven-archetype-webapp 或类似的 Web 应用骨架。
编辑 pom.xml 文件,添加 Spring MVC 及相关依赖:

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-webmvcartifactId>
    <version>5.3.20version> 
dependency>
<dependency>
    <groupId>javax.servletgroupId>
    <artifactId>javax.servlet-apiartifactId>
    <version>4.0.1version> 
    <scope>providedscope>
dependency>
<dependency>
    <groupId>javax.servlet.jspgroupId>
    <artifactId>javax.servlet.jsp-apiartifactId>
    <version>2.3.3version> 
    <scope>providedscope>
dependency>

<dependency>
    <groupId>javax.servletgroupId>
    <artifactId>jstlartifactId>
    <version>1.2version>
dependency>

配置 web.xml 初始化 DispatcherServlet (Servlet 3.0 之前的标准方式):

DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Applicationdisplay-name>

  
  <servlet>
    <servlet-name>dispatcherservlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
    
    <init-param>
      <param-name>contextConfigLocationparam-name>
      <param-value>/WEB-INF/spring-mvc.xmlparam-value>
    init-param>
    
    <load-on-startup>1load-on-startup>
  servlet>

  
  <servlet-mapping>
    <servlet-name>dispatcherservlet-name>
    <url-pattern>/url-pattern>
  servlet-mapping>
web-app>
说明:/ 表示 DispatcherServlet 会处理所有请求(除了 .jsp 默认由容器处理)。

在 /WEB-INF/ 目录下创建 spring-mvc.xml Spring MVC 配置文件(基于 XML 配置方式):


<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">

    
    <context:component-scan base-package="com.yourpackage.controller"/> 
    <context:component-scan base-package="com.yourpackage.service"/>   
    <context:component-scan base-package="com.yourpackage.repository"/> 

    
    <mvc:annotation-driven/>

    
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"/> 
        <property name="suffix" value=".jsp"/>            
    bean>

    
    <mvc:resources mapping="/resources/**" location="/resources/"/>

beans>
说明:你需要创建 src/main/webapp/WEB-INF/views/ 和 src/main/webapp/resources/ 目录。
使用 Java 配置方式 (Servlet 3.0+ 标准方式)

Servlet 3.0+ 规范允许通过代码配置 Servlet 容器,Spring MVC 提供了抽象基类简化配置。
不再需要 web.xml 文件(或者 web.xml 仅用于一些兼容性配置)。
创建一个配置类,继承 AbstractAnnotationConfigDispatcherServletInitializer:

package com.yourpackage.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        // 配置 Service/Repository 等 Bean 的 Spring Root Context
        return new Class<?>[]{RootConfig.class}; // 可以是 null 如果没有 Root Context
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        // 配置 Controller, ViewResolver 等 Spring MVC Bean 的 Servlet Context
        return new Class<?>[]{WebConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        // 将所有请求映射到 DispatcherServlet
        return new String[]{"/"};
    }

    // 可选:配置 DispatcherServlet 的其他属性,如文件上传等
    // @Override
    // protected void customizeRegistration(ServletRegistration.Dynamic registration) {
    //    registration.setInitParameter("contextConfigLocation", "/WEB-INF/spring-mvc.xml"); // 如果仍然需要加载 XML
    // }
}

创建 WebConfig 类,作为 Spring MVC 的配置源:

package com.yourpackage.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration // 标记这是一个配置类
@EnableWebMvc  // 启用 Spring MVC 的注解驱动功能
@ComponentScan(basePackages = {"com.yourpackage.controller", "com.yourpackage.service", "com.yourpackage.repository"}) // 扫描组件
public class WebConfig implements WebMvcConfigurer { // 推荐实现 WebMvcConfigurer 进行更多配置

    @Bean // 将方法的返回值注册为 Spring Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        return resolver;
    }

    // 配置静态资源处理
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
    }

    // 其他配置... 如拦截器、格式化器等
}

(可选)创建 RootConfig 类,用于配置非 Web 层的 Bean:

package com.yourpackage.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@Configuration
@ComponentScan(basePackages = {"com.yourpackage"}, // 扫描整个应用包
               excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = EnableWebMvc.class)}) // 排除掉 WebConfig,避免重复扫描 Controller 等
public class RootConfig {
    // 配置数据源、事务管理器、Service、Repository 等非 Web 组件
}
说明:Root Context 通常包含业务逻辑和数据访问层的 Bean,而 Servlet Context 包含 Web 层的 Bean (如 Controller, ViewResolver)。DispatcherServlet 创建自己的 Servlet Context,它持有对 Root Context 的引用,可以访问 Root Context 中的 Bean。这样可以将 Web 层与业务层/数据层解耦。

1.4 最佳实践与注意事项 (模块一)

• 构建工具: 强烈推荐使用 Maven 或 Gradle 管理项目依赖,它们可以简化版本管理和构建流程。
• 依赖管理: 明确项目中需要的依赖,避免引入不必要的库,关注依赖的版本兼容性。
• 配置文件分离: 将 Spring MVC 的配置(如 ViewResolver, HandlerAdapter 等)与业务逻辑/数据访问层的配置分开管理(尤其是在使用 JavaConfig 时通过 Root Context 和 Servlet Context)。
• 组件扫描范围: 明确 context:component-scan 或 @ComponentScan 的 base-package 范围,只扫描需要 Spring 管理的包,提高启动速度和减少潜在冲突。
• Servlet 版本: 了解你使用的 Servlet 容器(如 Tomcat, Jetty)支持的 Servlet API 版本,选择匹配的 javax.servlet-api 依赖。推荐使用 Servlet 3.0+ 以支持 Java Config。

模块二:Spring MVC 核心原理与工作流程

深入理解 Spring MVC 的核心组件及其协作方式是掌握框架的关键。本模块将详细解析 DispatcherServlet 和其他核心组件,并阐述请求处理的完整流程。

2.1 概念阐述

• 前端控制器模式: DispatcherServlet 是 Spring MVC 中的核心,扮演着前端控制器 (Front Controller) 的角色。所有进入应用的 Web 请求首先都会被它接收。它负责将请求分发给具体的处理器 (Controller),并协调整个请求处理过程。这种模式的好处是提供了一个统一的入口,便于集中管理和配置。
• Spring MVC 核心组件: Spring MVC 的请求处理流程是由一系列可插拔的核心组件协作完成的。理解这些组件的功能和交互是理解框架工作原理的关键。

2.2 核心组件与工作流程解析

DispatcherServlet 的工作流程是一个请求从接收到响应的完整生命周期。以下是主要的组件及其在流程中的作用:
DispatcherServlet (前端控制器):
作用: 统一接收所有请求,根据请求信息(如 URL、请求方法)查找合适的处理器(Controller)。协调其他组件完成请求处理,并将结果发送给客户端。
配置: 在 web.xml 中配置 和 ,或使用 Java Config (AbstractAnnotationConfigDispatcherServletInitializer) 进行配置,指定其监听的 URL 模式。
初始化: 容器启动时,DispatcherServlet 会被初始化,它会创建或加载 Spring 容器 (ApplicationContext),并在容器中查找其需要的各种组件 Bean (如 HandlerMapping, HandlerAdapter, ViewResolver 等)。
HandlerMapping (处理器映射器):
作用: 根据请求信息(URL、Header、参数等),查找能够处理该请求的处理器 (Handler,通常是 Controller 中的某个方法)。
Spring MVC 内置实现: RequestMappingHandlerMapping 是处理 @RequestMapping 注解的主要实现。
配置: 通常不需要手动配置 RequestMappingHandlerMapping,因为它在 mvc:annotation-driven/ 或 @EnableWebMvc 生效时会自动注册。
HandlerAdapter (处理器适配器):
作用: DispatcherServlet 找到 Handler 后,并不能直接调用它。HandlerAdapter 的作用是根据找到的 Handler 类型,以统一的方式调用 Handler 的方法。因为 Handler 可以是多种类型(如实现了特定接口、使用了特定注解等),HandlerAdapter 提供了适配能力。
Spring MVC 内置实现: RequestMappingHandlerAdapter 是处理带有 @RequestMapping 注解的 Controller 方法的主要实现。它负责解析方法参数、执行方法、处理返回值等。
配置: 通常不需要手动配置 RequestMappingHandlerAdapter,它在 mvc:annotation-driven/ 或 @EnableWebMvc 生效时会自动注册。
Controller (处理器/控制器):
作用: 包含具体的业务处理逻辑。接收 HandlerAdapter 传递的请求参数,调用 Service 层等进行业务处理,然后返回处理结果(通常是 ModelAndView、字符串视图名、或者直接写入响应体)。
配置: 使用 @Controller 或 @RestController 标记,并通过 @ComponentScan 扫描注册为 Spring Bean。
ModelAndView (模型与视图):
作用: 封装了 Controller 处理完请求后的结果。包含两部分:Model (数据) 和 View Name (逻辑视图名)。
Model: 是一个 Map 结构,存储了需要传递给视图的数据。
View Name: 是一个字符串,逻辑上代表一个视图(如 “userList”, “addUserForm”)。DispatcherServlet 会将其交给 ViewResolver 进行解析。
ViewResolver (视图解析器):
作用: 根据 Controller 返回的逻辑视图名,解析出实际的视图对象 (View)。视图对象可以是 JSP、HTML 文件路径,或者其他视图技术(如 Thymeleaf、FreeMarker)的 View 实现。
Spring MVC 内置实现: InternalResourceViewResolver (用于解析 JSP 或静态 HTML)、ThymeleafViewResolver 等。
配置: 需要在 Spring MVC 配置文件(XML 或 Java Config)中明确配置,指定视图文件存放的位置(前缀)和文件类型(后缀)。
View (视图):
作用: 是一个接口,表示具体的视图实现(如 JSP 页面)。它负责接收 Model 中的数据,并根据视图模板渲染最终的响应内容(如 HTML)。
其他辅助组件:
MultipartResolver:处理文件上传请求。
LocaleResolver:用于国际化,解析用户的区域设置。
ThemeResolver:用于主题切换。

Spring MVC 工作流程详解 (从请求到响应)

以下是 DispatcherServlet 处理请求的详细步骤:

+----------------+     +-----------------+     +-----------------+     +-------------+     +--------------+
| User Request   | --> | DispatcherServlet | --> | HandlerMapping  | --> | Controller  | --> | ModelAndView |
+----------------+     +-----------------+     +-----------------+     +-------------+     +--------------+
                               |                       ^       ^             |       ^             |
                               |                       |       |             |       |             |
                               |请求找到Controller       |找到HandlerAdapter   | 执行业务逻辑    |返回ModelAndView
                               |                       |       |             |       |             |
                               v                       |       |             v       |             v
                         +-----------------+     +-----------------+     +-------------+     +-------------+
                         | HandlerAdapter  | --> | DispatcherServlet | --> | ViewResolver| --> |    View     |
                         +-----------------+     +-----------------+     +-------------+     +-------------+
                               ^                             |                     |                 |
                               |调用Controller方法             |解析View Name         |找到View对象       |渲染视图
                               |                             |                     |                 |
                               +-----------------------------+---------------------+-----------------+
                                                           |
                                                           v
                                                     +------------+
                                                     |  Response  |
                                                     +------------+

用户发起请求: 用户在浏览器中输入 URL 或提交表单,请求被发送到 Web 服务器(如 Tomcat)。
服务器接收请求: Web 服务器接收到请求,根据 web.xml 或 Java Config 中的配置,将匹配的请求转发给 DispatcherServlet。
DispatcherServlet 接收请求: DispatcherServlet 作为前端控制器接收到请求。
查找 Handler: DispatcherServlet 请求 HandlerMapping,根据请求信息(如 URL)查找能够处理该请求的处理器 (Handler)。
HandlerMapping 返回 Handler: HandlerMapping 找到匹配的 Handler(例如,带有 @RequestMapping 注解的 Controller 方法),将其返回给 DispatcherServlet。
查找 HandlerAdapter: DispatcherServlet 根据返回的 Handler,查找合适的 HandlerAdapter,因为不同的 Handler 可能需要不同的调用方式。
HandlerAdapter 调用 Handler: HandlerAdapter 调用 Handler(即执行 Controller 方法)。在调用 Controller 方法之前,HandlerAdapter 还会处理方法参数的解析、数据绑定等。
Controller 执行业务逻辑: Controller 方法执行应用程序的业务逻辑,可能调用 Service 层或数据访问层。
Controller 返回 ModelAndView/其他: Controller 方法执行完毕后,通常返回 ModelAndView 对象、逻辑视图名 (String)、或者直接将数据写入响应体 (@ResponseBody)。
处理返回值: DispatcherServlet 根据 Controller 的返回值类型进行处理。
如果返回 ModelAndView 或逻辑视图名,DispatcherServlet 将请求 ViewResolver 进行视图解析。
如果使用了 @ResponseBody,DispatcherServlet 将返回值通过 HttpMessageConverter 直接写入响应体。
ViewResolver 解析视图: 如果需要视图渲染,DispatcherServlet 请求 ViewResolver,根据逻辑视图名解析得到实际的 View 对象。
View 渲染: DispatcherServlet 请求 View 对象进行渲染。View 负责使用 Model 中的数据,结合视图模板生成最终的响应内容(如 HTML)。
DispatcherServlet 完成响应: DispatcherServlet 将渲染生成的响应返回给 Web 服务器,最终由服务器发送给客户端浏览器。

2.3 最佳实践与注意事项 (模块二)

• 理解流程是关键: 多次回顾并理解 DispatcherServlet 的工作流程图,这是掌握 Spring MVC 的核心。
• 配置 DispatcherServlet: 在 Servlet 3.0+ 环境下,推荐使用 Java Config (AbstractAnnotationConfigDispatcherServletInitializer) 代替 web.xml 配置 DispatcherServlet,更加灵活和类型安全。
• 组件自动配置: 启用 mvc:annotation-driven/ 或 @EnableWebMvc 会自动注册许多核心组件(如 RequestMappingHandlerMapping, RequestMappingHandlerAdapter, ContentNegotiatingViewResolver, HttpMessageConverter 等),简化配置。
• 自定义组件: 如果需要自定义特定的组件(如自定义 ViewResolver),可以通过在 Spring MVC 配置类或 XML 文件中定义相应的 Bean 来覆盖默认配置。
• 日志查看: 在开发过程中,开启 Spring 的日志(如 DEBUG 级别),可以观察到 DispatcherServlet 如何接收请求、查找 Handler、解析视图等过程,有助于理解内部工作原理。

模块三:控制器 (Controller) 基础与请求处理

本模块学习如何编写 Spring MVC 控制器,并掌握接收和处理用户请求的各种方式。

3.1 概念阐述

• Controller (控制器): 在 Spring MVC 中,任何被 @Controller 或 @RestController 注解标记的类都可以作为控制器。控制器负责接收 HTTP 请求,调用业务逻辑处理,并决定返回什么响应(视图或数据)。
• 请求映射: 需要将特定的 HTTP 请求 URL 映射到 Controller 类的某个方法上,这个过程通过 @RequestMapping 系列注解来实现。

3.2 关键注解详解 (Controller 部分)

• @Controller: 标记一个类是 Spring MVC 控制器。Spring 会扫描并注册此类为 Bean,并能够处理 Web 请求。通常配合 @RequestMapping 使用。
• @RequestMapping: 核心请求映射注解。
• 可以应用在类上:指定基础 URL 路径,该类中所有方法的映射路径都会基于这个基础路径。
• 可以应用在方法上:指定方法处理的具体 URL 路径。
• 常用参数:
• value 或 path:指定请求的 URL 路径(支持 Ant 风格路径匹配和路径变量)。
• 示例:/users, /users/{id}, /files/*.txt
• method: 指定支持的 HTTP 请求方法 (RequestMethod 枚举,如 RequestMethod.GET, RequestMethod.POST)。
• 示例:method = RequestMethod.GET
• params: 指定请求必须包含或不包含某个参数。
• 示例:params = “action=save”, params = {“!id”, “type=user”}
• headers: 指定请求必须包含或不包含某个特定的请求头。
• 示例:headers = “Accept=application/json”, headers = {“!Content-Type”, “X-MyHeader=someValue”}
• consumes: 指定请求的 Content-Type 头,即客户端发送的数据类型(用于 @RequestBody)。
• 示例:consumes = “application/json”, consumes = {“!text/plain”, “application/xml”}
• produces: 指定响应的 Content-Type 头,即服务器返回的数据类型(用于 @ResponseBody)。
• 示例:produces = “application/json”, produces = “text/plain;charset=UTF-8”
• HTTP 方法快捷注解 (Spring 4.3+ 推荐): 这些注解是 @RequestMapping 的变体,语义更清晰。
• @GetMapping: 相当于 @RequestMapping(method = RequestMethod.GET)
• @PostMapping: 相当于 @RequestMapping(method = RequestMethod.POST)
• @PutMapping: 相当于 @RequestMapping(method = RequestMethod.PUT)
• @DeleteMapping: 相当于 @RequestMapping(method = RequestMethod.DELETE)
• @PatchMapping: 相当于 @RequestMapping(method = RequestMethod.PATCH)
• @RequestParam: 用于获取请求参数 (Query Parameter 或 Form Parameter)。
• 可以绑定请求 URL 中的查询参数(如 ?id=123)或 POST 请求体中的表单参数到 Controller 方法参数。
• 常用参数:
• value 或 name: 指定请求参数的名称。如果方法参数名与请求参数名一致,可以省略。
• required: boolean 类型,默认为 true。如果设置为 false,表示该参数不是必需的,请求中可以不包含此参数。
• defaultValue: 当请求中不包含该参数时,提供一个默认值。
• @PathVariable: 用于获取 URI 模板变量 (路径参数)。
• 当 @RequestMapping 路径中包含 {variable} 这样的占位符时,使用 @PathVariable 将占位符的值绑定到方法参数。
• 示例:@GetMapping(“/users/{userId}”) public String getUser(@PathVariable(“userId”) Long userId)
• 常用参数:
• value: 指定要绑定的 URI 模板变量名。如果方法参数名与模板变量名一致,可以省略。
• required: boolean 类型,默认为 true。路径变量通常是必需的。
• @RequestHeader: 用于获取请求头 (Request Header) 的值。
• 示例:@GetMapping(“/”) public String handleRequest(@RequestHeader(“User-Agent”) String userAgent)
• @CookieValue: 用于获取 Cookie 的值。
• 示例:@GetMapping(“/”) public String handleRequest(@CookieValue(“JSESSIONID”) String sessionId)
• @ModelAttribute:
• 用在方法参数上:将请求参数绑定到一个对象(通常是 JavaBean),常用于处理表单提交。Spring MVC 会自动创建该对象实例,然后填充请求参数中与对象属性同名的值。
• 示例:@PostMapping(“/users”) public String addUser(@ModelAttribute(“user”) User user)
• 用在方法上:标记一个方法,该方法的返回值会作为 Model 属性添加到所有由该 Controller 处理的请求的 Model 中。常用于为表单提供预填充数据或提供所有视图共享的数据。
• 示例:@ModelAttribute(“user”) public User setupForm()

3.3 代码示例 (模块三)

以下示例展示一个简单的用户控制器,处理用户列表显示、详情查看和用户创建表单。

1. Controller 层 (UserController.java)
package com.yourpackage.controller;

import com.yourpackage.model.User; // 假设有一个 User 类
import com.yourpackage.service.UserService; // 假设有一个 UserService 接口/类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; // 用于传递数据到视图
import org.springframework.web.bind.annotation.*; // 包含大部分常用注解
import org.springframework.web.servlet.ModelAndView; // 另一种传递数据和视图的方式

import java.util.List;

@Controller // 标记为控制器
@RequestMapping("/users") // 类级别的基础路径
public class UserController {

    private final UserService userService; // 依赖注入 UserService

    @Autowired // 构造器注入推荐方式
    public UserController(UserService userService) {
        this.userService = userService;
    }

    // 处理 GET 请求 /users,显示用户列表
    // 返回字符串表示逻辑视图名
    @GetMapping // 相当于 @RequestMapping(method = RequestMethod.GET)
    public String listUsers(Model model) {
        List<User> userList = userService.getAllUsers(); // 调用 Service 获取数据
        model.addAttribute("users", userList); // 将数据添加到 Model
        return "userList"; // 返回视图名,ViewResolver 会解析到 /WEB-INF/views/userList.jsp (如果配置的是 InternalResourceViewResolver)
    }

    // 处理 GET 请求 /users/{userId},显示用户详情
    // 使用 @PathVariable 获取路径变量
    // 使用 ModelAndView 返回数据和视图
    @GetMapping("/{userId}")
    public ModelAndView showUserDetail(@PathVariable("userId") Long userId) {
        User user = userService.getUserById(userId); // 调用 Service 获取用户
        ModelAndView mav = new ModelAndView("userDetail"); // 创建 ModelAndView,指定视图名
        mav.addObject("user", user); // 添加模型数据
        return mav;
    }

    // 处理 GET 请求 /users/new,显示创建用户表单
    // @ModelAttribute 用在方法上,为表单提供一个空的 User 对象
    @GetMapping("/new")
    @ModelAttribute("user") // 这个方法返回的对象会以名称 "user" 添加到 Model 中
    public User setupUserForm() {
        return new User(); // 返回一个空的 User 对象供表单绑定
    }

    // 处理 POST 请求 /users,提交创建用户表单
    // @ModelAttribute 用在方法参数上,绑定表单数据到 User 对象
    // @RequestParam 用于获取非对象属性的简单参数,如这里的 redirect 参数
    @PostMapping
    public String addUser(@ModelAttribute("user") User user,
                          @RequestParam(value = "redirect", required = false, defaultValue = "false") boolean redirectToList,
                          Model model) {
        // 简单的表单验证(实际应用中更复杂)
        if (user.getUsername() == null || user.getUsername().isEmpty()) {
            model.addAttribute("error", "Username cannot be empty");
            return "userForm"; // 返回表单视图,显示错误
        }

        userService.saveUser(user); // 调用 Service 保存用户

        if (redirectToList) {
             // 重定向到用户列表页面
             return "redirect:/users";
        } else {
            // 转发到用户详情页面(假设根据保存的用户ID重定向)
            // 转发和重定向将在模块六详细讲解
            // return "forward:/users/" + user.getId(); // 示例,假设 User 有 getId() 方法
            // 这里简化为返回成功页
            return "userSuccess"; // 返回成功视图
        }
    }

    // 示例:获取请求参数和请求头
    @GetMapping("/example")
    public String exampleParams(@RequestParam(value = "name", required = false) String name,
                                @RequestHeader("Accept-Language") String acceptLanguage,
                                Model model) {
        model.addAttribute("name", name);
        model.addAttribute("language", acceptLanguage);
        return "exampleView";
    }
}
2. Service 层模拟 (UserService.java, UserServiceImpl.java)
package com.yourpackage.service;

import com.yourpackage.model.User;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;
// 假设这里会引入 Repository/DAO 层

@Service // 标记为 Service 层组件
public class UserServiceImpl implements UserService {

    // 模拟数据,实际应从数据库获取
    private List<User> dummyUsers = Arrays.asList(
            new User(1L, "Alice"),
            new User(2L, "Bob")
    );

    @Override
    public List<User> getAllUsers() {
        System.out.println("UserService: Getting all users");
        return dummyUsers; // 模拟返回数据
    }

    @Override
    public User getUserById(Long id) {
         System.out.println("UserService: Getting user with ID: " + id);
        // 模拟查找
        return dummyUsers.stream()
                         .filter(user -> user.getId().equals(id))
                         .findFirst()
                         .orElse(null);
    }

    @Override
    public void saveUser(User user) {
         System.out.println("UserService: Saving user: " + user.getUsername());
        // 模拟保存逻辑
        // 在真实应用中,这里会调用 Repository/DAO 层的 save 方法
        // dummyUsers.add(user); // 简单添加到模拟列表 (注意:List 的 add 不会修改原始 Arrays.asList 的列表)
        // 在实际应用中,保存后可能需要更新 dummyUsers 或重新加载数据
    }
}
package com.yourpackage.service;

import com.yourpackage.model.User;

import java.util.List;

public interface UserService {
    List<User> getAllUsers();
    User getUserById(Long id);
    void saveUser(User user);
}
3. Model 模拟 (User.java)
package com.yourpackage.model;

// 简单的 POJO 作为模型
public class User {
    private Long id;
    private String username;

    // 需要一个无参构造器供 Spring MVC 数据绑定使用
    public User() {
    }

    public User(Long id, String username) {
        this.id = id;
        this.username = username;
    }

    // Getters and Setters
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
}
4. Spring 配置 (XML 或 JavaConfig)

参考模块一的 Spring MVC 配置,确保 @ComponentScan 扫描到 com.yourpackage.controller 和 com.yourpackage.service 包。

5. 视图技术 (JSP 代码片段)

• /WEB-INF/views/userList.jsp: 显示用户列表

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>



    
    User List


    

User List

<%-- 遍历 Model 中的 "users" 数据 --%>
ID Username Actions
${user.id} ${user.username} View Detail

Add New User

• /WEB-INF/views/userDetail.jsp: 显示用户详情

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>



    
    User Detail


    

User Detail

ID: ${user.id}

<%-- 显示 Model 中的 "user" 对象的属性 --%>

Username: ${user.username}

Back to List

• /WEB-INF/views/userForm.jsp: 显示创建用户表单

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> <%-- Spring 表单标签库 --%>



    
    Add New User


    

Add New User

${error}

<%-- form:form 标签会自动绑定到 Model 中名为 "user" 的对象 --%>
<%-- form:input 绑定到 user 对象的 username 属性 --%>
<%-- 示例:一个额外的非绑定字段,用于演示 @RequestParam --%>

Cancel

注意:使用 Spring 的  标签需要添加 spring-webmvc 依赖,并确保 JSP 页面头部引入了对应的标签库。

• /WEB-INF/views/userSuccess.jsp: 显示成功页面

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>



    
    Success


    

Operation Successful!

Back to User List

3.4 最佳实践与注意事项 (模块三)

• 区分 @Controller 和 @RestController: 如果控制器主要返回视图(如 JSP, Thymeleaf 模板),使用 @Controller。如果主要用于构建 RESTful API 返回 JSON/XML 等数据,使用 @RestController(详见模块五)。
• 使用快捷注解: 优先使用 @GetMapping, @PostMapping 等注解代替 @RequestMapping(method = …),代码更简洁易读。
• 类级别 @RequestMapping: 在 Controller 类上定义基础路径,有助于组织 URL 结构,避免重复。
• ** @RequestParam 的 required 和 defaultValue:** 合理使用这两个参数可以使控制器方法更健壮,处理可选参数和提供默认值。
• ** @PathVariable:** 用于从 URL 路径本身获取参数,适合表示资源的 ID 等。
• ** @ModelAttribute 处理表单:** @ModelAttribute 是处理复杂表单绑定的强大工具,它可以将整个表单数据绑定到一个 JavaBean 对象,简化参数接收。
• Controller 方法参数类型: Controller 方法支持多种参数类型,除了 @RequestParam, @PathVariable, @ModelAttribute 绑定的参数外,还可以直接注入 HttpServletRequest, HttpServletResponse, HttpSession, Model, RedirectAttributes 等对象。

模块四:数据绑定与视图技术

本模块深入学习 Spring MVC 如何将请求数据绑定到 Java 对象,以及如何使用视图技术将 Model 中的数据呈现给用户。

4.1 概念阐述

• 数据绑定 (Data Binding): Spring MVC 核心功能之一,它能够自动将 HTTP 请求中的参数值转换为 Controller 方法参数或 JavaBean 对象的属性值。这大大简化了从请求中获取数据的过程。
• 表单处理: Web 应用中常见的交互方式是用户填写表单并提交。Spring MVC 提供方便的方式接收、绑定和校验表单数据。
• 视图解析与渲染: Controller 处理完请求后,需要将结果呈现给用户。ViewResolver 负责根据 Controller 返回的逻辑视图名找到具体的视图,而 View 负责结合 Model 中的数据和视图模板生成最终的响应内容。

4.2 核心组件与工作流程 (数据绑定与视图)

• HandlerAdapter 中的数据绑定: 数据绑定主要发生在 HandlerAdapter 调用 Controller 方法之前。RequestMappingHandlerAdapter 使用 WebDataBinder 来完成数据绑定。
• WebDataBinder: 负责将请求参数绑定到目标对象(如方法参数或 @ModelAttribute 对象)。它能够进行类型转换(字符串转数字、日期等),并支持注册自定义的 PropertyEditor 或 Converter 来处理复杂的类型转换。
• ModelAndView: 在模块二已介绍,用于携带模型数据和逻辑视图名从 Controller 返回到 DispatcherServlet。
• ViewResolver: 在模块二已介绍,根据逻辑视图名解析出 View 对象。
• View: 在模块二已介绍,负责使用 Model 数据渲染视图。常见的视图技术有:
• JSP (JavaServer Pages): 传统的 Java Web 视图技术,通过 JSP 标签、EL 表达式和 JSTL 来访问 Model 数据并生成 HTML。
• Thymeleaf: 一种现代的服务器端 Java 模板引擎,语法自然,可以直接在浏览器中打开模板文件进行静态原型设计。与 Spring 深度集成。
• FreeMarker: 另一种流行的模板引擎。
• 其他: Velocity, Groovy Templates, JSON View 等。

数据绑定流程简化

请求到达 Controller 方法。
HandlerAdapter (具体如 RequestMappingHandlerAdapter) 准备调用该方法。
HandlerAdapter 识别方法参数(如 @RequestParam, @PathVariable, @ModelAttribute)。
对于需要绑定的参数(如 @ModelAttribute 修饰的 User 对象):
HandlerAdapter 创建或获取 WebDataBinder。
WebDataBinder 获取请求中的相关参数。
WebDataBinder 将参数值绑定到目标对象的相应属性上,进行类型转换和格式化。
绑定成功后,将填充好数据的对象作为参数传递给 Controller 方法。
对于 @RequestParam 或 @PathVariable 等简单类型参数:
HandlerAdapter 直接将请求参数或路径变量的值进行类型转换后作为方法参数。

视图解析与渲染流程简化

Controller 方法执行完毕,返回 ModelAndView 或逻辑视图名。
DispatcherServlet 接收到返回值。
DispatcherServlet 找到配置好的 ViewResolver。
DispatcherServlet 调用 ViewResolver 的 resolveViewName() 方法,传入逻辑视图名和 Locale 等信息。
ViewResolver 根据其内部规则(如前后缀配置)解析逻辑视图名,找到对应的实际视图资源(如 /WEB-INF/views/userList.jsp),并返回一个 View 对象。
DispatcherServlet 调用 View 对象的 render() 方法,传入 Model 数据和 Request/Response 对象。
View 对象(例如 JSP 引擎)使用 Model 中的数据,结合视图模板文件生成最终的响应内容(HTML),并写入 HttpServletResponse。
DispatcherServlet 完成响应。

4.3 代码示例 (模块四)

代码示例主要基于模块三的 UserController、User 模型和 JSP 视图,这里补充 @ModelAttribute 在方法参数上的详细说明,以及视图解析器的配置。

@ModelAttribute 用于方法参数的详细说明

在 UserController 的 addUser 方法中:

@PostMapping // 处理 POST 请求 /users
public String addUser(@ModelAttribute("user") User user, // <-- 数据绑定发生在这里
                      @RequestParam(value = "redirect", required = false, defaultValue = "false") boolean redirectToList,
                      Model model) {
    // ... 方法体
}

当请求(例如表单提交)到达 /users 且方法是 POST 时,Spring MVC 执行以下步骤:
看到 addUser 方法的 @ModelAttribute(“user”) User user 参数。
查找 Model 中是否有名为 “user” 的属性。
如果在 Controller 中有 @ModelAttribute(“user”) public User setupForm() 方法,并且该方法在当前请求处理之前执行了(例如,因为它是 GET /users/new 请求的处理方法,且 POST /users 提交时可能是在同一个会话中),Spring MVC 会使用 setupForm() 方法返回的 User 对象作为绑定目标。
如果 Model 中没有名为 “user” 的属性,Spring MVC 会使用 User 类的无参构造器创建一个新的 User 对象作为绑定目标。
WebDataBinder 开始将请求参数绑定到这个 User 对象。例如,如果请求包含参数 username=Alice&id=123,WebDataBinder 会调用 user.setUsername(“Alice”) 和 user.setId(123L)。
绑定成功后,将填充好数据的 user 对象作为参数传递给 addUser 方法。

视图解析器配置回顾

无论 XML 还是 JavaConfig,配置 ViewResolver 是将逻辑视图名映射到实际视图文件的关键。
XML 配置 (spring-mvc.xml):

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/"/> 
    <property name="suffix" value=".jsp"/>            
bean>

说明:逻辑视图名 “userList” 会被解析为 /WEB-INF/views/userList.jsp。
JavaConfig 配置 (WebConfig.java):

@Bean
public ViewResolver viewResolver() {
    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    resolver.setPrefix("/WEB-INF/views/");
    resolver.setSuffix(".jsp");
    return resolver;
}

说明:效果同上。

Thymeleaf 视图示例 (替代 JSP)

首先,需要在 pom.xml 中添加 Thymeleaf 的 Spring 集成依赖:

<dependency>
    <groupId>org.thymeleafgroupId>
    <artifactId>thymeleaf-spring5artifactId> 
    <version>3.0.11.RELEASEversion> 
dependency>
<dependency>
    <groupId>javax.servletgroupId>
    <artifactId>javax.servlet-apiartifactId>
    <version>4.0.1version>
    <scope>providedscope>
dependency>

然后,在 JavaConfig (WebConfig.java) 中配置 Thymeleaf 相关的 Bean:

package com.yourpackage.config;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring5.view.ThymeleafViewResolver;
import org.thymeleaf.templatemode.TemplateMode;

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"com.yourpackage.controller", "com.yourpackage.service", "com.yourpackage.repository"})
public class WebConfig implements WebMvcConfigurer, ApplicationContextAware { // 实现 ApplicationContextAware 获取 ApplicationContext

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    // 配置模板资源解析器
    @Bean
    public SpringResourceTemplateResolver templateResolver() {
        SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
        templateResolver.setApplicationContext(this.applicationContext);
        templateResolver.setPrefix("/WEB-INF/templates/"); // Thymeleaf 模板存放路径,通常放在 WEB-INF 下
        templateResolver.setSuffix(".html"); // 模板后缀
        templateResolver.setTemplateMode(TemplateMode.HTML); // 模板模式
        templateResolver.setCacheable(false); // 开发时建议关闭缓存
        return templateResolver;
    }

    // 配置模板引擎
    @Bean
    public SpringTemplateEngine templateEngine() {
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(templateResolver()); // 设置模板资源解析器
        templateEngine.setEnableSpringELCompiler(true); // 启用 Spring EL 表达式
        return templateEngine;
    }

    // 配置视图解析器
    @Bean
    public ViewResolver thymeleafViewResolver() {
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setTemplateEngine(templateEngine()); // 设置模板引擎
        viewResolver.setCharacterEncoding("UTF-8"); // 设置编码
        return viewResolver;
    }

    // 配置静态资源处理
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
    }
}

说明:你需要创建 src/main/webapp/WEB-INF/templates/ 目录来存放 Thymeleaf 模板文件 (.html)。
• /WEB-INF/templates/userList.html (Thymeleaf 示例):

DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"> 
<head>
    <meta charset="UTF-8">
    <title>User Listtitle>
head>
<body>
    <h1>User Listh1>
    <table>
        <thead>
            <tr>
                <th>IDth>
                <th>Usernameth>
                <th>Actionsth>
            tr>
        thead>
        <tbody>
            <tr th:each="user : ${users}"> 
                <td th:text="${user.id}">1td> 
                <td th:text="${user.username}">Alicetd>
                <td><a th:href="@{/users/{id}(id=${user.id})}">View Detaila>td> 
            tr>
        tbody>
    table>
    <p><a th:href="@{/users/new}">Add New Usera>p> 
body>
html>

4.4 最佳实践与注意事项 (模块四)

• 选择合适的视图技术: JSP 适用于传统 Web 应用,Thymeleaf 和 FreeMarker 更适合现代应用,它们语法简洁,支持 HTML 原型设计。RESTful API 通常不需要服务器端视图渲染。
• 数据绑定: @ModelAttribute 是处理复杂对象(如表单)绑定的首选。确保要绑定的对象有无参构造器和对应的 Setter 方法。
• 类型转换: Spring MVC 提供了默认的类型转换器,但对于复杂或自定义类型的绑定,可能需要注册自定义的 Converter 或 Formatter。
• 数据校验: 通常在数据绑定后立即进行数据校验(Validation),Spring MVC 集成了 Bean Validation (JSR 303/380)。虽然不是本模块核心,但在处理表单时非常重要。
• 避免在视图中写业务逻辑: 视图只负责数据显示和页面结构,所有业务逻辑应放在 Controller 或 Service 层。
• 视图文件存放位置: 将视图文件放在 /WEB-INF/ 目录下是最佳实践,这样可以防止用户通过直接 URL 访问视图文件,只能通过 Controller 访问。
• 静态资源处理: 使用 mvc:resources 或 addResourceHandlers 配置处理静态资源(CSS, JS, 图片等),避免 DispatcherServlet 处理这些请求。

模块五:RESTful 服务与数据交互

本模块学习如何使用 Spring MVC 构建 RESTful 风格的 Web 服务接口,实现前后端数据交互。

5.1 概念阐述

• RESTful (Representational State Transfer): 一种架构风格,用于设计网络应用程序。RESTful 服务基于 HTTP 协议,强调无状态、客户端-服务器分离、统一接口等原则。核心概念包括:
• 资源 (Resource): Web 上的命名事物(如用户、订单)。每个资源有一个唯一的 URI 来标识。
• 资源操作: 对资源的操作通过 HTTP 方法来表示,如 GET (获取), POST (创建), PUT (更新), DELETE (删除)。
• 表述 (Representation): 资源的状态通过某种格式(如 JSON, XML)来表述,在请求和响应中传输。
• 无状态 (Stateless): 服务器不保存客户端的状态信息。每次请求都包含处理该请求所需的所有信息。
• 数据交互格式: RESTful API 常使用 JSON (JavaScript Object Notation) 或 XML 作为数据交换格式,因为它们易于解析和跨平台传输。JSON 更加轻量和常用。

5.2 关键注解详解 (RESTful 部分)

• @ResponseBody: 用在 Controller 方法上。 表示该方法的返回值不会被当做视图名,而是直接写入 HTTP 响应体中。常用于返回 JSON 或 XML 数据。Spring MVC 会使用 HttpMessageConverter 将返回值转换为指定的格式。
• @RequestBody: 用在 Controller 方法参数上。 表示方法参数的值来自 HTTP 请求体。Spring MVC 会使用 HttpMessageConverter 将请求体的内容(如 JSON 或 XML)转换为指定类型的 Java 对象。
• @RestController: 用在类上。 是 @Controller 和 @ResponseBody 的组合注解。标记一个类是 RESTful Controller。这意味着该类中所有方法的返回值默认都会直接写入响应体,而不需要额外加 @ResponseBody。简化了 RESTful API 的开发。

5.3 核心组件与工作流程 (数据交互)

• HttpMessageConverter:
• 作用: 负责在 Java 对象和 HTTP 请求/响应体之间进行转换。例如,将 Java 对象转换为 JSON 字符串写入响应体(@ResponseBody),或将请求体中的 JSON 字符串转换为 Java 对象 (@RequestBody)。
• Spring MVC 内置实现: Spring MVC 提供了多种 HttpMessageConverter 实现,如 MappingJackson2HttpMessageConverter (处理 JSON,依赖 Jackson 库)、Jaxb2RootElementHttpMessageConverter (处理 XML,依赖 JAXB 库) 等。
• 配置: mvc:annotation-driven/ 或 @EnableWebMvc 会自动注册常用的 HttpMessageConverter。如果需要处理 JSON/XML,只需要确保项目中引入了 Jackson 或 Gson 等库的依赖。

RESTful 请求处理流程简化

请求到达 DispatcherServlet。
DispatcherServlet -> HandlerMapping -> 找到带有 @RestController 或 @Controller + @RequestMapping (返回值为 @ResponseBody 方法) 的 Handler。
DispatcherAdapter 调用 Handler 方法。
如果方法参数使用了 @RequestBody:
HandlerAdapter 调用 HttpMessageConverter 将请求体内容反序列化为方法参数对象。
Controller 方法执行业务逻辑。
Controller 方法返回一个 Java 对象。
HandlerAdapter 或 DispatcherServlet 检测到方法或类上有 @ResponseBody (或使用了 @RestController)。
调用 HttpMessageConverter 将返回的 Java 对象序列化为指定格式 (如 JSON)。
将序列化后的数据写入 HTTP 响应体,并设置正确的 Content-Type 头(例如 application/json)。

5.4 代码示例 (模块五)

以下示例基于模块三的用户管理,将其改造为 RESTful API。

1. Controller 层 (UserRestController.java)
package com.yourpackage.controller;

import com.yourpackage.model.User;
import com.yourpackage.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; // HTTP 状态码
import org.springframework.http.ResponseEntity; // 封装响应
import org.springframework.web.bind.annotation.*; // 包含大部分常用注解

import java.util.List;

@RestController // 标记为 RESTful 控制器,等同于 @Controller + 类级别 @ResponseBody
@RequestMapping("/api/users") // API 基础路径
public class UserRestController {

    private final UserService userService;

    @Autowired
    public UserRestController(UserService userService) {
        this.userService = userService;
    }

    // 获取所有用户
    // GET /api/users
    @GetMapping
    public List<User> getAllUsers() {
        return userService.getAllUsers(); // 直接返回 List,会被 HttpMessageConverter 转为 JSON 写入响应体
    }

    // 获取特定用户
    // GET /api/users/{userId}
    @GetMapping("/{userId}")
    public ResponseEntity<User> getUserById(@PathVariable Long userId) { // 使用 ResponseEntity 可以自定义状态码和头部
        User user = userService.getUserById(userId);
        if (user != null) {
            return new ResponseEntity<>(user, HttpStatus.OK); // 返回用户对象和 200 状态码
        } else {
            return new ResponseEntity<>(HttpStatus.NOT_FOUND); // 返回 404 状态码
        }
    }

    // 创建新用户
    // POST /api/users
    @PostMapping
    @ResponseStatus(HttpStatus.CREATED) // 指定响应状态码为 201 Created
    public User createUser(@RequestBody User user) { // @RequestBody 将请求体 (JSON) 转换为 User 对象
        // 实际应用中,这里会调用 Service 保存用户,并可能设置用户ID等
        userService.saveUser(user); // 模拟保存
        return user; // 返回保存后的用户对象 (可能包含生成的ID)
    }

    // 更新用户
    // PUT /api/users/{userId}
    @PutMapping("/{userId}")
    public ResponseEntity<User> updateUser(@PathVariable Long userId, @RequestBody User user) {
        User existingUser = userService.getUserById(userId); // 模拟查找现有用户
        if (existingUser != null) {
            // 模拟更新逻辑
            existingUser.setUsername(user.getUsername());
            // 实际中会调用 Service 更新
            System.out.println("Updating user " + userId + " with new username: " + user.getUsername());
            return new ResponseEntity<>(existingUser, HttpStatus.OK); // 返回更新后的用户和 200 状态码
        } else {
            return new ResponseEntity<>(HttpStatus.NOT_FOUND); // 返回 404 状态码
        }
    }

    // 删除用户
    // DELETE /api/users/{userId}
    @DeleteMapping("/{userId}")
    @ResponseStatus(HttpStatus.NO_CONTENT) // 指定响应状态码为 204 No Content
    public void deleteUser(@PathVariable Long userId) {
        // 实际中会调用 Service 删除
        System.out.println("Deleting user with ID: " + userId);
        // 模拟删除成功,返回 204
    }

    // 示例:接受简单 JSON 数据
    @PostMapping("/search")
    public List<User> searchUsers(@RequestBody SearchRequest searchRequest) { // SearchRequest 是一个简单的 POJO { "keyword": "..." }
         System.out.println("Searching for keyword: " + searchRequest.getKeyword());
        // 模拟根据 keyword 搜索
        return userService.getAllUsers(); // 简化,实际会调用根据 keyword 搜索的方法
    }

    // 示例模型对象
    public static class SearchRequest {
        private String keyword;
        // Getter, Setter, No-arg constructor
        public SearchRequest() {}
        public String getKeyword() { return keyword; }
        public void setKeyword(String keyword) { this.keyword = keyword; }
    }
}
2. Service 层模拟

使用模块三的 UserService 接口和 UserServiceImpl 实现即可。

3. Model 模拟

使用模块三的 User 类即可。

4. Spring 配置

• XML 配置 (spring-mvc.xml): 确保 mvc:annotation-driven/ 已启用,并且 jackson-databind 等库已添加到依赖。

<mvc:annotation-driven/> 
<context:component-scan base-package="com.yourpackage.controller"/> 

beans>

• JavaConfig 配置 (WebConfig.java): 确保 @EnableWebMvc 已启用,并且 jackson-databind 等库已添加到依赖。

@Configuration
@EnableWebMvc // 启用 MVC 注解驱动,会自动注册 HttpMessageConverter
@ComponentScan(basePackages = {"com.yourpackage.controller", "com.yourpackage.service", "com.yourpackage.repository"})
public class WebConfig implements WebMvcConfigurer {
    // ... ViewResolver 等其他配置,REST API 通常不需要 ViewResolver
    // 如果是混合应用,ViewResolver 和 HttpMessageConverter 都会生效
}
5. 依赖 (Maven pom.xml)

除了 Spring MVC 核心依赖,为了处理 JSON,需要添加 Jackson 库:

<dependency>
    <groupId>com.fasterxml.jackson.coregroupId>
    <artifactId>jackson-databindartifactId>
    <version>2.13.0version> 
dependency>

5.5 最佳实践与注意事项 (模块五)

• 使用 @RestController: 当开发 RESTful API 时,优先使用 @RestController,它可以省去在每个方法上添加 @ResponseBody 的麻烦。
• 合理的 HTTP 方法: 根据 RESTful 规范,使用正确的 HTTP 方法来表示资源的操作 (GET for retrieve, POST for create, PUT for update/replace, PATCH for partial update, DELETE for delete)。
• 状态码: 返回正确的 HTTP 状态码来表示操作结果 (200 OK, 201 Created, 204 No Content, 400 Bad Request, 404 Not Found, 500 Internal Server Error 等)。ResponseEntity 类可以方便地控制响应状态码、头部和体。@ResponseStatus 注解可以用于方法或异常类上设置固定的状态码。
• 请求和响应格式: 在企业开发中,通常使用 JSON 作为 RESTful API 的数据交换格式。确保项目中引入了 Jackson 或 Gson 库,并且 HttpMessageConverter 配置正确(通常由 @EnableWebMvc 或 mvc:annotation-driven/ 自动完成)。
• 版本控制: 对于 RESTful API,考虑如何在 URL 中进行版本控制 (如 /api/v1/users)。
• 统一异常处理: 使用 @ControllerAdvice 和 @ExceptionHandler 实现全局的异常处理,返回统一的错误格式(详见模块六)。
• 数据校验: 对 @RequestBody 接收的对象进行数据校验,通常结合 Bean Validation。

模块六:进阶概念与实践应用

本模块学习 Spring MVC 的一些高级特性,如异常处理、重定向与转发、文件上传下载、拦截器等,并指导如何整合数据访问层,最后引入 Spring Boot。

6.1 概念阐述

本模块涵盖 Spring MVC 中一些更复杂但非常实用的功能,它们能够提升应用的健壮性、用户体验和可维护性。

6.2 关键注解详解 (进阶部分)

• @ExceptionHandler: 用在 Controller 类或 @ControllerAdvice 类的方法上。 用于处理特定 Controller 或全局范围内发生的特定类型的异常。当 Controller 方法抛出该异常时,被 @ExceptionHandler 标记的方法会被调用来处理异常并生成响应。
• 示例:@ExceptionHandler(UserNotFoundException.class)
• @ControllerAdvice: 用在类上。 标记一个类为全局控制器增强器。此类中的 @ExceptionHandler, @ModelAttribute, @InitBinder 方法会应用到应用程序中所有或指定范围的 @Controller 类上。常用于实现全局异常处理、全局数据绑定预处理等。

6.3 核心组件与工作流程 (进阶)

• 异常处理:
• 当 Controller 方法执行过程中抛出异常时,Spring MVC 会通过 HandlerExceptionResolver 机制来处理。
• ExceptionHandlerExceptionResolver 是处理 @ExceptionHandler 注解的主要解析器。它会在抛出异常后查找匹配的 @ExceptionHandler 方法来处理异常。
• 重定向与转发:
• 这不是一个独立组件,而是 Controller 方法返回值的一种特殊约定。
• 转发 (Forward): 服务器内部的跳转。客户端只发出一次请求,服务器将请求的处理权交给另一个资源(如另一个 Controller 方法或 JSP 页面)。URL 不变。
• 重定向 (Redirect): 客户端发出两次请求。服务器返回一个重定向响应(状态码 3xx),包含新的 URL。客户端收到响应后向新的 URL 发起第二次请求。URL 改变。
• 文件上传:
• 需要配置 MultipartResolver 组件。它负责解析 multipart/form-data 类型的请求(文件上传请求),将文件内容封装到 MultipartFile 对象中。
• Spring MVC 内置实现: CommonsMultipartResolver (依赖 Apache Commons FileUpload) 和 StandardServletMultipartResolver (基于 Servlet 3.0+ 的文件上传 API)。推荐使用后者。
• 拦截器 (Interceptor):
• Spring MVC 拦截器允许在请求处理流程的特定阶段(Controller 方法执行前、后)插入自定义逻辑。类似于 Servlet 的 Filter,但 Filter 在 DispatcherServlet 之前/后执行,Interceptor 在 DispatcherServlet 内部、HandlerMapping 之后、HandlerAdapter 调用 Controller 之前/后执行。
• 通过实现 HandlerInterceptor 接口(或继承 HandlerInterceptorAdapter,已废弃,现在通常实现 HandlerInterceptor 并提供默认方法实现)来创建拦截器。
• 需要通过配置注册拦截器,指定拦截的 URL 模式。
• HandlerInterceptor 接口的三个方法:
• preHandle(request, response, handler): 在 Controller 方法执行前调用。返回 true 继续执行,返回 false 终止请求。可用于权限检查、日志记录等。
• postHandle(request, response, handler, modelAndView): 在 Controller 方法执行后、视图渲染前调用。可用于修改 Model 数据、修改视图名等。
• afterCompletion(request, response, handler, ex): 在整个请求处理完成后调用(包括视图渲染完成),无论是否发生异常。可用于资源清理等。

6.4 代码示例 (模块六)

1. 异常处理 (AppExceptionHandler.java)

使用 @ControllerAdvice 实现全局异常处理。

package com.yourpackage.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

// @ControllerAdvice 默认扫描所有 Controller
// 可以指定扫描的 Controller 包或类
// @ControllerAdvice("com.yourpackage.controller")
@ControllerAdvice
public class AppExceptionHandler {

    // 处理特定的 UserNotFoundException 异常,返回 404
    @ExceptionHandler(UserNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND) // 设置响应状态码为 404
    @ResponseBody // 直接返回响应体 (通常用于 RESTful API)
    public ErrorResponse handleUserNotFoundException(UserNotFoundException ex) {
        // 返回自定义的错误信息结构
        return new ErrorResponse(HttpStatus.NOT_FOUND.value(), ex.getMessage());
    }

    // 处理所有其他未捕获的异常,返回 500
    // 可以定义更具体的异常处理方法
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) // 设置响应状态码为 500
    @ResponseBody
    public ErrorResponse handleGeneralException(Exception ex) {
        // 记录异常日志 (实际应用中应使用日志框架)
        ex.printStackTrace();
        return new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Internal Server Error: " + ex.getMessage());
    }

    // 自定义错误响应结构 (POJO)
    private static class ErrorResponse {
        private int status;
        private String message;

        public ErrorResponse(int status, String message) {
            this.status = status;
            this.message = message;
        }

        public int getStatus() { return status; }
        public String getMessage() { return message; }
    }
}

// 模拟一个自定义异常
class UserNotFoundException extends RuntimeException {
    public UserNotFoundException(Long userId) {
        super("User not found with ID: " + userId);
    }
}

在 Controller 中可以抛出自定义异常:

// UserRestController 中的 getUserById 方法
@GetMapping("/{userId}")
public ResponseEntity<User> getUserById(@PathVariable Long userId) {
    User user = userService.getUserById(userId);
    if (user != null) {
        return new ResponseEntity<>(user, HttpStatus.OK);
    } else {
        // 抛出自定义异常
        throw new UserNotFoundException(userId);
    }
}
2. 重定向与转发

在 Controller 方法中,通过返回特定的字符串前缀实现。

// 在 UserController 中
@PostMapping // 处理 POST 请求 /users (创建用户)
public String addUser(@ModelAttribute("user") User user) {
    userService.saveUser(user);
    // 重定向到用户列表页面
    // URL 会变为 /users,浏览器会发起新的 GET 请求
    // 注意:重定向会丢失 Model 数据,如果需要传递数据,可以使用 RedirectAttributes (Spring 3.1+)
    return "redirect:/users";
}

// 示例:转发到另一个 Controller 方法或视图
@GetMapping("/process")
public String processRequest(@RequestParam("action") String action) {
    if ("showForm".equals(action)) {
        // 转发到显示表单的方法
        // URL 不变,服务器内部跳转
        return "forward:/users/new"; // 转发到 UserController 的 /users/new 方法
    } else {
        // 转发到某个视图
        return "forward:/WEB-INF/views/someView.jsp"; // 转发到 JSP 页面
    }
}
3. 文件上传 (使用 StandardServletMultipartResolver)

确保 Servlet 容器支持 Servlet 3.0+(如 Tomcat 7+)。
在配置类 WebConfig.java 中配置 MultipartResolver Bean:

package com.yourpackage.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"com.yourpackage.controller"})
public class WebConfig implements WebMvcConfigurer {

    // 配置 StandardServletMultipartResolver
    @Bean
    public MultipartResolver multipartResolver() {
        return new StandardServletMultipartResolver();
    }

    // 其他配置...
}

在 Controller 方法中接收文件:使用 MultipartFile 作为方法参数。

package com.yourpackage.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;

@Controller
@RequestMapping("/files")
public class FileUploadController {

    // 处理文件上传表单提交
    @PostMapping("/upload")
    public String handleFileUpload(@RequestParam("file") MultipartFile file) {
        if (!file.isEmpty()) {
            try {
                // 获取原始文件名
                String originalFilename = file.getOriginalFilename();
                // 定义文件保存路径 (实际应用中应更加健壮)
                String uploadDir = "/path/to/your/upload/directory/"; // !!! 替换为你的实际路径
                File destFile = new File(uploadDir + originalFilename);

                // 保存文件到服务器
                file.transferTo(destFile);

                System.out.println("File uploaded successfully: " + originalFilename);
                return "uploadSuccess"; // 返回成功视图
            } catch (IOException e) {
                e.printStackTrace();
                return "uploadFailure"; // 返回失败视图
            }
        } else {
             System.out.println("Uploaded file is empty");
            return "uploadFailure"; // 返回失败视图
        }
    }

    // 显示文件上传表单的 GET 请求
    @GetMapping("/uploadForm")
    public String showUploadForm() {
        return "uploadForm"; // 返回上传表单视图 (如 uploadForm.jsp/html)
    }
}

上传表单视图 (uploadForm.jsp):
表单的 enctype 必须是 multipart/form-data。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>



    
    File Upload


    

Upload File

<%-- 注意:method 必须是 POST,enctype 必须是 multipart/form-data --%>
4. Spring MVC 拦截器 (LoginInterceptor.java)

实现一个简单的登录检查拦截器。

package com.yourpackage.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class LoginInterceptor implements HandlerInterceptor {

    // 在 Controller 方法执行前执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("LoginInterceptor preHandle...");
        HttpSession session = request.getSession();
        // 检查用户是否已登录 (这里简化判断 session 中是否有 "loggedInUser" 属性)
        Object user = session.getAttribute("loggedInUser");
        if (user != null) {
            // 用户已登录,继续处理请求
            return true;
        } else {
            // 用户未登录,重定向到登录页面
            System.out.println("User not logged in, redirecting to login page...");
            response.sendRedirect(request.getContextPath() + "/login"); // 假设登录页面的 URL 是 /login
            return false; // 阻止后续的请求处理
        }
    }

    // 在 Controller 方法执行后,视图渲染前执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
         System.out.println("LoginInterceptor postHandle...");
        // 可以在这里对 ModelAndView 进行修改
    }

    // 在整个请求处理完成后执行 (视图渲染后)
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
         System.out.println("LoginInterceptor afterCompletion...");
        // 可以进行资源清理等
    }
}

配置拦截器 (在 WebConfig.java 中实现 WebMvcConfigurer):

package com.yourpackage.config;

import com.yourpackage.interceptor.LoginInterceptor; // 引入拦截器
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableWebMvc
// @ComponentScan...
public class WebConfig implements WebMvcConfigurer {

    // 将拦截器注册为 Spring Bean
    @Bean
    public LoginInterceptor loginInterceptor() {
        return new LoginInterceptor();
    }

    // 配置拦截器及其拦截规则
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor()) // 添加拦截器实例
                .addPathPatterns("/**") // 拦截所有请求路径
                .excludePathPatterns("/login", "/logout", "/resources/**"); // 排除登录、注销、静态资源等路径
    }

    // 其他配置,如 ViewResolver, MultipartResolver 等
}
5. 整合数据访问层

在模块三的示例中,UserServiceImpl 模拟了数据访问。在实际应用中,Service 层会通过依赖注入的方式引用 Repository 或 DAO 层 Bean。

package com.yourpackage.service;

import com.yourpackage.model.User;
import com.yourpackage.repository.UserRepository; // 假设有一个 UserRepository 接口/类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserServiceImpl implements UserService {

    private final UserRepository userRepository; // 依赖注入 UserRepository

    @Autowired
    public UserServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public List<User> getAllUsers() {
        return userRepository.findAll(); // 调用 Repository 获取数据
    }

    @Override
    public User getUserById(Long id) {
        return userRepository.findById(id); // 调用 Repository 获取单条数据
    }

    @Override
    public void saveUser(User user) {
        userRepository.save(user); // 调用 Repository 保存数据
    }
}

需要对应的 UserRepository 接口或类,例如使用 Spring Data JPA:

package com.yourpackage.repository;

import com.yourpackage.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

// 如果使用 Spring Data JPA,只需要定义接口并继承 JpaRepository
// Spring 会自动生成实现
@Repository // 标记为 Repository 层组件
public interface UserRepository extends JpaRepository<User, Long> {
    // JpaRepository 提供了 findAll(), findById(), save(), delete() 等方法
    // 可以定义查询方法如: List findByUsername(String username);
}

配置数据访问层需要额外的 Spring Context 配置(数据源、JPA/MyBatis 配置、事务管理器等),通常放在 Root Context 中。

6. 了解 Spring Boot

• 简化配置: Spring Boot 的核心思想是“约定大于配置”。它提供了大量的 Starter POMs (例如 spring-boot-starter-web 包含了 Spring MVC、嵌入式 Tomcat、Jackson 等常用依赖) 和自动配置 (Auto-configuration)。
• 快速启动: Spring Boot 应用内嵌了 Web 服务器 (Tomcat, Jetty 或 Undertow),可以直接运行 JAR 文件启动应用,无需单独部署到外部 Web 容器。
• Spring Boot 项目结构: 通常有一个主应用类,包含 @SpringBootApplication 注解,它是一个复合注解,包含了 @Configuration, @EnableAutoConfiguration, @ComponentScan。
• 通过 Spring Boot 创建 Spring MVC 应用: 使用 Spring Initializr (start.spring.io) 可以快速生成 Spring Boot 项目骨架,选择 Web 依赖即可。编写 Controller 与原生 Spring MVC 类似,但配置大大简化。

// 简单的 Spring Boot 应用主类
package com.yourpackage;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication // 包含了 @EnableAutoConfiguration 和 @ComponentScan
public class MyApp {
    public static void main(String[] args) {
        SpringApplication.run(MyApp.class, args); // 直接运行
    }
}

Spring Boot 会自动配置 DispatcherServlet、HandlerMapping、HandlerAdapter、HttpMessageConverter (如果 classpath 中有 Jackson/Gson)、默认的 ViewResolver (取决于依赖,如 Thymeleaf/FreeMarker)。你只需要编写 Controller、Service、Repository 等业务代码即可。
掌握 Spring MVC 原生配置有助于理解框架原理,但实际企业开发中,强烈推荐使用 Spring Boot 来快速构建应用。

6.5 最佳实践与注意事项 (模块六)

• 异常处理: 使用 @ControllerAdvice 和 @ExceptionHandler 实现统一的异常处理,避免在每个 Controller 方法中编写 try-catch 块。返回统一的错误响应格式(尤其是 RESTful API)。
• 重定向与转发的选择: 根据需求选择。重定向用于 POST 请求后的 PRG (Post/Redirect/Get) 模式,避免表单重复提交;转发用于服务器内部的流程跳转,URL 不变,可以共享 Request 属性。
• 文件上传大小限制: 配置 MultipartResolver 时,可以设置文件总大小和单个文件大小的限制,防止恶意上传。
• 拦截器: 适用于跨多个请求处理的横切关注点,如用户认证、权限检查、日志记录、性能监控等。与 Filter 相比,拦截器能访问 Spring 上下文和 Handler 对象。
• 数据访问整合: Service 层依赖 Repository/DAO 层是标准的分层架构。通过 Spring 的依赖注入将它们关联起来。配置数据源、事务管理是关键。
• 拥抱 Spring Boot: 在掌握 Spring MVC 基础后,立即学习 Spring Boot。它极大地提高了开发效率,是现代 Spring 应用开发的标准。
学习建议再次强调:
理论与实践结合: 每个知识点都动手写代码。
理解工作流程: DispatcherServlet 工作流程是核心,反复琢磨。
官方文档: Spring 官方文档是权威且全面的参考资料。
善用调试: 利用 IDE 调试功能跟踪代码执行,观察对象状态变化。

// 简单的 Spring Boot 应用主类
package com.yourpackage;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication // 包含了 @EnableAutoConfiguration 和 @ComponentScan
public class MyApp {
    public static void main(String[] args) {
        SpringApplication.run(MyApp.class, args); // 直接运行
    }
}

Spring Boot 会自动配置 DispatcherServlet、HandlerMapping、HandlerAdapter、HttpMessageConverter (如果 classpath 中有 Jackson/Gson)、默认的 ViewResolver (取决于依赖,如 Thymeleaf/FreeMarker)。你只需要编写 Controller、Service、Repository 等业务代码即可。
掌握 Spring MVC 原生配置有助于理解框架原理,但实际企业开发中,强烈推荐使用 Spring Boot 来快速构建应用。

6.5 最佳实践与注意事项 (模块六)

• 异常处理: 使用 @ControllerAdvice 和 @ExceptionHandler 实现统一的异常处理,避免在每个 Controller 方法中编写 try-catch 块。返回统一的错误响应格式(尤其是 RESTful API)。
• 重定向与转发的选择: 根据需求选择。重定向用于 POST 请求后的 PRG (Post/Redirect/Get) 模式,避免表单重复提交;转发用于服务器内部的流程跳转,URL 不变,可以共享 Request 属性。
• 文件上传大小限制: 配置 MultipartResolver 时,可以设置文件总大小和单个文件大小的限制,防止恶意上传。
• 拦截器: 适用于跨多个请求处理的横切关注点,如用户认证、权限检查、日志记录、性能监控等。与 Filter 相比,拦截器能访问 Spring 上下文和 Handler 对象。
• 数据访问整合: Service 层依赖 Repository/DAO 层是标准的分层架构。通过 Spring 的依赖注入将它们关联起来。配置数据源、事务管理是关键。
• 拥抱 Spring Boot: 在掌握 Spring MVC 基础后,立即学习 Spring Boot。它极大地提高了开发效率,是现代 Spring 应用开发的标准。

学习建议再次强调:
理论与实践结合: 每个知识点都动手写代码。
理解工作流程: DispatcherServlet 工作流程是核心,反复琢磨。
官方文档: Spring 官方文档是权威且全面的参考资料。
善用调试: 利用 IDE 调试功能跟踪代码执行,观察对象状态变化。
循序渐进,持续实践。

快速项目上手实践

小型图书管理系统案例 (Spring MVC + Spring Data JPA + Thymeleaf)

企业级实用技术讲解

企业级Spring MVC高级主题与实用技术讲解

你可能感兴趣的:(Java入职必知必会,spring,mvc,java,后端,html,前端,html5)