SpringMVC 作為 Spring 家族的 Web 層框架,核心優勢在於 靈活的視圖解析、攔截器鏈、異常統一處理 以及與 Spring IoC、Hibernate 的無縫整合。本文針對這三大進階主題給出生產級配置範例與最佳實踐。
@Controller
public class MyController {
@RequestMapping("/toIndex")
public ModelAndView toIndex() {
// 方式 1:直接構造 RedirectView
View view = new RedirectView("https://www.example.com/index.jsp");
return new ModelAndView(view);
// 方式 2:返回 "redirect:" 前綴(Spring 3.0+ 推薦)
// return new ModelAndView("redirect:https://www.example.com/index.jsp");
}
}
<!-- 視圖解析器鏈:優先級由 order 控制 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="1"/> <!-- 高優先級 -->
</bean>
<!-- 重定向視圖 Bean,id 需與 Controller 返回視圖名一致 -->
<bean id="index" class="org.springframework.web.servlet.view.RedirectView">
<property name="url" value="https://www.example.com/index.jsp"/>
</bean>
<!-- 內部資源視圖解析器(JSP),最低優先級 -->
<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="order" value="100"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
關鍵點:BeanNameViewResolver 先按 Bean 名查找;找不到再落後到 InternalResourceViewResolver。order 值越小優先級越高。
SimpleMappingExceptionResolver<!-- 使用 ResourceBundleViewResolver 讀取 views.properties -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basenames">
<list><value>views</value></list> <!-- 對應 views.properties -->
</property>
</bean>
<!-- 異常解析器 -->
<bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.Exception">error</prop> <!-- 所有異常映射到 error 視圖 -->
<prop key="com.myapp.BusinessException">bizError</prop> <!-- 業務異常單獨頁面 -->
</props>
</property>
<!-- 可選:異常屬性名,供錯誤頁面顯示 -->
<property name="exceptionAttribute" value="ex"/>
</bean>
views.properties 定義錯誤視圖# 通用錯誤頁:重定向到友好頁面
error.class=org.springframework.web.servlet.view.RedirectView
error.url=https://www.example.com/error.jsp
# 業務錯誤頁:直接渲染 JSP
bizError.class=org.springframework.web.servlet.view.JstlView
bizError.url=/WEB-INF/jsp/bizError.jsp
效果:控制器拋出任何未捕獲異常,框架自動跳轉至對應視圖,避免黃頁面洩露堆棧信息。
web.xml
└─ ContextLoaderListener → 載入 applicationContext.xml (Service、DAO、DataSource、Tx)
└─ DispatcherServlet → 載入 spring-mvc.xml (Controller、ViewResolver、Interceptor)
applicationContext.xml)<!-- 數據源 (C3P0/Druid) -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.pass}"/>
</bean>
<!-- Hibernate SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="com.myapp.entity"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!-- 事務管理器 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!-- 註解驅動事務 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 掃描 Service、DAO -->
<context:component-scan base-package="com.myapp.service, com.myapp.dao"/>
spring-mvc.xml)<mvc:annotation-driven/>
<context:component-scan base-package="com.myapp.controller" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 視圖解析器鏈(如前節) -->
<!-- 靜態資源映射 -->
<mvc:resources mapping="/static/**" location="/static/"/>
<!-- 攔截器(登錄檢查、日誌等) -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/login"/>
<bean class="com.myapp.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
| 問題 | 原因 | 解決 |
|---|---|---|
| 事務不生效 | Service 未被 Spring 管理、或 @Transactional 在 private 方法上 | 確保 Service 為 Spring Bean、方法 public、調用非 this 內部 |
| LazyInitializationException | Session 關閉後訪問關聯集合 | OpenSessionInViewFilter / fetch join / DTO 投影 |
| 重定向丟失 Model 數據 | Redirect 發起新請求,Model 不傳遞 | 使用 RedirectAttributes.addFlashAttribute() |
| 視圖解析器衝突 | 多解析器無 order 或順序錯誤 | 顯式設定 order,InternalResourceViewResolver 最大 |
redirect: 前綴,複雜場景配置 RedirectView Bean + BeanNameViewResolverSimpleMappingExceptionResolver + ResourceBundleViewResolver 實現「拋出即跳轉」掌握這三塊,SpringMVC 專案即可具備生產級的健壯性與可維護性。