原理:主要是調用目標方法時,注入不一樣的數據源,從而實現切換,即利用aop,而aop的實現是用代理實現的java
1,給工程添加一個獲取數據源的路由,並給它兩個不一樣的數據源ide
@Bean public ThreadLocalRountingDataSource threadLocalRountingDataSource() { ThreadLocalRountingDataSource dataSource=new ThreadLocalRountingDataSource(); Map<Object, Object> targetDataSources=new HashMap<Object, Object>(); DataSource master = master();//master 數據源 DataSource slave = slave();//slave 數據源 targetDataSources.put(DataSources.MASTER, master); targetDataSources.put(DataSources.SLAVE, slave); dataSource.setTargetDataSources(targetDataSources); dataSource.setDefaultTargetDataSource(master); return dataSource; }
2,ThreadLocalRountingDataSource類主要是有一個決定數據源鍵的方法工具
public class ThreadLocalRountingDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceTypeManager.get(); } }
3,DataSourceTypeManager實際是一個對ThreadLocal調用的一個工具類,主要是給當前線程設置期數據源的路由鍵,代碼以下:線程
import com.example.demo.enums.DataSources; public class DataSourceTypeManager { private static final ThreadLocal<DataSources> dataSourceTypes = new ThreadLocal<DataSources>(){ @Override protected DataSources initialValue(){ return DataSources.MASTER; } }; public static DataSources get(){ return dataSourceTypes.get(); } public static void set(DataSources dataSourceType){ dataSourceTypes.set(dataSourceType); } public static void reset(){ dataSourceTypes.set(DataSources.MASTER); } }
4,編寫一個aop類,決定在哪一個方法須要切換數據源代理
@Aspect // for aop @Component // for auto scan @Order(0) // execute before @Transactional public class DataSourceInterceptor { @Pointcut("execution(public * com.example.demo.service..*.getUser(..))") public void dataSourceSlave(){}; @Before("dataSourceSlave()") public void before(JoinPoint jp) { DataSourceTypeManager.set(DataSources.SLAVE); } }
彩蛋:code
事務攔截器在啥時候注入了ioc容器?blog
觀察@EnableTransactionManagement,它import了 TransactionManagementConfigurationSelector類,而後給容器注入了事務
AutoProxyRegistrar和ProxyTransactionManagementConfiguration,這裏咱們重點關注第二個類,點進去發現以下代碼路由
給@Transactional標註的方法添加了 TransactionInterceptor 攔截器get