您的位置 首页 java

SpringBoot自定义多数据源Starter组件

SpringBoot自定义多数据源starter组件

本案例我们使用多数据源封装成一个starter组件,以方便使用多数据源访问数据库的操作

创建一个普通 java 项目,引入SpringBoot相关的依赖

pom.xml

 <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="#34; xmlns:xsi="#34;
         xsi:schemaLocation=" #34;>
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.3</version>
        <relativePath/>
    </parent>
    <groupId>com.gitee.kenewstar.multi.datasource</groupId>
    <artifactId>multi-datasource-spring-boot-starter</artifactId>
    <version>0.0.1</version>
    <name>multi-datasource-spring-boot-starter</name>
    <description>multi- DataSource -spring-boot-starter</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <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>
    </dependencies>

</project>  

创建常量类和注解

 public interface Const {
   
     String  DEFAULT = "default";

    String MULTI_DS = "multiDataSource";

    String CONFIG_PREFIX = " spring .datasource.multi";

}  

数据源注解

 @Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {      

    String value() default Const.DEFAULT;

}  

创建多数据源属性类

主要用于存储SpringBoot配置文件中配置的数据源属性

 @Component
@ConfigurationProperties(prefix = Const.CONFIG_PREFIX)
public class MultiDataSourceProperties {       

    private Map<String, DataSourceProp> dataSourcePropMap;

    public Map<String, DataSourceProp> getDataSourcePropMap() { 
     
        return dataSourcePropMap;
    }

    public  void  setDataSourcePropMap(Map<String, DataSourceProp> dataSourcePropMap) {
   
     
        this.dataSourcePropMap = dataSourcePropMap;
    }
}
public class DataSourceProp  extends  HashMap<String, String> {   
     
}  

创建数据源key的切换工具

主要用于设置当前线程下数据源切换时的数据源唯一标识key,以便获取指定的数据源

 public class DynamicDataSourceHolder {      

     private   static  final  ThreadLocal <String> DATA_SOURCE_THEAD_LOCAL =
            ThreadLocal.withInitial(() -> Const.DEFAULT);
    public static String getDataSource() { 
     
        return DATA_SOURCE_THEAD_LOCAL.get();
    }

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

    public static void remove() {  
     
        DATA_SOURCE_THEAD_LOCAL.remove();
    }

}  

创建多数据源类

创建多数据源类继承AbstractRoutingDataSource类,重写determineCurrentLookupKey()方法,用于获取当前 线程 中的指定的数据源key,通过该key拿到对应的数据源对象

 public class MultiDataSource extends AbstractRoutingDataSource {        

    private static final Logger LOGGER = Logger.getLogger(MultiDataSource.class.getName());

    @Override
    protected Object determineCurrentLookupKey() {  
     
        String key = DynamicDataSourceHolder.getDataSource();
        LOGGER.info("DataSource key ---> " + key);
        return key;
    }

}  

创建多数据源的切面类

切面类主要用于获取被数据与注解指定的方法,拿到其注解中的属性值,再设置到数据源key设置组件中,方便数据源类获取该key

  • 需使用@Order设置切面优先级,否则设置无效
 @Aspect
@Order(100)
public class DynamicDataSourceAdviser {        

    @Pointcut("@ Annotation (com.gitee.kenewstar.multi.datasource.common.DataSource)")
    public void pointcut(){
   
     };

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {       
        try {      
            MethodSignature methodSignature = (MethodSignature) point.getSignature();
            //获取被代理的方法对象
            Method targetMethod = methodSignature.getMethod();
            // 获取数据源注解
            DataSource ds = targetMethod.getAnnotation(DataSource.class);
            if (Objects.nonNull(ds)) {        
                DynamicDataSourceHolder.setDataSource(ds.value());
            }
            return point.proceed();
        } finally {        
            DynamicDataSourceHolder.remove();
        }
    }
}  

创建数据源配置类

 @Configuration
@EnableConfigurationProperties(MultiDataSourceProperties.class)
public class MultiDataSourceConfig {        

    public static final String DS_TYPE = "dsType";

    @Resource
    private MultiDataSourceProperties multiDataSourceProperties;

