Spring MVC (表現層) → Service (業務層) → MyBatis Mapper (持久層)
↑ ↑ ↑
DispatcherServlet @Transactional SqlSessionFactory
HandlerMapping AOP 織入 MapperScanner
ViewResolver
# SqlMapConfig.xml
<configuration>
<typeAliases>
<package name="com.luchao.pojo"/> <!-- 批量別名,類名即別名 -->
</typeAliases>
</configuration>
# applicationContext-dao.xml
<beans ...>
<context:property-placeholder location="classpath:db.properties"/>
<!-- DBCP 數據源 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="10"/>
<property name="maxIdle" value="5"/>
</bean>
<!-- SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml"/>
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- Mapper 掃描器:自動註冊所有 Mapper 介面為 Bean -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.luchao.mapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
</beans>
public interface ItemsService {
List<ItemsCustom> findItemsList(ItemsQueryVo vo) throws Exception;
}
@Service
public class ItemsServiceImpl implements ItemsService {
@Autowired
private ItemsMapperCustom itemsMapperCustom;
@Override
public List<ItemsCustom> findItemsList(ItemsQueryVo vo) throws Exception {
return itemsMapperCustom.findItemsList(vo);
}
}
# applicationContext-service.xml <bean id="itemsService" class="com.luchao.service.ItemsServiceImpl"/>
# applicationContext-transaction.xml
<beans ...>
<!-- 事務管理器:使用 JDBC DataSourceTransactionManager -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/> <!-- 引用 dao 層 dataSource -->
</bean>
<!-- 事務屬性:增刪改 REQUIRED,查詢 READ_ONLY -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="find*" propagation="REQUIRED" read-only="true"/>
<tx:method name="get*" propagation="REQUIRED" read-only="true"/>
<tx:method name="select*" propagation="REQUIRED" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- AOP 切點:所有 service 包下方法 -->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.luchao.service.*.*(..))"/>
</aop:config>
</beans>
# springmvc.xml
<beans ...>
<!-- 掃描 Controller -->
<context:component-scan base-package="com.luchao.controller"/>
<!-- 註解驅動:HandlerMapping + HandlerAdapter + HttpMessageConverter -->
<mvc:annotation-driven/>
<!-- 視圖解析器:JSP -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern> <!-- 非 /*, 避免靜態資源攔截 -->
</servlet-mapping>
<!-- 載入 Spring 容器(dao/service/tx 配置) -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/spring/applicationContext-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
@Controller
@RequestMapping("/items")
public class ItemController {
@Autowired
private ItemsService itemsService;
@RequestMapping("/queryItems")
public ModelAndView queryItems() throws Exception {
List<ItemsCustom> list = itemsService.findItemsList(null);
ModelAndView mv = new ModelAndView();
mv.addObject("itemsList", list); // 對應 JSP ${itemsList}
mv.setViewName("items/itemsList"); // 視圖解析器拼接:/WEB-INF/jsp/items/itemsList.jsp
return mv;
}
}
| 層級 | 核心配置 | 關鍵類/註解 | 常見坑點 |
|---|---|---|---|
| Dao | MapperScannerConfigurer | @Mapper / Mapper 介面 | basePackage 掃描路徑錯誤導致 Bean 找不到 |
| Service | tx:advice + aop:advisor | @Service, @Transactional | 事務管理器引用錯誤 dataSource;切點表達式不匹配 |
| Web | mvc:annotation-driven | @Controller, @RequestMapping | url-pattern 用 /* 導致靜態資源 404;視圖前綴/後綴與 JSP 路徑不符 |
| 整合 | ContextLoaderListener | applicationContext-*.xml | 重複載入配置導致 Bean 重複註冊 |
DruidDataSource 替換 DBCP,支援監控、防 SQL 注入。mapUnderscoreToCamelCase=true,自動駝峰映射。@Transactional 註解替代 XML AOP,更靈活控制回滾條件。PageHelper 分頁插件,一行代碼實現物理分頁。ResponseEntity<T> 或統一 Result<T>,前後端分離更友好。完成上述配置,即可擁有一個標準化、可維護、事務安全的 SpringMVC + MyBatis 專案骨架。