前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

其实这里可以使用很多库的,但是我这里却只用了2个库,一个库one作为默认库,另一个库作为指定库,当然one库也可以指定。先提供给类注解,如果方法上有注解则先看方法里面的

1、自定义注解

package com.hlj.many.datasourse.data;

/**
 * @Description  自定义注解
 * @Date   2018/4/24 下午6:09.
 */

public enum DataSource {

    ONE("one"),
    TWO("two"),
    ;

    private String name;

    DataSource(String name){
        this.name = name;
    }

    public String getName(){
        return this.name;
    }

}


2、配置数据源信息

server.port=8888

########################################################
### jpa
########################################################
spring.jpa.database=MYSQL
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto =update
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect



# 主数据源
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url=jdbc:mysql://localhost:3306/com_hlj_many_database-one
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver



# 其他数据源
hlj.datasource.names=one,two
#datasource one
hlj.datasource.one.type=com.alibaba.druid.pool.DruidDataSource
hlj.datasource.one.url=jdbc:mysql://localhost:3306/com_hlj_many_database-one
hlj.datasource.one.username=root
hlj.datasource.one.password=123456
hlj.datasource.one.driver-class-name=com.mysql.jdbc.Driver

#datasource two
hlj.datasource.two.type=com.alibaba.druid.pool.DruidDataSource
hlj.datasource.two.url=jdbc:mysql://localhost:3306/com_hlj_many_database-two
hlj.datasource.two.username=root
hlj.datasource.two.password=123456
hlj.datasource.two.driver-class-name=com.mysql.jdbc.Driver


3、多个数据源的存取单例模式

package com.hlj.many.datasourse.data;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

/**
 * @Description  类似于单例模式数据源
 * @Date   2018/4/24 下午6:10.
 */

public class DynamicDataSourceContextHolder {

    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
    private static final ThreadLocal<Stack<String>> stackHolder = new ThreadLocal<>();
    public static List<String> dataSourceIds = new ArrayList<>();

    public static void setDataSource(String dataSourceType) {
        // 保存当前线程上一次的数据源,用于清除时保存上次的上线文
        if (contextHolder.get() != null){
            Stack<String> stack = stackHolder.get();
            if (stack == null){
                stack = new Stack<>();
                stackHolder.set(stack);
            }
            stack.push(contextHolder.get());
        }
        contextHolder.set(dataSourceType);
    }

    public static String getDataSourceType() {
        return contextHolder.get();
    }

    public static void clearDataSource() {
        // 清除本次的数据源
        contextHolder.remove();
        // 从栈中拿出上一次的数据源
        Stack<String> stack = stackHolder.get();
        if (stack != null){
            if (stack.isEmpty()){
                stackHolder.remove();
            }else{
                String pop = stack.pop();
                contextHolder.set(pop);
                if (stack.isEmpty()){
                    stackHolder.remove();
                }
            }
        }
    }

    /**
     * 判断指定DataSrouce当前是否存在
     * @param dataSourceName
     * @return
     */
    public static boolean containsDataSource(String dataSourceName){
        return dataSourceIds.contains(dataSourceName);
    }

}


4、获取动态数据源

package com.hlj.many.datasourse.data;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * @Description  获取动态数据源
 * @Date   2018/4/24 下午6:11.
 */

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}


5、数据源的设置和绑定,提供给springboot启动的时候进行导入和加载

package com.hlj.many.datasourse.data;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.bind.RelaxedDataBinder;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by fengchuanbo on 2017/5/8.
 */
@Slf4j
public class DynamicDataSourceRegistrar implements ImportBeanDefinitionRegistrar,EnvironmentAware {

    private ConversionService conversionService = new DefaultConversionService();
    private PropertyValues dataSourcePropertyValues;

    // 如配置文件中未指定数据源类型,使用该默认值
    private static final Object DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource";

    // 数据源
    private DataSource defaultDataSource;
    private Map<String, DataSource> customDataSources = new HashMap<>();
    private static final String DEFAULT_DATASOURCE_NAME = com.hlj.many.datasourse.data.DataSource.ONE.getName();