    private DataSource createDs(DataSourceProp dsProp) {
        
        DataSource dataSource = null;
        try {   
     
            Class<?> dsClass = Class.forName(dsProp.get(DS_TYPE));
            if (DataSource.class.isAssignableFrom(dsClass)) {
       
                dataSource = (DataSource) dsClass.get Constructor ().newInstance();

                DataSource finalDataSource = dataSource;
                ReflectionUtils.doWithFields(dsClass, field -> { 
     
                    field.setAccessible(true);
                    field.set(finalDataSource, dsProp.get(field.getName()));
                }, field -> {  
     
                    if (Objects.equals(dsProp.get(DS_TYPE), field.getName())) {
        
                        return false;
                    }
                    return Objects.nonNull(dsProp.get(field.getName()));
                });

            }

        } catch ( Exception  e) {   
     
            throw new RuntimeException(e);
        }
        return dataSource;
    }

    @Bean(Const.MULTI_DS)
    @Primary
    public DataSource multiDataSource() {
   
     
        MultiDataSource multiDataSource = new MultiDataSource();

         Map <Object, Object> dataSourceMap = new HashMap<>(multiDataSourceProperties.getDataSourcePropMap().size());
        Map<String, DataSourceProp> dataSourcePropMap = multiDataSourceProperties.getDataSourcePropMap();
        dataSourcePropMap.forEach((lookupKey,dsProp) -> {  
     
            dataSourceMap.put(lookupKey, createDs(dsProp));
        });

        multiDataSource.setTargetDataSources(dataSourceMap);
        multiDataSource.setDefaultTargetDataSource(dataSourceMap.get(Const.DEFAULT));
        return multiDataSource;
    }

    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager(
            @Qualifier(Const.MULTI_DS) DataSource multiDataSource) {  
     
        DataSourceTransactionManager tx = new DataSourceTransactionManager();
        tx.setDataSource(multiDataSource);
        return tx;
    }

    @Bean
    public DynamicDataSourceAdviser dynamicDataSourceAdviser() {
        
        return new DynamicDataSourceAdviser();
    }

}  

配置spring.factories文件

在resources目录下创建META-INF目录,在该目录创建spring.factories

文件内容如下:

设置key为开启自动配置的注解全路径名,后面的value值为配置类全路径名,本starter组件中为数据源配置类,如有多个配置类,则以逗号分隔,以反斜杆表示忽略换行

 org.springframework.boot.autoconfigure.EnableAutoConfiguration=
com.gitee.kenewstar.multi.datasource.config.MultiDataSourceConfig  

这样我们封装的一个简单的多数据源starter组件就完成了,只需进行 maven 打包即可在本地使用

maven命令: mvn clean install

使用示例:

引入打包后的依赖

 <dependency>
    <groupId>com.gitee.kenewstar.multi.datasource</groupId>
    <artifactId>multi-datasource-spring-boot-starter</artifactId>
    <version>0.0.1</version>
</dependency>  

修改SpringBoot全局配置文件

default为默认数据源,必须配置, master为可选数据源,名称可自定义。

数据源的属性名称为对应的dsType数据源类型的属性字段

 spring:
  datasource:
    multi:
      data-source-prop-map:
        default:
          dsType: com.zaxxer.hikari.HikariDataSource
          jdbcUrl: jdbc:mysql://localhost:3306/test
          username:  root 
          password: kenewstar
        master:
          dsType: com.zaxxer.hikari.HikariDataSource
          jdbcUrl: jdbc:mysql://localhost:3306/test2
          username: root
          password: kenewstar  

使用数据源

直接在指定的方法上添加@DataSource注解即可,注解的默认值为default,数据源的切换通过注解的值进行切换。 值为application.yml中配置的default,master等

 @Service
public class PersonService {     

    @Resource
    private PersonMapper personMapper;

    @DataSource("master")
    @Transactional(rollbackFor = Exception.class)
    public void insertPerson() {
     
        personMapper.insert(new Person(null, "kk", 12));
        personMapper.insert(new Person(null, "kk", 12));
    }

}  
 @Service
public class PersonService {       

    @Resource
    private PersonMapper personMapper;

    @DataSource("master")
    @Transactional(rollbackFor = Exception.class)
    public void insertPerson() {
   
     
        personMapper.insert(new Person(null, "kk", 12));
        personMapper.insert(new Person(null, "kk", 12));
    }
}
  

多数据源starter组件源码地址:

来源:

文章来源:智云一二三科技

文章标题:SpringBoot自定义多数据源Starter组件

文章地址:https://www.zhihuclub.com/193044.shtml

关于作者: 智云科技

热门文章

网站地图