SpringBoot 自定义多数据源 starter 组件

梦里梦外; 2023-09-23 15:00 172阅读 0赞

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

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

pom.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>2.6.3</version>
  9. <relativePath/>
  10. </parent>
  11. <groupId>com.gitee.kenewstar.multi.datasource</groupId>
  12. <artifactId>multi-datasource-spring-boot-starter</artifactId>
  13. <version>0.0.1</version>
  14. <name>multi-datasource-spring-boot-starter</name>
  15. <description>multi-datasource-spring-boot-starter</description>
  16. <properties>
  17. <java.version>1.8</java.version>
  18. </properties>
  19. <dependencies>
  20. <dependency>
  21. <groupId>org.springframework.boot</groupId>
  22. <artifactId>spring-boot-starter</artifactId>
  23. </dependency>
  24. <dependency>
  25. <groupId>org.springframework.boot</groupId>
  26. <artifactId>spring-boot-configuration-processor</artifactId>
  27. <optional>true</optional>
  28. </dependency>
  29. <dependency>
  30. <groupId>org.springframework.boot</groupId>
  31. <artifactId>spring-boot-starter-jdbc</artifactId>
  32. </dependency>
  33. <dependency>
  34. <groupId>org.springframework.boot</groupId>
  35. <artifactId>spring-boot-starter-aop</artifactId>
  36. </dependency>
  37. </dependencies>
  38. </project>

创建常量类和注解

  1. public interface Const {
  2. String DEFAULT = "default";
  3. String MULTI_DS = "multiDataSource";
  4. String CONFIG_PREFIX = "spring.datasource.multi";
  5. }

数据源注解

  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface DataSource {
  5. String value() default Const.DEFAULT;
  6. }

创建多数据源属性类

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

  1. @Component
  2. @ConfigurationProperties(prefix = Const.CONFIG_PREFIX)
  3. public class MultiDataSourceProperties {
  4. private Map<String, DataSourceProp> dataSourcePropMap;
  5. public Map<String, DataSourceProp> getDataSourcePropMap() {
  6. return dataSourcePropMap;
  7. }
  8. public void setDataSourcePropMap(Map<String, DataSourceProp> dataSourcePropMap) {
  9. this.dataSourcePropMap = dataSourcePropMap;
  10. }
  11. }
  12. public class DataSourceProp extends HashMap<String, String> {
  13. }

创建数据源key的切换工具

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

  1. public class DynamicDataSourceHolder {
  2. private static final ThreadLocal<String> DATA_SOURCE_THEAD_LOCAL =
  3. ThreadLocal.withInitial(() -> Const.DEFAULT);
  4. public static String getDataSource() {
  5. return DATA_SOURCE_THEAD_LOCAL.get();
  6. }
  7. public static void setDataSource(String dataSource) {
  8. DATA_SOURCE_THEAD_LOCAL.set(dataSource);
  9. }
  10. public static void remove() {
  11. DATA_SOURCE_THEAD_LOCAL.remove();
  12. }
  13. }

创建多数据源类

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

  1. public class MultiDataSource extends AbstractRoutingDataSource {
  2. private static final Logger LOGGER = Logger.getLogger(MultiDataSource.class.getName());
  3. @Override
  4. protected Object determineCurrentLookupKey() {
  5. String key = DynamicDataSourceHolder.getDataSource();
  6. LOGGER.info("DataSource key ---> " + key);
  7. return key;
  8. }
  9. }

创建多数据源的切面类

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

需使用@Order设置切面优先级,否则设置无效

  1. @Aspect
  2. @Order(100)
  3. public class DynamicDataSourceAdviser {
  4. @Pointcut("@annotation(com.gitee.kenewstar.multi.datasource.common.DataSource)")
  5. public void pointcut(){};
  6. @Around("pointcut()")
  7. public Object around(ProceedingJoinPoint point) throws Throwable {
  8. try {
  9. MethodSignature methodSignature = (MethodSignature) point.getSignature();
  10. //获取被代理的方法对象
  11. Method targetMethod = methodSignature.getMethod();
  12. // 获取数据源注解
  13. DataSource ds = targetMethod.getAnnotation(DataSource.class);
  14. if (Objects.nonNull(ds)) {
  15. DynamicDataSourceHolder.setDataSource(ds.value());
  16. }
  17. return point.proceed();
  18. } finally {
  19. DynamicDataSourceHolder.remove();
  20. }
  21. }
  22. }

