SpringBoot多数据源配置

(1)首先配置application 

  1. 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)配置一个注解,方便使用,直接在需要配置的方法上面加上数据源即可

  1. @Target({ ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface DataSource {
        String name() default "";
    }

(3)动态数据源加载

  1. /**
     * 动态数据源 
     */
    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这个类,加载一个图片:

SpringBoot多数据源配置

可以看到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=""),它默认会使用第一个数据源,否则的话,按照你的数据源名称去使用的。