為每個數據源定義完整 Bean 鏈:
<bean name="dataSource" class="DruidDataSource">...</bean>
<bean id="sqlSessionFactory" class="SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/> ...
</bean>
<bean id="transactionManager" class="DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean class="MapperScannerConfigurer">
<property name="basePackage" value="net.aazj.mapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> <!-- 關鍵綁定 -->
</bean>
<!-- 第二套:dataSource_2, sqlSessionFactory_2, transactionManager_2, mapper2 -->
enum DataSources { MASTER, SLAVE }public class DataSourceTypeManager {
private static final ThreadLocal holder = ThreadLocal.withInitial(() -> DataSources.MASTER);
public static DataSources get() { return holder.get(); }
public static void set(DataSources ds) { holder.set(ds); }
public static void reset() { holder.set(DataSources.MASTER); }
}
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
@Override protected Object determineCurrentLookupKey() {
return DataSourceTypeManager.get();
}
}
<bean id="dataSource" class="DynamicRoutingDataSource">
<property name="defaultTargetDataSource" ref="dataSourceMaster"/>
<property name="targetDataSources">
<map key-type="DataSources">
<entry key="MASTER" value-ref="dataSourceMaster"/>
<entry key="SLAVE" value-ref="dataSourceSlave"/>
</map>
</property>
</bean>
<bean id="sqlSessionFactory" class="SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/> ...
</bean>
@Aspect @Component @Order(0) // 必須早於 @Transactional
public class DataSourceRoutingAspect {
@Pointcut("execution(public * com.xxx.service..*.query*(..))")
public void slaveMethods() {}
@Before("slaveMethods()")
public void routeToSlave() { DataSourceTypeManager.set(DataSources.SLAVE); }
@Pointcut("execution(public * com.xxx.service..*.save*(..)) || execution(* ..update*(..)) || execution(* ..delete*(..))")
public void masterMethods() {}
@Before("masterMethods()")
public void routeToMaster() { DataSourceTypeManager.set(DataSources.MASTER); }
}
關鍵:@Order(0) 確保路由切面在事務切面(@Transactional)之前執行,拿到正確連接。
InitializingBean,afterPropertiesSet() 將 targetDataSources Map 轉換為 resolvedDataSources(Key 解析後)。getConnection() → determineTargetDataSource() → determineCurrentLookupKey()(子類實現,讀 ThreadLocal)→ 從 Map 獲取實際 DataSource → 返回連接。DynamicRoutingDataSource 實現(內部維護可用節點列表、定時健康檢查線程)。AOP + AbstractRoutingDataSource 實現**零侵入、極致靈活**的多數據源路由,完美支撐主從讀寫分離、多租戶、分庫分表等進階場景。