创建数据源配置类

  1. @Configuration
  2. @EnableConfigurationProperties(MultiDataSourceProperties.class)
  3. public class MultiDataSourceConfig {
  4. public static final String DS_TYPE = "dsType";
  5. @Resource
  6. private MultiDataSourceProperties multiDataSourceProperties;
  7. private DataSource createDs(DataSourceProp dsProp) {
  8. DataSource dataSource = null;
  9. try {
  10. Class<?> dsClass = Class.forName(dsProp.get(DS_TYPE));
  11. if (DataSource.class.isAssignableFrom(dsClass)) {
  12. dataSource = (DataSource) dsClass.getConstructor().newInstance();
  13. DataSource finalDataSource = dataSource;
  14. ReflectionUtils.doWithFields(dsClass, field -> {
  15. field.setAccessible(true);
  16. field.set(finalDataSource, dsProp.get(field.getName()));
  17. }, field -> {
  18. if (Objects.equals(dsProp.get(DS_TYPE), field.getName())) {
  19. return false;
  20. }
  21. return Objects.nonNull(dsProp.get(field.getName()));
  22. });
  23. }
  24. } catch (Exception e) {
  25. throw new RuntimeException(e);
  26. }
  27. return dataSource;
  28. }
  29. @Bean(Const.MULTI_DS)
  30. @Primary
  31. public DataSource multiDataSource() {
  32. MultiDataSource multiDataSource = new MultiDataSource();
  33. Map<Object, Object> dataSourceMap = new HashMap<>(multiDataSourceProperties.getDataSourcePropMap().size());
  34. Map<String, DataSourceProp> dataSourcePropMap = multiDataSourceProperties.getDataSourcePropMap();
  35. dataSourcePropMap.forEach((lookupKey,dsProp) -> {
  36. dataSourceMap.put(lookupKey, createDs(dsProp));
  37. });
  38. multiDataSource.setTargetDataSources(dataSourceMap);
  39. multiDataSource.setDefaultTargetDataSource(dataSourceMap.get(Const.DEFAULT));
  40. return multiDataSource;
  41. }
  42. @Bean
  43. public DataSourceTransactionManager dataSourceTransactionManager(
  44. @Qualifier(Const.MULTI_DS) DataSource multiDataSource) {
  45. DataSourceTransactionManager tx = new DataSourceTransactionManager();
  46. tx.setDataSource(multiDataSource);
  47. return tx;
  48. }
  49. @Bean
  50. public DynamicDataSourceAdviser dynamicDataSourceAdviser() {
  51. return new DynamicDataSourceAdviser();
  52. }
  53. }

配置spring.factories文件

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

849f01b31218e854256b538cb4182f46.png

文件内容如下:

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

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

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

maven命令:mvn clean install

使用示例

引入打包后的依赖

  1. <dependency>
  2. <groupId>com.gitee.kenewstar.multi.datasource</groupId>
  3. <artifactId>multi-datasource-spring-boot-starter</artifactId>
  4. <version>0.0.1</version>
  5. </dependency>

修改SpringBoot全局配置文件

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

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

  1. spring:
  2. datasource:
  3. multi:
  4. data-source-prop-map:
  5. default:
  6. dsType: com.zaxxer.hikari.HikariDataSource
  7. jdbcUrl: jdbc:mysql://localhost:3306/test
  8. username: root
  9. password: kenewstar
  10. master:
  11. dsType: com.zaxxer.hikari.HikariDataSource
  12. jdbcUrl: jdbc:mysql://localhost:3306/test2
  13. username: root
  14. password: kenewstar

使用数据源

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

  1. @Service
  2. public class PersonService {
  3. @Resource
  4. private PersonMapper personMapper;
  5. @DataSource("master")
  6. @Transactional(rollbackFor = Exception.class)
  7. public void insertPerson() {
  8. personMapper.insert(new Person(null, "kk", 12));
  9. personMapper.insert(new Person(null, "kk", 12));
  10. }
  11. }
  12. @Service
  13. public class PersonService {
  14. @Resource
  15. private PersonMapper personMapper;
  16. @DataSource("master")
  17. @Transactional(rollbackFor = Exception.class)
  18. public void insertPerson() {
  19. personMapper.insert(new Person(null, "kk", 12));
  20. personMapper.insert(new Person(null, "kk", 12));
  21. }
  22. }

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

https://gitee.com/kenewstar/multi-datasource-spring-boot-starter

发表评论

表情:
评论列表 (有 0 条评论,172人围观)

还没有评论,来说两句吧...

相关阅读