从 XML 到注解的 MVC 工程演进与小而全示例骨架
Spring MVC 工程配置经历了「全 XML → XML 扫描 + 注解驱动 → 全 Java 注解 → Spring Boot 约定大于配置」的演进。理解这条线,看老项目不慌,写新项目也明白每个 Bean 从哪来。
演进时间线
| 时代 | 配置形态 | 代表做法 |
|---|---|---|
| Spring 2.x | 全 XML | 每个 Controller 手工配 bean、HandlerMapping 全手写 |
| Spring 3.x | XML + 注解 | @Controller + context:component-scan + mvc:annotation-driven |
| Spring 3.1+ | Java 配置 | @Configuration/@EnableWebMvc、WebApplicationInitializer 取代 web.xml |
| Spring 5/6 | 注解 + 函数式 | 全注解为主,Boot 提供自动装配 |
XML 时代一个最小 MVC 片段(扫描包 + 注解驱动 + 视图解析器三件套):
<context:component-scan base-package="com.example.web"/>
<mvc:annotation-driven/>
Java 配置中同样的三件事:扫描包、开 MVC 注解驱动、配视图解析器。
演进主线:谁替换了谁
| 老写法 | 新写法 |
|---|---|
| web.xml 注册 DispatcherServlet | AbstractAnnotationConfigDispatcherServletInitializer |
| XML 里的 mvc:annotation-driven/ | @EnableWebMvc |
| context:component-scan | @ComponentScan |
| 每类一个 <bean> | @Component/@Service/@Repository + 组件扫描 |
| mvc:interceptors / mvc:resources 等 | 实现 WebMvcConfigurer 覆写对应方法 |
一个小而全的骨架(原生 Spring MVC 6,WAR 部署到 Tomcat 10.1)
依赖(pom.xml 关键项)
<dependency>
<groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId>
<version>6.1.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId>
<version>2.15.3</version>
</dependency>
其余依赖按需补充:jakarta.servlet-api(provided)、hibernate-validator(如需校验)等。
1) 入口初始化器(替代 web.xml)
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() { return new Class[]{RootConfig.class}; } // 根容器:Service 层
@Override
protected Class<?>[] getServletConfigClasses() { return new Class[]{WebConfig.class}; } // Web 容器
@Override
protected String[] getServletMappings() { return new String[]{"/"}; } // 所有请求进 DispatcherServlet
}
2) 根容器:只扫 Service 层
@Configuration
@ComponentScan("com.example.service")
public class RootConfig {}
3) Web 容器:注解驱动 + MVC 定制
@Configuration
@EnableWebMvc
@ComponentScan("com.example.web")
public class WebConfig implements WebMvcConfigurer {
@Bean
public ViewResolver viewResolver() { // 页面需要时配置
InternalResourceViewResolver r = new InternalResourceViewResolver();
r.setPrefix("/WEB-INF/jsp/"); r.setSuffix(".jsp");
return r;
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
}
// 拦截器、CORS、Formatter 等按需覆写对应方法(第 30/32/27 章)
}
4) Controller 与统一异常(其余章已写,示意结构)
@RestController
@RequestMapping("/api/users")
public class UserApi { /* @GetMapping/@PostMapping... 见第 33 章 */ }
@RestControllerAdvice
public class ApiExceptionAdvice { /* @ExceptionHandler... 见第 29 章 */ }
打成 WAR 放进 Tomcat 10.1 的 webapps 启动即可;本地调试可用 IDE 的 Tomcat 集成或嵌入式容器插件。请求 https://example.com/static/app.js 与 /api/users 分别验证静态资源与接口。
两个易混概念
- 根容器(root)与 Web 容器(servlet)是两个上下文:根放 Service/DAO,Web 放 Controller,Web 容器能看到根容器的 Bean,反之不行。
- @EnableWebMvc 一旦加上,MVC 默认装配全部生效,同时你实现的 WebMvcConfigurer 方法会覆盖/扩展对应默认行为,二者配合而非互斥。
从 web.xml + XML Bean 到初始化器 + @EnableWebMvc + WebMvcConfigurer,Spring 用注解把「配置即代码」贯彻到底。小而全骨架五件套(初始化器、根容器、Web 容器、Controller、Advice)是后续一切 MVC 工程的起跑线。