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;
}
复制代码
测试没有问题,能够动态的根据注解切换数据源,在结合以前的主从数据库配置就能够进一步的实现读写分离。