From 00c53734b3dbcfa1d1f6ce3b2075759980606db9 Mon Sep 17 00:00:00 2001 From: iizmg Date: Sun, 26 Apr 2020 10:01:05 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + udf-starter/pom.xml | 4 +- .../udf-starter-datasource-dynamic/pom.xml | 24 ++++++ .../dynamic/DataSourceThreadLocal.java | 14 ++++ .../datasource/dynamic/DbContextHolder.java | 51 ++++++++++++ .../starter/datasource/dynamic/DbSwitch.java | 10 +++ .../datasource/dynamic/DbSwitchAspect.java | 51 ++++++++++++ .../datasource/dynamic/DynamicDataSource.java | 77 +++++++++++++++++++ .../DynamicDataSourceConfiguration.java | 37 +++++++++ .../dynamic/DynamicDataSourceProperties.java | 36 +++++++++ 10 files changed, 304 insertions(+), 2 deletions(-) create mode 100644 udf-starter/udf-starter-datasource-dynamic/pom.xml create mode 100644 udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DataSourceThreadLocal.java create mode 100644 udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DbContextHolder.java create mode 100644 udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DbSwitch.java create mode 100644 udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DbSwitchAspect.java create mode 100644 udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DynamicDataSource.java create mode 100644 udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DynamicDataSourceConfiguration.java create mode 100644 udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DynamicDataSourceProperties.java diff --git a/.gitignore b/.gitignore index cce4806..abc5673 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ /udf-starter/udf-starter.iml /udf-starter/udf-starter-core/target/ /udf-starter/udf-starter-core/udf-starter-core.iml +/udf-starter/udf-starter-datasource-dynamic/udf-starter-datasource-dynamic.iml +/udf-starter/udf-starter-datasource-dynamic/target/ diff --git a/udf-starter/pom.xml b/udf-starter/pom.xml index fa3c60a..9e2d4f2 100644 --- a/udf-starter/pom.xml +++ b/udf-starter/pom.xml @@ -11,7 +11,7 @@ https://itkk.org/ - 1.0.5 + 1.0.0 2.2.5.RELEASE UTF-8 1.8 @@ -30,7 +30,7 @@ udf-starter-core - + udf-starter-datasource-dynamic diff --git a/udf-starter/udf-starter-datasource-dynamic/pom.xml b/udf-starter/udf-starter-datasource-dynamic/pom.xml new file mode 100644 index 0000000..debd3e8 --- /dev/null +++ b/udf-starter/udf-starter-datasource-dynamic/pom.xml @@ -0,0 +1,24 @@ + + + 4.0.0 + + udf-starter-datasource-dynamic + udf-starter-datasource-dynamic + jar + https://www.itkk.org/ + + + org.itkk.udf + udf-starter + 1.0.0 + + + + + + org.itkk.udf + udf-starter-core + + + diff --git a/udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DataSourceThreadLocal.java b/udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DataSourceThreadLocal.java new file mode 100644 index 0000000..70bc9cb --- /dev/null +++ b/udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DataSourceThreadLocal.java @@ -0,0 +1,14 @@ +package org.itkk.udf.starter.datasource.dynamic; + +/** + * 描述 : 数据源本地线程 + * + * @author wangkang + */ +public class DataSourceThreadLocal extends ThreadLocal { + @Override + public void remove() { + super.remove(); + this.initialValue(); + } +} diff --git a/udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DbContextHolder.java b/udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DbContextHolder.java new file mode 100644 index 0000000..ce71d1d --- /dev/null +++ b/udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DbContextHolder.java @@ -0,0 +1,51 @@ +package org.itkk.udf.starter.datasource.dynamic; + +/** + * 描述 : DbContextHolder + *

