spring-boot整合Hikari实现多数据源starer

项目地址

点击前往

功能展示
  • 引入
   <dependency>
            <groupId>com.paper.tiger</groupId>
            <artifactId>multipart-datasource-spring-star</artifactId>
            <version>1.0.1-SNAPSHOT</version>
        </dependency>
  • 配置
spring.datasource.dynamic.source.master.username=
spring.datasource.dynamic.source.master.password=
spring.datasource.dynamic.source.master.url=
spring.datasource.dynamic.source.master.driver=


spring.datasource.dynamic.source.slave.username=
spring.datasource.dynamic.source.slave.password=
spring.datasource.dynamic.source.slave.url=
spring.datasource.dynamic.source.slave.driver=

+ 动态添加即可
spring.datasource.dynamic.source.*.username=
spring.datasource.dynamic.source.*.password=
spring.datasource.dynamic.source.*.url=
spring.datasource.dynamic.source.*.driver=com.mysql.jdbc.Driver

其中master为必须,其他数据源根据需要动态添加即可

功能实现

引入依赖
 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
            <version>3.4.5</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.44</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.73</version>
        </dependency>
        
配置模块(省略get和set)
public class DataSourceConfig
{
    /**用户名*/
    private String username;
    /**密码*/
    private String password;
    /**连接url*/
    private String url;
    /**驱动*/
    private String driver;
  • 真正引入配置的类
@Order(50)
@Configuration
@ConfigurationProperties(prefix = "spring.datasource.dynamic")
public class DataSourceConfigProperties
{
    private Map<String, DataSourceConfig> source = new LinkedHashMap<>();
}
  • 编写描述文件(可以在写配置的时候自动提示和点击配置跳转)
    在这里插入图片描述
    文件内容
{
  "hints": [],
  "groups": [
    {
      "sourceType": "com.paper.tiger.datasource.common.DataSourceConfigProperties",
      "name": "spring.datasource.dynamic",
      "type": "com.paper.tiger.datasource.common.DataSourceConfigProperties"
    }
  ],
  "properties": [
    {
      "sourceType": "com.paper.tiger.datasource.common.DataSourceConfigProperties",
      "name": "spring.datasource.dynamic.source",
      "type": "java.util.Map<java.lang.String,com.paper.tiger.datasource.common.DataSourceConfig>"
    }
  ]
}
aop 动态拦截
  • 注解类
    该注解可以使用到class上和方法上,value填写数据源的配置名称即可。
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource
{

    /**
     * 默认数据源为 master
     * @return
     */
    String value() default "master";
}

  • 数据库路由
    这里父类其实是一个父类的模版方法模式,我们这里提供的钩子方法determineCurrentLookupKey,在进行数据库选择的时候调用此方法,拿到真正想要操作的数据源。
public class DynamicDataSource extends AbstractRoutingDataSource {


    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    @Override
    protected Object determineCurrentLookupKey() {
        return contextHolder.get();
    }

    public static void setDataSource(String dataSource) {
        contextHolder.set(dataSource);
    }

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

    public static void clear() {
        contextHolder.remove();
    }

}
  • 指定需要拦截的地方
public class DataSourceAnnotionAdvisor extends AbstractPointcutAdvisor
{

    private Advice advice;

    private Pointcut pointcut;


    public DataSourceAnnotionAdvisor(DataSourceAnnotionMethodIntercept advice)
    {
        this.advice = advice;

        // 类或者是方法上面有DataSource注解,就把他们加入到advisor
        Pointcut pointcut1 = new AnnotationMatchingPointcut(DataSource.class, true);
        Pointcut pointcut = AnnotationMatchingPointcut
                .forMethodAnnotation(DataSource.class);

       this.pointcut = new ComposablePointcut(pointcut).union(pointcut1);
    }
    @Override
    public Pointcut getPointcut()
    {
        return pointcut;
    }
    @Override
    public Advice getAdvice()
    {
        return advice;
    }
}
  • 执行数据库切换的aop
public class DataSourceAnnotionMethodIntercept implements MethodInterceptor
{

