从 XML 到注解的 MVC 工程演进与小而全示例骨架

Spring MVC 工程配置经历了「全 XML → XML 扫描 + 注解驱动 → 全 Java 注解 → Spring Boot 约定大于配置」的演进。理解这条线,看老项目不慌,写新项目也明白每个 Bean 从哪来。

演进时间线

时代配置形态代表做法
Spring 2.x全 XML每个 Controller 手工配 bean、HandlerMapping 全手写
Spring 3.xXML + 注解@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 注册 DispatcherServletAbstractAnnotationConfigDispatcherServletInitializer
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 工程的起跑线。

笔记加载中…