Spring MVC 作為 Spring 家族核心 Web 框架,憑藉註解驅動、RESTful 支援、靈活視圖解析、攔截器鏈等特性,成為企業級開發標配。本文梳理尚學堂教學大綱,提供從環境搭建到生產級整合的完整知識地圖。
ModelAndView、String (視圖名)、void (原生 response)、forward:/redirect: 前綴<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern> <!-- 非 *.do,支援 REST -->
</servlet-mapping>
<context:component-scan base-package="com.example.controller" />
<mvc:annotation-driven /> <!-- 自動註冊 RequestMappingHandlerMapping/Adapter、轉換器、校驗器 -->
<mvc:resources mapping="/static/**" location="/static/" /> <!-- 靜態資源放行 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- 文件上傳解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10485760" /> <!-- 10MB -->
</bean>
@Controller
@RequestMapping("/api/users")
public class UserController {
@Autowired private UserService userService;
@GetMapping("/{id}")
public ResponseEntity get(@PathVariable Long id) {
return ResponseEntity.ok(userService.get(id));
}
@PostMapping
public ResponseEntity create(@Valid @RequestBody UserCreateDTO dto) {
Long id = userService.create(dto);
return ResponseEntity.status(HttpStatus.CREATED).body(id);
}
@PutMapping("/{id}")
public ResponseEntity update(@PathVariable Long id, @Valid @RequestBody UserUpdateDTO dto) {
userService.update(id, dto);
return ResponseEntity.noContent().build();
}
@DeleteMapping("/{id}")
public ResponseEntity delete(@PathVariable Long id) {
userService.delete(id);
return ResponseEntity.noContent().build();
}
@PostMapping("/{id}/avatar")
public ResponseEntity uploadAvatar(@PathVariable Long id, @RequestPart MultipartFile file) {
String url = userService.uploadAvatar(id, file);
return ResponseEntity.ok(url);
}
}
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) {
String uri = req.getRequestURI();
if (uri.startsWith("/api/auth/") || uri.startsWith("/static/")) return true; // 放行
String token = req.getHeader("Authorization");
if (StringUtils.isBlank(token)) {
resp.setStatus(HttpStatus.UNAUTHORIZED.value());
return false;
}
// 解析 token、驗證簽名/過期、存入 ThreadLocal
UserContext.setCurrentUser(jwtUtil.parse(token));
return true;
}
@Override
public void afterCompletion(HttpServletRequest req, HttpServletResponse resp, Object handler, Exception ex) {
UserContext.remove(); // 防止 ThreadLocal 洩漏
}
}
// 配置註冊
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired private AuthInterceptor authInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor)
.addPathPatterns("/api/**")
.excludePathPatterns("/api/auth/**", "/static/**");
}
}
| 層級 | Spring 容器 | 掃描包 | 核心 Bean |
|---|---|---|---|
| 業務層 | Root Context (ContextLoaderListener) | service、dao、util | @Service、@Repository、DataSource、SqlSessionFactory、TransactionManager |
| Web 層 | Servlet Context (DispatcherServlet) | controller、interceptor、config | @Controller、ViewResolver、MultipartResolver、HandlerMapping |
父子容器隔離原則:子容器可訪問父容器 Bean,父容器**不可**訪問子容器 Bean。
事務配置:@EnableTransactionManagement + DataSourceTransactionManager,只在 Service 層開啟。
component-scan 基礎包、@RequestMapping 路徑、url-pattern 是否為 /CharacterEncodingFilter 必須在 filter-chain **最前面**enctype="multipart/form-data"、Bean 名稱必須為 multipartResolverObjectMapper 配置 setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"))addInterceptors 路徑匹配、未被 excludePathPatterns 誤排除Spring MVC 學習關鍵在於理解 DispatcherServlet 請求處理流程、掌握 註解驅動開發模式、熟練 攔截器鏈與異常處理、落地 SSM 分層架構與事務邊界。建議按本文 14 站路線逐項敲碼,配合案例項目,兩周內可達到獨立開發企業級 Web 應用水平。