    private static final Logger log = LoggerFactory.getLogger(DataSourceAnnotionMethodIntercept.class);

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable
    {

        Method method = invocation.getMethod();
        DataSource ds = method.getAnnotation(DataSource.class);

        // 如果方法上面没有 获取类上的 注解
        if (ds == null){
            ds = method.getDeclaringClass().getAnnotation(DataSource.class);
        }

        if (ds == null) {
            DynamicDataSource.setDataSource("master");
            log.debug("set datasource is master" );
        } else {
            DynamicDataSource.setDataSource(ds.value());
            log.debug("set datasource is " + ds.value());
        }
        try {
            return invocation.proceed();
        } finally {
            DynamicDataSource.clear();
            log.debug("clean datasource");
        }
    }
}
  • 自动化配置类
@Configuration
@AutoConfigureBefore(value = DataSourceAutoConfiguration.class)// 在springboot自带配置前加载
@EnableConfigurationProperties(value = DataSourceConfigProperties.class)// 引入配置
public class MultipartDataSourceAutoConfiguration implements InitializingBean
{

    private static final Logger log = LoggerFactory.getLogger(MultipartDataSourceAutoConfiguration.class);


    @Autowired
    private DataSourceConfigProperties dataSourceConfigProperties;

    private DataSource defaultDataSource;

    private Map<Object, Object> targetDataSources = new HashMap<>();

    @Override
    public void afterPropertiesSet()
    {
        // 加载数据源配置,创建对应的数据源
        Map<String, DataSourceConfig> source = dataSourceConfigProperties.getSource();
        source.forEach((sourceName,sourceConfig)->{

            HikariConfig config = new HikariConfig();
            config.setUsername(sourceConfig.getUsername());
            config.setPassword(sourceConfig.getPassword());
            config.setJdbcUrl(sourceConfig.getUrl());
            config.setDriverClassName(sourceConfig.getDriver());
            config.setPoolName(sourceName+"PoolName");
            HikariDataSource dataSource = new HikariDataSource(config);
            if (sourceName.equals("master")){
               setDefaultDataSource(dataSource);
            }

            targetDataSources.put(sourceName,dataSource);

        });
    }

    @Bean
    @ConditionalOnMissingBean
    @Primary
    public DynamicDataSource dynamicDataSource()
    {
        log.info("开始创建动态数据源,创建参数为:defaultDataSource:{},targetDataSources:{}",defaultDataSource,targetDataSources);
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setDefaultTargetDataSource(defaultDataSource);
        dynamicDataSource.setTargetDataSources(targetDataSources);
        return dynamicDataSource;
    }
    @Bean
    @ConditionalOnMissingBean
    public DataSourceAnnotionMethodIntercept dataSourceAnnotionMethodIntercept() {
        DataSourceAnnotionMethodIntercept intercept = new DataSourceAnnotionMethodIntercept();
        return intercept;
    }

    @Bean
    @ConditionalOnMissingBean
    public DataSourceAnnotionAdvisor dataSourceAnnotionAdvisor(@Autowired DataSourceAnnotionMethodIntercept dataSourceAnnotionMethodIntercept) {
        DataSourceAnnotionAdvisor annotionAdvisor = new DataSourceAnnotionAdvisor(dataSourceAnnotionMethodIntercept);
        return annotionAdvisor;
    }

    public Map<Object, Object> getTargetDataSources()
    {
        return targetDataSources;
    }

    public void setTargetDataSources(Map<Object, Object> targetDataSources)
    {
        this.targetDataSources = targetDataSources;
    }

    public DataSource getDefaultDataSource()
    {
        return defaultDataSource;
    }

    public void setDefaultDataSource(DataSource defaultDataSource)
    {
        this.defaultDataSource = defaultDataSource;
    }
}

  • 编写 spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.paper.tiger.datasource.config.MultipartDataSourceAutoConfiguration

完成

这样就完成了一个动态多数据源的配置,添加数据源和删减数据源直接在配置文件修改即可。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值