导读:
上一篇博客中介绍了 SpringBoot + MyBatis 多数据源配置,这种配置针对的场景是根据实际业务会将不同业务类型的表存放在不同的库中,但都属于同一个MySQL服务中。当我们MySQL采用的是主从模型,主库负责写数据,从库负责读数据,针对这种情况,需要使用动态数据源。我们还是通过一个demo来看下动态数据源(自动切换)如何实现,配置相对多数据源多一丢丢。
项目创建参考SpringBoot + MyBatis 多数据源配置,直接上干货
项目结构:
1.项目配置
- pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.harvey</groupId>
<artifactId>springboot-dynamic-ds</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springboot-dynamic-ds</name>
<description>springboot dynamic datasource</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.29</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- application.yml
master表示主库,slave表示从库,主库和从库可多个,新增参考如下配置即可
# port
server:
port: 8080
# log
log:
root:
level: INFO
# mysql
datasource:
master:
order-write:
url: jdbc:mysql://10.35.0.21:3306/order_write?useUnicode=true&characterEncoding=utf8&autoReconnect=true
username: 用户名
password: 密码
driver-class-name: com.mysql.jdbc.Driver
max-wait: 10000
min-idle: 2
type: com.alibaba.druid.pool.DruidDataSource
maxActive: 10
initial-size: 5
validation-query: SELECT 1
test-on-borrow: false
test-while-idle: true
time-between-eviction-runs-millis: 360000
filters: stat
slave:
order-read:
url: jdbc:mysql://192.168.191.65:3306/order_read?useUnicode=true&characterEncoding=utf8&autoReconnect=true
username: 用户名
password: 密码
driver-class-name: com.mysql.jdbc.Driver
max-wait: 10000
min-idle: 2
type: com.alibaba.druid.pool.DruidDataSource
maxActive: 10
initial-size: 5
validation-query: SELECT 1
test-on-borrow: false
test-while-idle: true
time-between-eviction-runs-millis: 360000
filters: stat
- banner.txt
_____ _ ____ _ _____ _ _____ _____
/ ____| (_) | _ \ | | | __ \ (_) | __ \ / ____|
| (___ _ __ _ __ _ _ __ __ _| |_) | ___ ___ | |_ ______| | | |_ _ _ __ __ _ _ __ ___ _ ___ ______| | | | (___
\___ \| '_ \| '__| | '_ \ / _` | _ < / _ \ / _ \| __|______| | | | | | | '_ \ / _` | '_ ` _ \| |/ __|______| | | |\___ \
____) | |_) | | | | | | | (_| | |_) | (_) | (_) | |_ | |__| | |_| | | | | (_| | | | | | | | (__ | |__| |____) |
|_____/| .__/|_| |_|_| |_|\__, |____/ \___/ \___/ \__| |_____/ \__, |_| |_|\__,_|_| |_| |_|_|\___| |_____/|_____/
| | __/ | __/ |
|_| |___/ |___/
- logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="false" scanPeriod="1 minutes">
<property name="LOG_NAME" value="springboot-dynamic-datasource"/>
<springProperty scope="context" name="log.root.level" source="log.root.level"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender" >
<encoder>
<pattern>%d{yyyy-MM-dd.HH:mm:ss.SSS} %t %p %c{0} %X{traceId} : %m%n</pattern>
</encoder>
</appender>
<root level="${log.root.level}" >
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
2.代码
(1).数据源
以下类在datasource包下
- DataSourceConfigurer.java
package com.harvey.datasource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.harvey.common.DataSourceKey;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* @description 数据源配置类,在该类中生成多个数据源实例并将其注入到 ApplicationContext 中
*/
@Configuration
@MapperScan(basePackages = {"com.harvey.dao"})
public class DataSourceConfigurer {
private final String MAPPER_LOCATION = "classpath*:mapper/**/*.xml";
// ------------------------------ 数据源配置 ------------------------------
@Bean("orderWrite")
@ConfigurationProperties(prefix = "datasource.master.order-write")
public DataSource orderWrite() {
return DruidDataSourceBuilder.create().build();
}
@Bean("orderRead")
@ConfigurationProperties(prefix = "datasource.slave.order-read")
public DataSource orderRead() {
return DruidDataSourceBuilder.create().build();
}
// ----------------------------------------------------------------------
@Bean("dynamicDataSource")
public DataSource dynamicDataSource() {
DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
Map<Object, Object> dataSourceMap = new HashMap<Object, Object>(DataSourceKey.values().length);
dataSourceMap.put(DataSourceKey.orderWrite.name(), orderWrite());
dataSourceMap.put(DataSourceKey.orderRead.name(), orderRead());
// 将 driverOrderRecordMaster 数据源作为默认指定的数据源
dynamicRoutingDataSource.setDefaultTargetDataSource(orderWrite());
// 指定使用到的所有数据源
dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);
// 将数据源的 key 放到数据源上下文的 key 集合中,用于切换时判断数据源是否有效
DynamicDataSourceContextHolder.dataSourceKeys.addAll(dataSourceMap.keySet());
return dynamicRoutingDataSource;
}
@Bean("sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dynamicDataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dynamicDataSource);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources(MAPPER_LOCATION));
return sqlSessionFactoryBean.getObject();
}
@Bean("sqlSessionTemplate")
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
// 注入 DataSourceTransactionManager 事务
@Bean
public PlatformTransactionManager transactionManager(@Qualifier("dynamicDataSource") DataSource dynamicDataSource) {
return new DataSourceTransactionManager(dynamicDataSource);
}
}
- DataSourceRoutingDataSource.java
package com.harvey.datasource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* @description 动态数据源配置类,继承AbstractRoutingDataSource
*/
@Slf4j
public class DataSourceRoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
log.info("Current DataSource is " + DynamicDataSourceContextHolder.getDataSourceKey());
return DynamicDataSourceContextHolder.getDataSourceKey();
}
}
- DynamicDataSourceAspect.java
package com.harvey.datasource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* @description 在数据操作之前切换数据源,完成操作后将数据源重置为默认数据源
*/
@Slf4j // 打印日志不用再写声明Logger变量,使用lombok的注解@Slf4j即可
@Aspect
@Order(-1) // 在 transaction 执行前执行
@Component
public class DynamicDataSourceAspect {
// 选择数据源
@Before("@annotation(targetDataSource))")
public void switchDataSource(JoinPoint point, TargetDataSource targetDataSource) {
// 数据源是否存在,存在则切换,反之使用默认
if (!DynamicDataSourceContextHolder.containDataSourceKey(targetDataSource.value())) {
log.info("DataSource [" + targetDataSource.value() + "] doesn't exist, use default DataSource [" + point.getSignature() + "] ");
} else {
// 切换数据源
DynamicDataSourceContextHolder.setDataSourceKey(targetDataSource.value());
log.info("Switch DataSource to [" + DynamicDataSourceContextHolder.getDataSourceKey() + "] in Method [" + point.getSignature() +"] ");
}
}
// 执行完数据操作后,重置到默认数据源
@After("@annotation(targetDataSource))")
public void restoreDataSource(JoinPoint point, TargetDataSource targetDataSource) {
// 将数据源置为默认数据源
DynamicDataSourceContextHolder.clearDataSourceKey();
log.info("Restore DataSource to [" + DynamicDataSourceContextHolder.getDataSourceKey() + "] in Method [" + point.getSignature() + "] ");
}
}
- DynamicDataSourceContextHolder.java
package com.harvey.datasource;
import com.harvey.common.DataSourceKey;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>() {
/** 将 driverOrderWrite 数据源的 key 作为默认数据源的 key */
@Override
protected String initialValue() {
return DataSourceKey.orderWrite.name();
}
};
// 存放所有数据源key
public static List<Object> dataSourceKeys = new ArrayList<>();
// 设置数据源
public static void setDataSourceKey(String key) {
contextHolder.set(key);
}
// 获取数据源
public static String getDataSourceKey() {
return contextHolder.get();
}
// 清空数据源
public static void clearDataSourceKey() {
contextHolder.remove();
}
// 判断某个数据源是否存在
public static boolean containDataSourceKey(String key) {
return dataSourceKeys.contains(key);
}
}
- DynamicRoutingDataSource.java
package com.harvey.datasource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* @description 访问数据库时会调用该类的 determineCurrentLookupKey() 方法获取数据库实例的 key
*/
@Slf4j
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
log.info("Current DataSource is [" + DynamicDataSourceContextHolder.getDataSourceKey() + "]");
return DynamicDataSourceContextHolder.getDataSourceKey();
}
}
- TargetDataSource.java
package com.harvey.datasource;
import java.lang.annotation.*;
/**
* @description 数据源切换接口,使用时使用注解 @TargetDataSource(value = "指定要使用的数据源")
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
String value();
}
在common包下新建类DataSourceKey
package com.harvey.common;
/**
* 数据源key
*/
public enum DataSourceKey {
orderWrite,
orderRead
}
(2).实体类
package com.harvey.model;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
// 类中不用在提供 get set 方法,使用lombok的注解 @Getter @Setter 即可
@Getter
@Setter
public class Order {
private String orderNo; // 订单号
private String userId; // 用户id
private Double money; // 金额
private String createTime; // 创建时间
}
(3).Dao及Mapper
- OrderDao.java
package com.harvey.dao;
import com.harvey.model.Order;
import java.util.List;
public interface OrderDao {
void insert(Order order);
List<Order> orderList();
}
- OrderDao.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.harvey.dao.OrderDao">
<select id="orderList" parameterType="int" resultType="com.harvey.model.Order">
SELECT
order_no orderNo,
user_id userId,
money,
create_time createTime
FROM
order_tb
</select>
<insert id="insert" parameterType="com.harvey.model.Order">
INSERT INTO order_tb (
order_no,
user_id,
money,
create_time
)
VALUES
(
#{orderNo},
#{userId},
#{money},
#{createTime}
)
</insert>
</mapper>
(4).Service
- IOrderService.java
package com.harvey.service;
import com.harvey.model.Order;
import java.util.List;
public interface IOrderService {
void insert(Order order);
List<Order> orderList();
}
- OrderServiceImpl.java
package com.harvey.service.impl;
import com.harvey.dao.OrderDao;
import com.harvey.datasource.TargetDataSource;
import com.harvey.model.Order;
import com.harvey.service.IOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class OrderServiceImpl implements IOrderService {
@Autowired
private OrderDao orderDao;
@TargetDataSource(value = "orderWrite")
@Override
public void insert(Order order) {
orderDao.insert(order);
}
@TargetDataSource(value = "orderRead")
@Override
public List<Order> orderList() {
return orderDao.orderList();
}
}
(5).Controller
- OrderController.java
package com.harvey.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.harvey.model.Order;
import com.harvey.service.IOrderService;
import com.sun.tools.corba.se.idl.constExpr.Or;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.text.SimpleDateFormat;
import java.util.*;
@RestController
@RequestMapping("/dynamicDS")
@Slf4j
public class OrderController {
private static final ObjectMapper objectMapper = new ObjectMapper();
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Autowired
private IOrderService orderService;
@PostMapping(value = "/queryOrderList")
public List queryOrderList(){
List<Order> orderList = null;
try {
orderList = orderService.orderList();
String result = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(orderList);
} catch (Exception e) {
log.info("query order data exception!", e);
}
return orderList;
}
@PostMapping(value = "/insertOrder")
public void insertOrder() {
Order order = new Order();
order.setOrderNo("20170512101112");
order.setUserId("12345");
order.setMoney(200.0);
order.setCreateTime(sdf.format(new Date()));
try {
orderService.insert(order);
} catch (Exception e) {
log.error("insert data exception!", e);
}
}
}
3.数据库表结构
主库和从库表结构相同,如下
drop table if exists `order_tb`;
create table `order_tb` (
`order_id` int(11) not null auto_increment,
`order_no` varchar(20) default null,
`user_id` varchar(20) default null,
`money` double default null,
`create_time` varchar(20) default null,
primary key (`order_id`)
) engine=innodb auto_increment=3 default charset=utf8;
从库表中插入数据,用于测试
insert into `order_tb` values ('1', 'o123456', '1001', '88.8', '2018-08-03 11:30:00');
insert into `order_tb` values ('2', 'o098336', '1002', '100', '2018-08-03 11:31:00');
4.测试
启动项目,打印日志如下
"C:\Program Files\Java\jdk1.8.0_51\bin\java" -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:55376,suspend=y,server=n -Dspring.output.ansi.enabled=always -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_51\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_51\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_51\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_51\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_51\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_51\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_51\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_51\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_51\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_51\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_51\jre\lib\rt.jar;D:\code\study\springboot-dynamic-ds\target\classes;D:\mvnrepo\org\springframework\boot\spring-boot-starter-web\2.0.4.RELEASE\spring-boot-starter-web-2.0.4.RELEASE.jar;D:\mvnrepo\org\springframework\boot\spring-boot-starter\2.0.4.RELEASE\spring-boot-starter-2.0.4.RELEASE.jar;D:\mvnrepo\org\springframework\boot\spring-boot\2.0.4.RELEASE\spring-boot-2.0.4.RELEASE.jar;D:\mvnrepo\org\springframework\boot\spring-boot-starter-logging\2.0.4.RELEASE\spring-boot-starter-logging-2.0.4.RELEASE.jar;D:\mvnrepo\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;D:\mvnrepo\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;D:\mvnrepo\org\apache\logging\log4j\log4j-to-slf4j\2.10.0\log4j-to-slf4j-2.10.0.jar;D:\mvnrepo\org\apache\logging\log4j\log4j-api\2.10.0\log4j-api-2.10.0.jar;D:\mvnrepo\org\slf4j\jul-to-slf4j\1.7.25\jul-to-slf4j-1.7.25.jar;D:\mvnrepo\javax\annotation\javax.annotation-api\1.3.2\javax.annotation-api-1.3.2.jar;D:\mvnrepo\org\yaml\snakeyaml\1.19\snakeyaml-1.19.jar;D:\mvnrepo\org\springframework\boot\spring-boot-starter-json\2.0.4.RELEASE\spring-boot-starter-json-2.0.4.RELEASE.jar;D:\mvnrepo\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.9.6\jackson-datatype-jdk8-2.9.6.jar;D:\mvnrepo\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.9.6\jackson-datatype-jsr310-2.9.6.jar;D:\mvnrepo\com\fasterxml\jackson\module\jackson-module-parameter-names\2.9.6\jackson-module-parameter-names-2.9.6.jar;D:\mvnrepo\org\springframework\boot\spring-boot-starter-tomcat\2.0.4.RELEASE\spring-boot-starter-tomcat-2.0.4.RELEASE.jar;D:\mvnrepo\org\apache\tomcat\embed\tomcat-embed-core\8.5.32\tomcat-embed-core-8.5.32.jar;D:\mvnrepo\org\apache\tomcat\embed\tomcat-embed-el\8.5.32\tomcat-embed-el-8.5.32.jar;D:\mvnrepo\org\apache\tomcat\embed\tomcat-embed-websocket\8.5.32\tomcat-embed-websocket-8.5.32.jar;D:\mvnrepo\org\hibernate\validator\hibernate-validator\6.0.11.Final\hibernate-validator-6.0.11.Final.jar;D:\mvnrepo\javax\validation\validation-api\2.0.1.Final\validation-api-2.0.1.Final.jar;D:\mvnrepo\org\jboss\logging\jboss-logging\3.3.2.Final\jboss-logging-3.3.2.Final.jar;D:\mvnrepo\com\fasterxml\classmate\1.3.4\classmate-1.3.4.jar;D:\mvnrepo\org\springframework\spring-web\5.0.8.RELEASE\spring-web-5.0.8.RELEASE.jar;D:\mvnrepo\org\springframework\spring-beans\5.0.8.RELEASE\spring-beans-5.0.8.RELEASE.jar;D:\mvnrepo\org\springframework\spring-webmvc\5.0.8.RELEASE\spring-webmvc-5.0.8.RELEASE.jar;D:\mvnrepo\org\springframework\spring-context\5.0.8.RELEASE\spring-context-5.0.8.RELEASE.jar;D:\mvnrepo\org\springframework\spring-expression\5.0.8.RELEASE\spring-expression-5.0.8.RELEASE.jar;D:\mvnrepo\org\mybatis\spring\boot\mybatis-spring-boot-starter\1.3.2\mybatis-spring-boot-starter-1.3.2.jar;D:\mvnrepo\org\springframework\boot\spring-boot-starter-jdbc\2.0.4.RELEASE\spring-boot-starter-jdbc-2.0.4.RELEASE.jar;D:\mvnrepo\com\zaxxer\HikariCP\2.7.9\HikariCP-2.7.9.jar;D:\mvnrepo\org\springframework\spring-jdbc\5.0.8.RELEASE\spring-jdbc-5.0.8.RELEASE.jar;D:\mvnrepo\org\springframework\spring-tx\5.0.8.RELEASE\spring-tx-5.0.8.RELEASE.jar;D:\mvnrepo\org\mybatis\spring\boot\mybatis-spring-boot-autoconfigure\1.3.2\mybatis-spring-boot-autoconfigure-1.3.2.jar;D:\mvnrepo\org\mybatis\mybatis\3.4.6\mybatis-3.4.6.jar;D:\mvnrepo\org\mybatis\mybatis-spring\1.3.2\mybatis-spring-1.3.2.jar;D:\mvnrepo\org\springframework\boot\spring-boot-starter-aop\2.0.4.RELEASE\spring-boot-starter-aop-2.0.4.RELEASE.jar;D:\mvnrepo\org\springframework\spring-aop\5.0.8.RELEASE\spring-aop-5.0.8.RELEASE.jar;D:\mvnrepo\org\aspectj\aspectjweaver\1.8.13\aspectjweaver-1.8.13.jar;D:\mvnrepo\org\springframework\spring-core\5.0.8.RELEASE\spring-core-5.0.8.RELEASE.jar;D:\mvnrepo\org\springframework\spring-jcl\5.0.8.RELEASE\spring-jcl-5.0.8.RELEASE.jar;D:\mvnrepo\com\alibaba\druid-spring-boot-starter\1.1.10\druid-spring-boot-starter-1.1.10.jar;D:\mvnrepo\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar;D:\mvnrepo\org\springframework\boot\spring-boot-autoconfigure\2.0.4.RELEASE\spring-boot-autoconfigure-2.0.4.RELEASE.jar;D:\mvnrepo\mysql\mysql-connector-java\5.1.46\mysql-connector-java-5.1.46.jar;D:\mvnrepo\com\alibaba\druid\1.0.29\druid-1.0.29.jar;C:\Program Files\Java\jdk1.8.0_51\lib\jconsole.jar;C:\Program Files\Java\jdk1.8.0_51\lib\tools.jar;D:\mvnrepo\org\projectlombok\lombok\1.16.22\lombok-1.16.22.jar;D:\mvnrepo\com\fasterxml\jackson\core\jackson-databind\2.9.6\jackson-databind-2.9.6.jar;D:\mvnrepo\com\fasterxml\jackson\core\jackson-annotations\2.9.0\jackson-annotations-2.9.0.jar;D:\mvnrepo\com\fasterxml\jackson\core\jackson-core\2.9.6\jackson-core-2.9.6.jar;D:\software\IntelliJ IDEA\lib\idea_rt.jar" com.harvey.SpringbootDynamicDsApplication
Connected to the target VM, address: '127.0.0.1:55376', transport: 'socket'
_____ _ ____ _ _____ _ _____ _____
/ ____| (_) | _ \ | | | __ \ (_) | __ \ / ____|
| (___ _ __ _ __ _ _ __ __ _| |_) | ___ ___ | |_ ______| | | |_ _ _ __ __ _ _ __ ___ _ ___ ______| | | | (___
\___ \| '_ \| '__| | '_ \ / _` | _ < / _ \ / _ \| __|______| | | | | | | '_ \ / _` | '_ ` _ \| |/ __|______| | | |\___ \
____) | |_) | | | | | | | (_| | |_) | (_) | (_) | |_ | |__| | |_| | | | | (_| | | | | | | | (__ | |__| |____) |
|_____/| .__/|_| |_|_| |_|\__, |____/ \___/ \___/ \__| |_____/ \__, |_| |_|\__,_|_| |_| |_|_|\___| |_____/|_____/
| | __/ | __/ |
|_| |___/ |___/
2018-08-14.14:41:42.574 main INFO SpringbootDynamicDsApplication : Starting SpringbootDynamicDsApplication on DESKTOP-EBF0M9S with PID 19664 (D:\code\study\springboot-dynamic-ds\target\classes started by admin in D:\code\work-idea\springboot-dynamic-ds)
2018-08-14.14:41:42.577 main INFO SpringbootDynamicDsApplication : No active profile set, falling back to default profiles: default
2018-08-14.14:41:42.737 main INFO AnnotationConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@258d79be: startup date [Tue Aug 14 14:41:42 CST 2018]; root of context hierarchy
2018-08-14.14:41:46.430 main INFO TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2018-08-14.14:41:46.465 main INFO Http11NioProtocol : Initializing ProtocolHandler ["http-nio-8080"]
2018-08-14.14:41:46.493 main INFO StandardService : Starting service [Tomcat]
2018-08-14.14:41:46.493 main INFO StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.32
2018-08-14.14:41:46.509 localhost-startStop-1 INFO AprLifecycleListener : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [C:\Program Files\Java\jdk1.8.0_51\bin;C:\WINDOWS\Sun\Java\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\ProgramData\Oracle\Java\javapath;C:\Program Files (x86)\Intel\iCLS Client\;C:\Program Files\Intel\iCLS Client\;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\Intel\Intel(R) Management Engine Components\DAL;C:\Program Files\Intel\Intel(R) Management Engine Components\DAL;C:\Program Files (x86)\Intel\Intel(R) Management Engine Components\IPT;C:\Program Files\Intel\Intel(R) Management Engine Components\IPT;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Program Files\Java\jdk1.8.0_51\bin;C:\Program Files\Java\jdk1.8.0_51\jre\bin;D:\software\Python27;D:\software\TortoiseSVN\bin;D:\software\apache-maven-3.3.9\bin;D:\software\Python27\Scripts;D:\software\hadoop\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;D:\software\Git\cmd;D:\software\Git\bin;D:\software\scala\bin;;C:\WINDOWS\System32\OpenSSH\;C:\Users\admin\AppData\Local\Microsoft\WindowsApps;;.]
2018-08-14.14:41:46.667 localhost-startStop-1 INFO [/] : Initializing Spring embedded WebApplicationContext
2018-08-14.14:41:46.667 localhost-startStop-1 INFO ContextLoader : Root WebApplicationContext: initialization completed in 3935 ms
2018-08-14.14:41:46.884 localhost-startStop-1 INFO ServletRegistrationBean : Servlet dispatcherServlet mapped to [/]
2018-08-14.14:41:46.888 localhost-startStop-1 INFO ServletRegistrationBean : Servlet statViewServlet mapped to [/druid/*]
2018-08-14.14:41:46.896 localhost-startStop-1 INFO FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
2018-08-14.14:41:46.896 localhost-startStop-1 INFO FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2018-08-14.14:41:46.896 localhost-startStop-1 INFO FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2018-08-14.14:41:46.896 localhost-startStop-1 INFO FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
2018-08-14.14:41:46.896 localhost-startStop-1 INFO FilterRegistrationBean : Mapping filter: 'webStatFilter' to urls: [/*]
2018-08-14.14:41:47.938 main INFO SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-08-14.14:41:48.364 main INFO RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@258d79be: startup date [Tue Aug 14 14:41:42 CST 2018]; root of context hierarchy
2018-08-14.14:41:48.503 main INFO RequestMappingHandlerMapping : Mapped "{[/dynamicDS/queryOrderList],methods=[POST]}" onto public java.util.List com.harvey.controller.OrderController.queryOrderList()
2018-08-14.14:41:48.512 main INFO RequestMappingHandlerMapping : Mapped "{[/dynamicDS/insertOrder],methods=[POST]}" onto public void com.harvey.controller.OrderController.insertOrder()
2018-08-14.14:41:48.530 main INFO RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2018-08-14.14:41:48.534 main INFO RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2018-08-14.14:41:48.611 main INFO SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-08-14.14:41:48.611 main INFO SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-08-14.14:41:49.071 main INFO AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2018-08-14.14:41:49.073 main INFO AnnotationMBeanExporter : Bean with name 'orderWrite' has been autodetected for JMX exposure
2018-08-14.14:41:49.073 main INFO AnnotationMBeanExporter : Bean with name 'orderRead' has been autodetected for JMX exposure
2018-08-14.14:41:49.074 main INFO AnnotationMBeanExporter : Bean with name 'statFilter' has been autodetected for JMX exposure
2018-08-14.14:41:49.080 main INFO AnnotationMBeanExporter : Located MBean 'orderRead': registering with JMX server as MBean [com.alibaba.druid.spring.boot.autoconfigure:name=orderRead,type=DruidDataSourceWrapper]
2018-08-14.14:41:49.084 main INFO AnnotationMBeanExporter : Located MBean 'orderWrite': registering with JMX server as MBean [com.alibaba.druid.spring.boot.autoconfigure:name=orderWrite,type=DruidDataSourceWrapper]
2018-08-14.14:41:49.085 main INFO AnnotationMBeanExporter : Located MBean 'statFilter': registering with JMX server as MBean [com.alibaba.druid.filter.stat:name=statFilter,type=StatFilter]
2018-08-14.14:41:49.101 main INFO Http11NioProtocol : Starting ProtocolHandler ["http-nio-8080"]
2018-08-14.14:41:49.125 main INFO NioSelectorPool : Using a shared selector for servlet write/read
2018-08-14.14:41:49.152 main INFO TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2018-08-14.14:41:49.157 main INFO SpringbootDynamicDsApplication : Started SpringbootDynamicDsApplication in 7.782 seconds (JVM running for 9.139)
使用postman测试
- 查询(使用从库)
打印日志:
2018-08-14.14:43:35.341 http-nio-8080-exec-8 INFO [/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
2018-08-14.14:43:35.341 http-nio-8080-exec-8 INFO DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
2018-08-14.14:43:35.372 http-nio-8080-exec-8 INFO DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 31 ms
2018-08-14.14:43:35.434 http-nio-8080-exec-8 INFO DynamicDataSourceAspect : Switch DataSource to [orderRead] in Method [List com.harvey.service.impl.OrderServiceImpl.orderList()]
2018-08-14.14:43:35.478 http-nio-8080-exec-8 INFO DynamicRoutingDataSource : Current DataSource is [orderRead]
2018-08-14.14:43:36.733 http-nio-8080-exec-8 INFO DruidDataSource : {dataSource-1} inited
2018-08-14.14:43:36.788 http-nio-8080-exec-8 INFO DynamicDataSourceAspect : Restore DataSource to [orderWrite] in Method [List com.harvey.service.impl.OrderServiceImpl.orderList()]
- 插入(使用主库)
打印日志:
2018-08-14.14:44:30.753 http-nio-8080-exec-4 INFO DynamicDataSourceAspect : Switch DataSource to [orderWrite] in Method [void com.harvey.service.impl.OrderServiceImpl.insert(Order)]
2018-08-14.14:44:30.754 http-nio-8080-exec-4 INFO DynamicRoutingDataSource : Current DataSource is [orderWrite]
2018-08-14.14:44:31.374 http-nio-8080-exec-4 INFO DruidDataSource : {dataSource-2} inited
2018-08-14.14:44:31.394 http-nio-8080-exec-4 INFO DynamicDataSourceAspect : Restore DataSource to [orderWrite] in Method [void com.harvey.service.impl.OrderServiceImpl.insert(Order)]
说明:当我们插入数据时用的是主库,查询操作用的是从库,日志中可以看出,插入还是查询操作,数据源会进行自动切换