Spring MVC:从入门到"入土",一篇让你笑中带泪的硬核指南

103 阅读4分钟

Spring MVC:从入门到"入土",一篇让你笑中带泪的硬核指南

一、Spring MVC 是什么?(三句话让面试官记住你)

"朋友,你掉的是这个金控制器,还是银模型,或者是这个普通的视图?" —— 来自Spring MVC河神的问候

Spring MVC本质上就是个大型线上剧本杀现场:

  • 用户(浏览器)发来剧本(请求)
  • 导演(DispatcherServlet)拿着剧本找演员(Controller)
  • 演员根据剧本即兴发挥(业务逻辑)
  • 道具组(ViewResolver)准备舞台布景
  • 最后给观众呈现一出好戏(响应)

二、用法大全:从青铜到王者的配置之路

1. 基础配置(青铜段位)

<!-- 经典web.xml配置 -->
<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

2. JavaConfig配置(王者段位)

@Configuration
@EnableWebMvc
@ComponentScan("com.example")
public class WebConfig implements WebMvcConfigurer {
    // 配置视图解析器
    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        return resolver;
    }
}

3. Restful风格控制器(秀儿写法)

@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        // 假装这里有数据库查询
        return new User(id, "码农阿呆", "coding@geek.com");
    }
    
    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody User user) {
        // 假装保存到数据库
        return ResponseEntity.created(URI.create("/api/users/1")).body(user);
    }
}

三、经典案例:用户注册功能(含完整翻车记录)

场景:用户注册→表单验证→保存数据库→发送欢迎邮件

1. 翻车现场实录

// 错误示范:线程安全问题集锦
public class UserController {
    private UserService userService; // 忘记加@Autowired
    private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); // 非线程安全
    
    @PostMapping("/register")
    public String register(User user) {
        // 没有表单验证
        userService.save(user); // 可能空指针
        sendEmail(user.getEmail()); // 同步发送邮件,性能灾难
        return "success";
    }
}

2. 正确姿势(秋名山车神版)

@RestController
@Validated
public class UserController {
    @Autowired
    private UserService userService;
    
    @PostMapping("/register")
    public ResponseEntity<?> register(@Valid @RequestBody UserDTO userDTO) {
        if(userService.existsByEmail(userDTO.getEmail())) {
            throw new BusinessException("邮箱已存在");
        }
        User user = userService.save(userDTO);
        emailService.asyncSendWelcomeEmail(user); // 异步发送
        return ResponseEntity.ok(Map.of("message", "注册成功"));
    }
}

四、原理深挖:DispatcherServlet的内心戏

处理流程的戏剧化解读:

1. 用户请求:"我要点一份宫保鸡丁!"
2. 大堂经理(DispatcherServlet):"后厨准备!"
3. 菜单解析器(HandlerMapping):"这道菜应该由川菜师傅处理"
4. 川菜师傅(Controller):"收到,开始炒菜!"
5. 传菜员(HandlerAdapter):"师傅这是您要的食材(参数绑定)"
6. 摆盘师傅(ViewResolver):"用青花瓷盘子装盘"
7. 服务员(View):"您的宫保鸡丁,请慢用"

源码级流程图(浓缩版):

HTTP Request → DispatcherServlet → HandlerMapping → HandlerAdapter → 
Interceptor.preHandle → Controller → Interceptor.postHandle → 
ViewResolver → View.render → Interceptor.afterCompletion

五、华山论剑:Spring MVC vs 其他框架

Spring MVCStruts2JAX-RS
配置方式注解/JavaConfigXML配置为主注解驱动
线程安全单例无状态ActionContext依赖实现
REST支持@RestController插件支持原生支持
性能优秀一般优秀
学习曲线中等简单较陡
流行度🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟

经典吐槽:"Struts2就像前任,曾经爱过,但现在提起来都是漏洞..."

六、避坑指南:血泪换来的10条军规

  1. 404之谜:视图解析器的prefix没写对?建议全文背诵"/WEB-INF/views/"
  2. 参数绑定失败:忘记加@RequestParam注解就像上厕所不带纸
  3. 跨域问题:CORS配置要像海关安检,既严格又灵活
  4. 文件上传:MultipartResolver没配置?文件上传就像传送门被封印
  5. 线程安全:在Controller里写成员变量等于在雷区蹦迪
  6. 静态资源:别让DispatcherServlet拦截所有请求,记得放行.css/.js
  7. 异常处理:全局异常处理器是程序员的后悔药
  8. 性能陷阱:同步发送邮件等于让法拉利去拉货
  9. 缓存误用:乱用@Cacheable就像把冰箱门当保险柜
  10. 版本控制:API没有版本控制就像永远在修改遗嘱

七、最佳实践:老司机的套路

  1. RESTful设计

    • 用HTTP方法动词代替action参数
    • 响应状态码要准确:200 OK,201 Created,404 Not Found
    • HATEOAS让API自己会说话
  2. 验证三板斧

    @PostMapping
    public ResponseEntity<?> create(@Valid @RequestBody UserDTO dto, 
                                   BindingResult result) {
        if(result.hasErrors()) {
            // 优雅处理验证错误
        }
    }
    
  3. 全局异常处理

    @RestControllerAdvice
    public class GlobalExceptionHandler {
        @ExceptionHandler(BusinessException.class)
        public ResponseEntity<ErrorResponse> handleBusinessException() {
            // 返回结构化的错误信息
        }
    }
    
  4. 文档自动生成:集成Swagger就像给API装行车记录仪

八、面试考点:BAT考官最爱问的5道题

  1. Spring MVC处理流程(必考题)

    • 参考答案:"记住DispatcherServlet是总导演,HandlerMapping是选角导演,ViewResolver是舞美设计..."
  2. @Autowired和@Resource的区别(套路题)

    • 解析:前者按类型,后者按名称,建议画个追星族找偶像的比喻
  3. 如何实现文件上传(实操题)

    • 正确姿势:MultipartFile参数 + 配置MultipartResolver
  4. RESTful设计原则(设计题)

    • 高分答案:资源化URL + HTTP方法语义化 + HATEOAS
  5. 怎么处理全局异常(实战题)

    • 加分项:@ControllerAdvice + 自定义异常体系 + 统一响应格式

九、总结:Spring MVC的十二时辰

晨起(初始化)→ 接客(接收请求)→ 派单(HandlerMapping)→ 处理(Controller)→ 善后(视图渲染)→ 复盘(拦截器)

最后友情提醒:

  • 不要重复造轮子,但要知道轮子怎么造
  • 文档是你的好朋友,源码是你的好老师
  • 遇到问题先debug,再问度娘,最后发帖

最后的最后: "愿你的Controller永远返回200,愿你的ViewResolver永不迷路,愿你的Interceptor拦截所有bug!"