+ * 多个登入用户可能需要同时切换数据源,所以这里需要写一个线程安全的ThreadLocal 用户切换数据源只要在线程中使用 + * + * @author wangkang + */ +public class DbContextHolder { + + /** + * 描述 : 本地线程 + */ + private static final ThreadLocal CONTEXTHOLDER = new DataSourceThreadLocal(); + + /** + * 描述 : 私有构造函数 + */ + private DbContextHolder() { + + } + + /** + * 描述 : 设置数据源代码 + * + * @param code 数据源代码 + */ + public static void setDataSourceCode(String code) { + CONTEXTHOLDER.set(code); + } + + /** + * 描述 : 获取数据源代码 + * + * @return 数据源代码 + */ + public static String getDataSourceCode() { + return CONTEXTHOLDER.get(); + } + + /** + *

+ * Description: 清理数据源代码 + *

+ */ + public static void clearDataSourceCode() { + CONTEXTHOLDER.remove(); + } + +} diff --git a/udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DbSwitch.java b/udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DbSwitch.java new file mode 100644 index 0000000..0238774 --- /dev/null +++ b/udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DbSwitch.java @@ -0,0 +1,10 @@ +package org.itkk.udf.starter.datasource.dynamic; + +import java.lang.annotation.*; + +@Documented +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface DbSwitch { + String value(); +} \ No newline at end of file diff --git a/udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DbSwitchAspect.java b/udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DbSwitchAspect.java new file mode 100644 index 0000000..986ad49 --- /dev/null +++ b/udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DbSwitchAspect.java @@ -0,0 +1,51 @@ +package org.itkk.udf.starter.datasource.dynamic; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +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.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; + +@Aspect +@Component +@Slf4j +public class DbSwitchAspect { + /** + * 描述 : dbSwitchAspect + */ + @Pointcut("@annotation(org.itkk.udf.starter.datasource.dynamic.DbSwitch)") + public void pointCut() { + + } + + /** + * 环绕通知 + * @param pjd pjd + * @return Object + * @throws Throwable Throwable + */ + @Around("pointCut()") + public Object around(ProceedingJoinPoint pjd) throws Throwable { + try { + Signature signature = pjd.getSignature(); + MethodSignature methodSignature = (MethodSignature) signature; + Method method = methodSignature.getMethod(); + if (method.isAnnotationPresent(DbSwitch.class)) { + DbSwitch dbSwitch = method.getAnnotation(DbSwitch.class); + String code = dbSwitch.value(); + if (StringUtils.isNotBlank(code)) { + DbContextHolder.setDataSourceCode(code); + } + } + return pjd.proceed(); + } finally { + DbContextHolder.clearDataSourceCode(); + } + } +} diff --git a/udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DynamicDataSource.java b/udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DynamicDataSource.java new file mode 100644 index 0000000..dc565c6 --- /dev/null +++ b/udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DynamicDataSource.java @@ -0,0 +1,77 @@ +package org.itkk.udf.starter.datasource.dynamic; + +import org.itkk.udf.starter.core.CoreUtil; +import org.itkk.udf.starter.core.datasource.DataSourceMeta; +import org.itkk.udf.starter.core.datasource.IBuildDataSource; +import org.itkk.udf.starter.core.exception.SystemRuntimeException; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +import javax.sql.DataSource; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 动态数据源 + */ +@Slf4j +public class DynamicDataSource extends AbstractRoutingDataSource { + + /** + * 描述 : 目标数据库 + */ + private static Map dyanmictargetDataSources = new ConcurrentHashMap<>(); + + /** + * 描述 : 动态数据源参数 + */ + @Autowired + private DynamicDataSourceProperties dynamicDataSourceProperties; + + /** + * 描述 : 数据源构建 + */ + @Autowired + private IBuildDataSource buildDataSource; + + @Override + protected Object determineCurrentLookupKey() { + //拿到code + String dataSourceCode = DbContextHolder.getDataSourceCode(); + //判断code是否为空,如果为空,则设置为默认的数据源代码 + if (StringUtils.isBlank(dataSourceCode)) { + dataSourceCode = dynamicDataSourceProperties.getDefaultCode(); + if (StringUtils.isBlank(dataSourceCode)) { + throw new SystemRuntimeException("you do not specify a data source , current dataSourceCode is null"); + } + } + //拿到数据源属性,并且判断数据源属性是否存在,以及是否可用 + DataSourceMeta dataSourceProperties = dynamicDataSourceProperties.getMap().get(dataSourceCode); + if (dataSourceProperties != null && dataSourceProperties.getEnable()) { + Object obj = DynamicDataSource.dyanmictargetDataSources.get(dataSourceCode); + if (obj == null) { + DataSource dataSource = buildDataSource.build(dataSourceProperties); + if (dataSource != null) { + DynamicDataSource.dyanmictargetDataSources.put(dataSourceCode, dataSource); + super.setTargetDataSources(DynamicDataSource.dyanmictargetDataSources); + super.afterPropertiesSet(); + } + } + } else { + //如果在配置文件中,选择的数据源不存在,则也清理掉数据源缓存(可能是动态删除) + if (DynamicDataSource.dyanmictargetDataSources.containsKey(dataSourceCode)) { + DynamicDataSource.dyanmictargetDataSources.remove(dataSourceCode); + super.setTargetDataSources(DynamicDataSource.dyanmictargetDataSources); + super.afterPropertiesSet(); + } + throw new SystemRuntimeException("dataSourceCode : " + dataSourceCode + " not exist or not enable"); + } + if(log.isDebugEnabled()) { + log.debug("{} switch to the Data Source : {}", CoreUtil.getTraceId(), dataSourceCode); + } + return dataSourceCode; + } + +} diff --git a/udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DynamicDataSourceConfiguration.java b/udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DynamicDataSourceConfiguration.java new file mode 100644 index 0000000..c19a787 --- /dev/null +++ b/udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DynamicDataSourceConfiguration.java @@ -0,0 +1,37 @@ +/** + * DruidDataSourceConfig.java + * Created at 2016-11-02 + * Created by wangkang + * Copyright (C) 2016 egridcloud.com, All rights reserved. + */ +package org.itkk.udf.starter.datasource.dynamic; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +import javax.sql.DataSource; +import java.util.HashMap; + +/** + * 描述 : DynamicDataSourceConfig + * + * @author wangkang + */ +@Configuration +public class DynamicDataSourceConfiguration { + + /** + * 构造数据源 + * + * @return DataSource + */ + @Bean + @Primary + public DataSource dataSource() { + DynamicDataSource dynamicDataSource = new DynamicDataSource(); + dynamicDataSource.setTargetDataSources(new HashMap<>()); + return dynamicDataSource; + } + +} diff --git a/udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DynamicDataSourceProperties.java b/udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DynamicDataSourceProperties.java new file mode 100644 index 0000000..8151cfe --- /dev/null +++ b/udf-starter/udf-starter-datasource-dynamic/src/main/java/org/itkk/udf/starter/datasource/dynamic/DynamicDataSourceProperties.java @@ -0,0 +1,36 @@ +package org.itkk.udf.starter.datasource.dynamic; + +import org.itkk.udf.starter.core.datasource.DataSourceMeta; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; +import org.springframework.validation.annotation.Validated; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.util.Map; + +@Component +@ConfigurationProperties(prefix = DynamicDataSourceProperties.DYNAMIC_DATASOURCE_PROPERTIES_PRE_FIX) +@Validated +@Data +public class DynamicDataSourceProperties { + + /** + * 配置文件前缀 + */ + protected static final String DYNAMIC_DATASOURCE_PROPERTIES_PRE_FIX = "org.itkk.udf.starter.datasource.dynamic.properties"; + + /** + * 默认数据代码 + */ + @NotBlank(message = DYNAMIC_DATASOURCE_PROPERTIES_PRE_FIX + ".defaultCode不能为空") + private String defaultCode; + + /** + * 数据源配置 + */ + @NotNull(message = DYNAMIC_DATASOURCE_PROPERTIES_PRE_FIX + ".map不能为空") + private Map map; + +}