Spring boot提供了AbstractRoutingDataSource 根據用戶定義的規則選擇當前的數據源,這樣咱們能夠在執行查詢以前,設置使用的數據源。實現可動態路由的數據源,在每次數據庫查詢操做前執行。它的抽象方法 determineCurrentLookupKey() 決定使用哪一個數據源,這裏使用aop實現。mysql
一、數據準備,兩個數據庫,分別爲 「springboot」、「springboot_1」spring
測試表booksql
CREATE TABLE `book` (
`id` int(11) NOT NULL,
`book_name` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `book_index` (`id`,`book_name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
複製代碼
測試數據數據庫
INSERT INTO springboot.book(id, book_name) VALUES (1, '測試,來自主庫');
INSERT INTO springboot_1.book(id, book_name) VALUES (1, '測試,來自從庫');
複製代碼
二、pom.xmlspringboot
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
複製代碼
三、application.yml 配置多個數據源bash
spring:
#數據庫配置
datasource:
# type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
druid:
master:
jdbc-url: jdbc:mysql://192.0.0.210:3306/springboot?useSSL=true&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true
username: root
password: 123456
slave:
jdbc-url: jdbc:mysql://192.0.0.210:3306/springboot_1?useSSL=true&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true
username: root
password: 123456
maxActive: 20
initialSize: 1
maxWait: 60000
minIdle: 1
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxOpenPreparedStatements: 20
複製代碼
四、自定義註解類app
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MoreDataSource {
public static String master = "master";
public static String slave = "slave";
String name() default MoreDataSource.slave;
}
複製代碼
五、新建DynamicDataSource類,擴展Spring的AbstractRoutingDataSource抽象類,重寫 determineCurrentLookupKey() 方法ide
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
#在第6步建立
String key = DataSourceHolder.getDataSourceKey();
if (StringUtils.isBlank(key)) {
return MoreDataSource.master;
}
return key;
}
}
複製代碼
六、新建DataSourceHolder類,一個擁有ThreadLocal變量的類,用來存取數據源名稱spring-boot
public class DataSourceHolder {
private static final ThreadLocal<String> dataSources = new ThreadLocal<>();
public static void setDataSourceKey(String customType) {
dataSources.set(customType);
}
public static String getDataSourceKey() {
return (String) dataSources.get();
}
public static void clearDataSourceKey() {
dataSources.remove();
}
}
複製代碼
七、新建多數據源配置類DataSourceConfig測試
@Configuration
public class DataSourceConfig {
@Bean
@Primary
public DataSource dataSource() {
DynamicDataSource resolver = new DynamicDataSource();
Map<Object, Object> dataSources = Maps.newHashMap();
dataSources.put(MoreDataSource.master, masterDataSource());
dataSources.put(MoreDataSource.slave, slaveDataSource());
resolver.setTargetDataSources(dataSources);
return resolver;
}
@Bean
@ConfigurationProperties(prefix="spring.datasource.druid.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix="spring.datasource.druid.slave")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
}
複製代碼
八、新建切面類DataSourceAspect
@Aspect
@Component
public class DataSourceAspect {
@Pointcut("@annotation(com.test.annotation.MoreDataSource)")
public void aspect() {
}
@Before("aspect()")
public void doBefore(JoinPoint point) throws Throwable {
final MethodSignature methodSignature = (MethodSignature) point.getSignature();
Method method = methodSignature.getMethod();
MoreDataSource mzDataSource = method.getAnnotation(MoreDataSource.class);
if (method.getDeclaringClass().isInterface()) {
method = point.getTarget().getClass().getMethod(method.getName(), method.getParameterTypes());
}
mzDataSource = method.getAnnotation(MoreDataSource.class);
if (null != mzDataSource) {
DataSourceHolder.setDataSourceKey(mzDataSource.name());
}
System.out.println("數據源切換:" + DataSourceHolder.getDataSourceKey());
}
@After("aspect()")
public void doAfter() {
DataSourceHolder.clearDataSourceKey();
}
}
複製代碼
九、修改啓動類,由於數據源是本身生成的,因此要去掉原先springboot啓動時候自動裝配的數據源配置
加上註解
@Import({DataSourceConfig.class})
複製代碼
十、測試
controller
@RequestMapping(value="/list/{id}")
@ResponseBody
public Book list(@PathVariable(value = "id") Integer id){
Book book = bookService.findById(id);
return book;
}
@RequestMapping(value="/list2/{id}")
@ResponseBody
@MoreDataSource
public Book list2(@PathVariable(value = "id") Integer id){
Book book = bookService.findById(id);
return book;
}
複製代碼
測試沒有問題,能夠動態的根據註解切換數據源,在結合以前的主從數據庫配置就能夠進一步的實現讀寫分離。