(1)首先配置application
-
spring: datasource: type: com.alibaba.druid.pool.DruidDataSource druid: first: #数据源1 driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/xujl?useUnicode=true&characterEncoding=UTF-8 username: root password: admin second: #数据源2 url: jdbc:postgresql://172.xx.x.xx:xxxx/pcsu?charSet=utf-8 username: suit_test password: suit_test driverClassName: org.postgresql.Driver initial-size: 10 max-active: 100 min-idle: 10 max-wait: 60000 pool-prepared-statements: true max-pool-prepared-statement-per-connection-size: 20 time-between-eviction-runs-millis: 60000 min-evictable-idle-time-millis: 300000 validation-query: SELECT 1 FROM DUAL test-while-idle: true test-on-borrow: false test-on-return: false stat-view-servlet: enabled: true url-pattern: /druid/* #login-username: admin #login-password: admin filter: stat: log-slow-sql: true slow-sql-millis: 1000 merge-sql: true wall: config: multi-statement-allow: true
上面的方式实现就已经配置了两个 数据源了,下面来看下代码的实现
(2)配置一个注解,方便使用,直接在需要配置的方法上面加上数据源即可
-
@Target({ ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DataSource { String name() default ""; }
(3)动态数据源加载
-
/** * 动态数据源 */ public class DynamicDataSource extends AbstractRoutingDataSource { //用来保存数据源与获取数据源 private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public DynamicDataSource(DataSource defaultTargetDataSource, Map<String, DataSource> targetDataSources) { super.setDefaultTargetDataSource(defaultTargetDataSource); super.setTargetDataSources(new HashMap<Object, Object>(targetDataSources)); super.afterPropertiesSet(); } @Override protected Object determineCurrentLookupKey() { return getDataSource(); } public static void setDataSource(String dataSource) { contextHolder.set(dataSource); } public static String getDataSource() { return contextHolder.get(); } public static void clearDataSource() { contextHolder.remove(); } }
这里有必要说一下AbstractRoutingDataSource这个类,加载一个图片:
可以看到AbstractRoutingDataSource获取数据源之前会先调用determineCurrentLookupKey方法查找当前的lookupKey,这个lookupKey就是数据源标识。
因此通过重写这个查找数据源标识的方法就可以让spring切换到指定的数据源了。
(4)重要一点:把上面的信息加载到配置中
/**
* 配置多数据源
*/
@Configuration
public class DynamicDataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.druid.first")
public DataSource firstDataSource(){
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.druid.second")
public DataSource secondDataSource(){
return DruidDataSourceBuilder.create().build();
}
@Bean
@Primary
public DynamicDataSource dataSource(DataSource firstDataSource, DataSource secondDataSource) {
Map<String, DataSource> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceNames.FIRST, firstDataSource);
targetDataSources.put(DataSourceNames.SECOND, secondDataSource);
return new DynamicDataSource(firstDataSource, targetDataSources);
}
}
(5)最最重要的一步,就是使用spring的aop原理,切面方式加载数据源
/**
* 多数据源,切面处理类 处理带有注解的方法类
*/
@Aspect
@Component
public class DataSourceAspect implements Ordered {
protected Logger logger = LoggerFactory.getLogger(getClass());
@Pointcut("@annotation(xxxx.DataSource)")//注意:这里的xxxx代表的是上面public @interface DataSource这个注解DataSource的包名
public void dataSourcePointCut() {
}
@Around("dataSourcePointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
DataSource ds = method.getAnnotation(DataSource.class);
if (ds == null) {
DynamicDataSource.setDataSource(DataSourceNames.FIRST);
logger.debug("set datasource is " + DataSourceNames.FIRST);
} else {
DynamicDataSource.setDataSource(ds.name());
logger.debug("set datasource is " + ds.name());
}
try {
return point.proceed();
} finally {
DynamicDataSource.clearDataSource();
logger.debug("clean datasource");
}
}
@Override
public int getOrder() {
return 1;
}
}
(6)最后一步就是使用了在你的service的实现类 serviceImpl上面进行注解,这里是重点(我刚开一直放在dao上面,因为我用的是mybatis,以为就是要放在这个上面,结果一直出不来,最后才知道应该放在serviceImpl上面)
@Override
@DataSource(name="second")
public List<IntegralExchangeRule> list(Map<String,Object> map) {
return dao.list(map);
}
OK,现在已经全部配置完成,可以使用了
有必要说一下:我上面的DataSourceAspect这个类里面around方法里面,已经默认是数据源1,如果你不配置@DaeSource(name=""),它默认会使用第一个数据源,否则的话,按照你的数据源名称去使用的。