    @Override
    public void setEnvironment(Environment environment) {
        initDefaultDataSource(environment);
        initCustomDataSources(environment);
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        // 将主数据源添加到更多数据源中
        targetDataSources.put(DEFAULT_DATASOURCE_NAME, defaultDataSource);
        DynamicDataSourceContextHolder.dataSourceIds.add(DEFAULT_DATASOURCE_NAME);
        // 添加更多数据源
        targetDataSources.putAll(customDataSources);
        for (String key : customDataSources.keySet()) {
            DynamicDataSourceContextHolder.dataSourceIds.add(key);
        }

        // 创建DynamicDataSource
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(DynamicDataSource.class);
        beanDefinition.setSynthetic(true);
        MutablePropertyValues mpv = beanDefinition.getPropertyValues();
        mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
        mpv.addPropertyValue("targetDataSources", targetDataSources);
        registry.registerBeanDefinition(DEFAULT_DATASOURCE_NAME, beanDefinition);

        if (log.isInfoEnabled()) {
            log.info("Dynamic DataSource Registry");
        }
    }

    /**
     * 初始化主数据源
     *
     * @author SHANHY
     * @create 2016年1月24日
     */
    private void initDefaultDataSource(Environment env) {
        // 读取主数据源
        RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "spring.datasource.");
        Map<String, Object> dsMap = new HashMap<>();
        dsMap.put("type", propertyResolver.getProperty("type"));
        dsMap.put("driver-class-name", propertyResolver.getProperty("driver-class-name"));
        dsMap.put("url", propertyResolver.getProperty("url"));
        dsMap.put("username", propertyResolver.getProperty("username"));
        dsMap.put("password", propertyResolver.getProperty("password"));

        defaultDataSource = buildDataSource(dsMap);
        dataBinder(defaultDataSource, env,"spring.datasource");
    }

    /**
     * 为DataSource绑定更多数据
     * @param dataSource
     * @param env
     */
    private void dataBinder(DataSource dataSource, Environment env, String prefix){
        RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
        //dataBinder.setValidator(new LocalValidatorFactory().run(this.applicationContext));
        dataBinder.setConversionService(conversionService);
        dataBinder.setIgnoreNestedProperties(false);//false
        dataBinder.setIgnoreInvalidFields(false);//false
        dataBinder.setIgnoreUnknownFields(true);//true
        if(dataSourcePropertyValues == null){
            Map<String, Object> rpr = new RelaxedPropertyResolver(env, prefix).getSubProperties(".");
            Map<String, Object> values = new HashMap<>(rpr);
            // 排除已经设置的属性
            values.remove("type");
            values.remove("driver-class-name");
            values.remove("url");
            values.remove("username");
            values.remove("password");
            dataSourcePropertyValues = new MutablePropertyValues(values);
        }
        dataBinder.bind(dataSourcePropertyValues);
    }

    /**
     * 初始化更多数据源
     */
    private void initCustomDataSources(Environment env) {
        // 读取配置文件获取更多数据源,也可以通过defaultDataSource读取数据库获取更多数据源
        RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "hlj.datasource.");
        String dsPrefixs = propertyResolver.getProperty("names");
        if (StringUtils.isEmpty(dsPrefixs)) return;
        for (String dsPrefix : dsPrefixs.split(",")) {// 多个数据源
            Map<String, Object> dsMap = propertyResolver.getSubProperties(dsPrefix + ".");
            DataSource ds = buildDataSource(dsMap);
            customDataSources.put(dsPrefix, ds);
            dataBinder(ds, env,"hlj.datasource." + dsPrefix);
        }
    }

    /**
     * 创建DataSource
     * @return
     */
    public DataSource buildDataSource(Map<String, Object> dsMap) {
        try {
            Object type = dsMap.get("type");
            if (type == null)
                type = DATASOURCE_TYPE_DEFAULT;// 默认DataSource

            Class<? extends DataSource> dataSourceType;
            dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);

            String driverClassName = dsMap.get("driver-class-name").toString();
            String url = dsMap.get("url").toString();
            String username = dsMap.get("username").toString();
            String password = dsMap.get("password").toString();

            DataSourceBuilder factory = DataSourceBuilder.create()
                    .driverClassName(driverClassName)
                    .url(url).username(username)
                    .password(password)
                    .type(dataSourceType);
            return factory.build();
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
}


