社交媒体

Spring MVC核心理解与相关知识点

Spring MVC核心理解与相关知识点

MVC是什么

MVC不单单只属于Spring,他更是一种设计模式、他最重要的目的便是“解耦”

  • M(Model,模型 ):数据+业务逻辑。比如Student类,StudentService 。

  • V(View,视图):展示数据界面。比如JSP、或者是现代返回JSON格式的前端页面。

  • C(Controller,控制器):接收用户请求,调用Model,最后选择View来展示。

流程:用户触发键鼠监听 →Controllerna拿到请求→调Service拿到数据(Model**)→把数据交给View渲染→返回HTML给浏览器。

Spring MVC ** 就是把这个流程在Java Web 环境里面实现的一套框架。

核心组件与处理流程

Spring MVC 最核心的就是一个 前端控制器(Font Controller)——DispatcherServlet 。

组件

作用

常见实现

DispatcherServlet

统一入口,调度一切

Spring 提供,我们只需配置

HandlerMapping

根据请求 URL 找到对应的处理器

RequestMappingHandlerMapping

HandlerAdapter

执行找到的处理器(Controller 方法)

RequestMappingHandlerAdapter

HandlerInterceptor

拦截器,在处理方法前后做增强

自定义

ViewResolver

根据视图名找到真正的视图文件

InternalResourceViewResolver (JSP)

HandlerExceptionResolver

处理异常

ExceptionHandlerExceptionResolver

一个请求的完整声明周期

  1. 请求到达DispatcherServlet 。

  2. DispatcherServlet 找HandlerMapping :谁出来这个路由路径

  3. HandelerMapping 返回一个HandlerEXecutionChain (包含Controller和拦截器)

  4. DispatcherServlet 找HandlerAdapter

  5. HandlerAdapter 执行具体方法(期间会做参数绑定、类型转换、校验等)

  6. 方法返回ModelAndView (或@ResponseBody 直接返回数据)

  7. 若有视图名,ViewResolver 解析出真正的JSP等视图

  8. 渲染视图,响应给浏览器。

Controller与注解

@Controller & @RestController

@Controller  // 声明这是一个控制器类,方法通常返回视图名
public class TeacherController { ... }

@RestController  // = @Controller + @ResponseBody,所有方法默认返回 JSON
public class TeacherRestController { ... }

@RequestMapping 及衍生注解

@RequestMapping("/teacher")   // 类级别映射
public class TeacherController {

    @RequestMapping(value = "/list", method = RequestMethod.GET)
    public String list() { ... }  // 等价于 @GetMapping("/list")
}

@GetMapping、@PostMapping、@PutMapping、@DeleteMapping 等都是 @RequestMapping 的快捷方式。

参数绑定注解

注解

从哪里拿数据

示例

@RequestParam

URL 问号后的参数或表单数据

?name=Tom → @RequestParam("name") String name

@PathVariable

URI 路径中的占位符

/teacher/{id} → @PathVariable("id") Long id

@RequestBody

请求体中的 JSON/XML

POST 的 JSON {"name":"Tom"} 自动转成 Teacher 对象

@ModelAttribute

① 从 Model 中取;② 把参数绑定到对象

常用于表单提交自动封装对象,

@RequestHeader

请求头

@RequestHeader("User-Agent") String ua

@CookieValue

Cookie

@CookieValue("JSESSIONID") String sid

把参数自动封装成对象

Spring 可以直接将表单字段或 JSON ** 映射成一个 Java Bean。

表单提交示例:

错误: Language jsp is not included in this bundle. You may want to load it from external source.

@PostMapping("/teacher/save")
public String save(@ModelAttribute Teacher teacher) {
    // teacher 对象已被自动填充 name 和 age
    teacherService.save(teacher);
    return "redirect:/teacher/list";
}

底层原理:DataBinder + BeanWrapper 负责属性拷贝。

自定义类型转换器: String → Date

很多时候,请求参数是 String,但你想转成 Date、LocalDate 或自定义类型。

Spring 内置的转换器和格式化器

  • Converter<S, T>:源类型 → 目标类型,如 StringToDateConverter。

  • Formatter:专为字符串和对象互转设计,支持国际化,适合 String <-> Date。

自定义一个 String → Date 的 Converter 示例

@Component
public class StringToDateConverter implements Converter<String, Date> {
    private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

    @Override
    public Date convert(String source) {
        try {
            return format.parse(source);
        } catch (ParseException e) {
            throw new IllegalArgumentException("日期格式必须为 yyyy-MM-dd");
        }
    }
}

在 Spring Boot ** 中,只需让它被容器管理(@Component)就会自动注册到转换器链。

在传统 Spring MVC XML 中,需要配置 FormattingConversionServiceFactoryBean。

使用场景

@GetMapping("/teacher/search")
public String searchByDate(@RequestParam("date") Date date) {
    // /teacher/search?date=2025-01-01 → date 直接转换好
}

如果转换失败,会抛出 TypeMismatchException,我们可以用异常处理来统一返回友好信息。

拦截器(Interceptor)和过滤器(Filter)

两者都可以在请求前后做手脚,但层级不同。

对比维度

Filter(过滤器)

Interceptor(拦截器)

归属

Servlet 规范,Java EE

Spring MVC 框架

作用范围

能拦截所有进入 Servlet 的请求(包括静态资源)

只能拦截进到 Spring MVC 的请求(DispatcherServlet 处理)

是否能用 Spring Bean

不能直接注入(可以迂回)

可以正常注入其他 Bean

执行顺序

先经过 Filter,再进 DispatcherServlet

在 DispatcherServlet 之后,Controller 方法之前

典型场景

字符编码、跨域、权限安全检查

登录检查、日志记录、性能监控、用户权限补充

定义拦截器

@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) {
        // 检查 session 中是否有用户
        if (request.getSession().getAttribute("user") == null) {
            response.sendRedirect("/login");
            return false;  // 不放行
        }
        return true;
    }
}

注册拦截器

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/teacher/**")   // 要拦截的
                .excludePathPatterns("/teacher/login"); // 排除的
    }
}

异常处理

局部异常处理

@Controller
public class TeacherController {
    @ExceptionHandler(TeacherNotFoundException.class)
    public String handleNotFound(TeacherNotFoundException ex, Model model) {
        model.addAttribute("msg", ex.getMessage());
        return "error/404";   // 返回错误视图
    }
}

全局异常处理

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public Map<String, String> handleValidation(MethodArgumentNotValidException ex) {
        // 返回校验失败信息,如 {"name":"不能为空"}
        ...
    }

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public Map<String, String> handleGeneral(Exception ex) {
        return Map.of("error", "服务器内部错误");
    }
}