6、springBoot启动,扫描jpa和导入DynamicDataSourceRegistrar.class

package com.hlj.many.datasourse;

import com.hlj.many.datasourse.data.DynamicDataSourceRegistrar;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = {"com.hlj.many.datasourse.dataresource.dao"})
@EntityScan(basePackages = "com.hlj.many.datasourse.dataresource.dao")
@Import({DynamicDataSourceRegistrar.class}) //导入数据库
public class ComHljManyDatasourceApplication {

	public static void main(String[] args) {
		SpringApplication.run(ComHljManyDatasourceApplication.class, args);
	}
}

7、添加mybatis

package com.hlj.many.datasourse.config;

import org.apache.commons.lang3.ArrayUtils;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;

import javax.sql.DataSource;
import java.io.IOException;



/**
 * @Description
 * @Author 
 * @Date 2018/4/24  下午5:44.
 */
@Configuration
public class MybatisConfig {

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer configurer = new MapperScannerConfigurer();
        configurer.setBasePackage("com.hlj.many.datasourse.dataresource.dao.*");
        return configurer;
    }

    @Bean
    public SqlSessionFactoryBean sessionFactory(DataSource dataSource, ApplicationContext applicationContext) throws IOException {
        SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource);
        sessionFactoryBean.setConfigLocation(applicationContext.getResource("classpath:mybatis.xml"));

        Resource[] resources = ArrayUtils.addAll(
                applicationContext.getResources("classpath*:com/hlj/many/datasourse/dataresource/dao/**/*.xml")
        );
        sessionFactoryBean.setMapperLocations(resources);
        return sessionFactoryBean;
    }
}

8、aop拦截

package com.hlj.many.datasourse.aop;


import com.hlj.many.datasourse.data.DataSource;
import com.hlj.many.datasourse.data.DynamicDataSourceContextHolder;
import com.hlj.many.datasourse.data.TargetDataSource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
 * Created by fengchuanbo on 2017/5/8.
 */
@Aspect
@Order(0)// 保证该AOP在@Transactional之前执行
@Component
@Slf4j
public class DynamicDataSourceAspect {

    @Around("@annotation(com.hlj.many.datasourse.data.TargetDataSource) || @within(com.hlj.many.datasourse.data.TargetDataSource)")
    public Object invoke(ProceedingJoinPoint point) throws Throwable {
        try {
            String dataSource = null;
            Signature signature = point.getSignature();
            // 默认使用方法上的数据源
            if (signature instanceof MethodSignature) {
                Method method = ((MethodSignature) signature).getMethod();
                TargetDataSource annotation = method.getAnnotation(TargetDataSource.class);
                if (annotation != null){
                    dataSource = annotation.value().getName();
                }
            }
            // 方法没有标注数据源,使用类标注的
            if (dataSource == null){
                Annotation annotation = signature.getDeclaringType().getAnnotation(TargetDataSource.class);
                if (annotation instanceof TargetDataSource){
                    dataSource = ((TargetDataSource)annotation).value().getName();
                }
            }
            // 如果都没有,则使用默认数据源
            if (dataSource == null){
                dataSource = DataSource.ONE.getName();
            }
            if (!DynamicDataSourceContextHolder.containsDataSource(dataSource)) {
                log.error("数据源[{}]不存在,使用默认数据源 > {}", dataSource, point.getSignature());
            } else {
                if (log.isDebugEnabled()) {
                    log.debug("Use DataSource : {} > {}", dataSource, point.getSignature());
                }
                DynamicDataSourceContextHolder.setDataSource(dataSource);
            }

            return point.proceed();
        }catch (Exception e){
            throw new RuntimeException(e);
        } finally {
            DynamicDataSourceContextHolder.clearDataSource();
        }
    }

}

测试方法也挺多的,如果需要请直接下载代码

下载代码

ContactAuthor