From 78a8bb1d7ffdd87933e8422c306ebb032ca10f48 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 07:09:18 +0000 Subject: [PATCH 1/4] Initial plan From bdda1f4f5f599f90ca72d69ff15a0619f27d2182 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 07:15:51 +0000 Subject: [PATCH 2/4] Optimize performance: cache lists, fix overflow, reduce reflection Co-authored-by: huayanYu <16700837+huayanYu@users.noreply.github.com> --- .../toolkit/ConfigMergeCreator.java | 25 ++++++++++++++++--- .../datasource/DynamicRoutingDataSource.java | 12 ++++++--- .../datasource/ds/GroupDataSource.java | 20 ++++++++++++--- .../LoadBalanceDynamicDataSourceStrategy.java | 5 +++- 4 files changed, 51 insertions(+), 11 deletions(-) diff --git a/dynamic-datasource-creator/src/main/java/com/baomidou/dynamic/datasource/toolkit/ConfigMergeCreator.java b/dynamic-datasource-creator/src/main/java/com/baomidou/dynamic/datasource/toolkit/ConfigMergeCreator.java index 058a7290..2e1e0f41 100644 --- a/dynamic-datasource-creator/src/main/java/com/baomidou/dynamic/datasource/toolkit/ConfigMergeCreator.java +++ b/dynamic-datasource-creator/src/main/java/com/baomidou/dynamic/datasource/toolkit/ConfigMergeCreator.java @@ -35,7 +35,6 @@ * @author TaoYu */ @Slf4j -@AllArgsConstructor public class ConfigMergeCreator { private final String configName; @@ -43,6 +42,17 @@ public class ConfigMergeCreator { private final Class configClazz; private final Class targetClazz; + + /** + * Cached BeanInfo to avoid repeated introspection for better performance + */ + private volatile BeanInfo cachedBeanInfo; + + public ConfigMergeCreator(String configName, Class configClazz, Class targetClazz) { + this.configName = configName; + this.configClazz = configClazz; + this.targetClazz = targetClazz; + } @SneakyThrows @SuppressWarnings("unchecked") @@ -51,8 +61,17 @@ public T create(C global, C item) { return (T) item; } T result = targetClazz.getDeclaredConstructor().newInstance(); - BeanInfo beanInfo = Introspector.getBeanInfo(configClazz, Object.class); - PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); + + // Use cached BeanInfo or create and cache it + if (cachedBeanInfo == null) { + synchronized (this) { + if (cachedBeanInfo == null) { + cachedBeanInfo = Introspector.getBeanInfo(configClazz, Object.class); + } + } + } + + PropertyDescriptor[] propertyDescriptors = cachedBeanInfo.getPropertyDescriptors(); for (PropertyDescriptor pd : propertyDescriptors) { Class propertyType = pd.getPropertyType(); if (Properties.class == propertyType) { diff --git a/dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/DynamicRoutingDataSource.java b/dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/DynamicRoutingDataSource.java index 67180611..a11ea0d7 100644 --- a/dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/DynamicRoutingDataSource.java +++ b/dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/DynamicRoutingDataSource.java @@ -165,8 +165,10 @@ public synchronized void addDataSource(String ds, DataSource dataSource) { * @param dataSource 新数据源 */ private void addGroupDataSource(String ds, DataSource dataSource) { - if (ds.contains(UNDERLINE)) { - String group = ds.split(UNDERLINE)[0]; + int underlineIndex = ds.indexOf(UNDERLINE); + if (underlineIndex > 0) { + // Extract group name without using split() for better performance + String group = ds.substring(0, underlineIndex); GroupDataSource groupDataSource = groupDataSources.get(group); if (groupDataSource == null) { try { @@ -194,8 +196,10 @@ public synchronized void removeDataSource(String ds) { } if (dataSourceMap.containsKey(ds)) { DataSource dataSource = dataSourceMap.remove(ds); - if (ds.contains(UNDERLINE)) { - String group = ds.split(UNDERLINE)[0]; + int underlineIndex = ds.indexOf(UNDERLINE); + if (underlineIndex > 0) { + // Extract group name without using split() for better performance + String group = ds.substring(0, underlineIndex); if (groupDataSources.containsKey(group)) { DataSource oldDataSource = groupDataSources.get(group).removeDatasource(ds); if (oldDataSource == null) { diff --git a/dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/ds/GroupDataSource.java b/dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/ds/GroupDataSource.java index 9b91b51c..5496a8b6 100644 --- a/dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/ds/GroupDataSource.java +++ b/dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/ds/GroupDataSource.java @@ -20,6 +20,7 @@ import javax.sql.DataSource; import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -39,6 +40,12 @@ public class GroupDataSource { private DynamicDataSourceStrategy dynamicDataSourceStrategy; private Map dataSourceMap = new ConcurrentHashMap<>(); + + /** + * Cached list of datasource keys to avoid recreating ArrayList on every call. + * Marked as volatile to ensure visibility across threads. + */ + private volatile List cachedDsKeys = new ArrayList<>(); public GroupDataSource(String groupName, DynamicDataSourceStrategy dynamicDataSourceStrategy) { this.groupName = groupName; @@ -53,23 +60,30 @@ public GroupDataSource(String groupName, DynamicDataSourceStrategy dynamicDataSo * @return the previous value associated with ds, or null if there was no mapping for ds. */ public DataSource addDatasource(String ds, DataSource dataSource) { - return dataSourceMap.put(ds, dataSource); + DataSource result = dataSourceMap.put(ds, dataSource); + // Update cached keys list after modification + cachedDsKeys = new ArrayList<>(dataSourceMap.keySet()); + return result; } /** * @param ds the name of the datasource */ public DataSource removeDatasource(String ds) { - return dataSourceMap.remove(ds); + DataSource result = dataSourceMap.remove(ds); + // Update cached keys list after modification + cachedDsKeys = new ArrayList<>(dataSourceMap.keySet()); + return result; } /** * determineDsKey + * Performance optimized: uses cached list instead of creating new ArrayList on each call * * @return the name of the datasource */ public String determineDsKey() { - return dynamicDataSourceStrategy.determineKey(new ArrayList<>(dataSourceMap.keySet())); + return dynamicDataSourceStrategy.determineKey(cachedDsKeys); } /** diff --git a/dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/strategy/LoadBalanceDynamicDataSourceStrategy.java b/dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/strategy/LoadBalanceDynamicDataSourceStrategy.java index 4ad2aefd..4d979357 100644 --- a/dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/strategy/LoadBalanceDynamicDataSourceStrategy.java +++ b/dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/strategy/LoadBalanceDynamicDataSourceStrategy.java @@ -33,6 +33,9 @@ public class LoadBalanceDynamicDataSourceStrategy implements DynamicDataSourceSt @Override public String determineKey(List dsNames) { - return dsNames.get(Math.abs(index.getAndAdd(1) % dsNames.size())); + // Use getAndIncrement with proper modulo to prevent issues with negative values + // when counter overflows. Using & Integer.MAX_VALUE ensures non-negative value + int currentIndex = index.getAndIncrement() & Integer.MAX_VALUE; + return dsNames.get(currentIndex % dsNames.size()); } } From 8abeb426cc942b93dc04381a44d2b051c49341ca Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 07:18:44 +0000 Subject: [PATCH 3/4] Address code review: use immutable lists and add constructor validation Co-authored-by: huayanYu <16700837+huayanYu@users.noreply.github.com> --- .../datasource/toolkit/ConfigMergeCreator.java | 3 +++ .../dynamic/datasource/ds/GroupDataSource.java | 17 ++++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/dynamic-datasource-creator/src/main/java/com/baomidou/dynamic/datasource/toolkit/ConfigMergeCreator.java b/dynamic-datasource-creator/src/main/java/com/baomidou/dynamic/datasource/toolkit/ConfigMergeCreator.java index 2e1e0f41..4f90e77b 100644 --- a/dynamic-datasource-creator/src/main/java/com/baomidou/dynamic/datasource/toolkit/ConfigMergeCreator.java +++ b/dynamic-datasource-creator/src/main/java/com/baomidou/dynamic/datasource/toolkit/ConfigMergeCreator.java @@ -49,6 +49,9 @@ public class ConfigMergeCreator { private volatile BeanInfo cachedBeanInfo; public ConfigMergeCreator(String configName, Class configClazz, Class targetClazz) { + if (configName == null || configClazz == null || targetClazz == null) { + throw new IllegalArgumentException("ConfigMergeCreator parameters cannot be null"); + } this.configName = configName; this.configClazz = configClazz; this.targetClazz = targetClazz; diff --git a/dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/ds/GroupDataSource.java b/dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/ds/GroupDataSource.java index 5496a8b6..0cd07eb2 100644 --- a/dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/ds/GroupDataSource.java +++ b/dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/ds/GroupDataSource.java @@ -20,6 +20,7 @@ import javax.sql.DataSource; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -42,10 +43,11 @@ public class GroupDataSource { private Map dataSourceMap = new ConcurrentHashMap<>(); /** - * Cached list of datasource keys to avoid recreating ArrayList on every call. + * Cached immutable list of datasource keys to avoid recreating ArrayList on every call. * Marked as volatile to ensure visibility across threads. + * Immutable to prevent concurrent modification issues. */ - private volatile List cachedDsKeys = new ArrayList<>(); + private volatile List cachedDsKeys = Collections.emptyList(); public GroupDataSource(String groupName, DynamicDataSourceStrategy dynamicDataSourceStrategy) { this.groupName = groupName; @@ -61,8 +63,8 @@ public GroupDataSource(String groupName, DynamicDataSourceStrategy dynamicDataSo */ public DataSource addDatasource(String ds, DataSource dataSource) { DataSource result = dataSourceMap.put(ds, dataSource); - // Update cached keys list after modification - cachedDsKeys = new ArrayList<>(dataSourceMap.keySet()); + // Update cached keys list after modification with immutable copy + cachedDsKeys = Collections.unmodifiableList(new ArrayList<>(dataSourceMap.keySet())); return result; } @@ -71,18 +73,19 @@ public DataSource addDatasource(String ds, DataSource dataSource) { */ public DataSource removeDatasource(String ds) { DataSource result = dataSourceMap.remove(ds); - // Update cached keys list after modification - cachedDsKeys = new ArrayList<>(dataSourceMap.keySet()); + // Update cached keys list after modification with immutable copy + cachedDsKeys = Collections.unmodifiableList(new ArrayList<>(dataSourceMap.keySet())); return result; } /** * determineDsKey - * Performance optimized: uses cached list instead of creating new ArrayList on each call + * Performance optimized: uses cached immutable list instead of creating new ArrayList on each call * * @return the name of the datasource */ public String determineDsKey() { + // Safe to pass directly - list is immutable and strategies only read from it return dynamicDataSourceStrategy.determineKey(cachedDsKeys); } From ecc0c9a4b96952281edc72600bd5d28cb91de295 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 07:21:26 +0000 Subject: [PATCH 4/4] Fix underscore position check and add cache rebuild rationale Co-authored-by: huayanYu <16700837+huayanYu@users.noreply.github.com> --- .../baomidou/dynamic/datasource/DynamicRoutingDataSource.java | 2 ++ .../com/baomidou/dynamic/datasource/ds/GroupDataSource.java | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/DynamicRoutingDataSource.java b/dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/DynamicRoutingDataSource.java index a11ea0d7..df12a4ac 100644 --- a/dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/DynamicRoutingDataSource.java +++ b/dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/DynamicRoutingDataSource.java @@ -166,6 +166,7 @@ public synchronized void addDataSource(String ds, DataSource dataSource) { */ private void addGroupDataSource(String ds, DataSource dataSource) { int underlineIndex = ds.indexOf(UNDERLINE); + // Only process if underscore exists and is not at the start (group name must not be empty) if (underlineIndex > 0) { // Extract group name without using split() for better performance String group = ds.substring(0, underlineIndex); @@ -197,6 +198,7 @@ public synchronized void removeDataSource(String ds) { if (dataSourceMap.containsKey(ds)) { DataSource dataSource = dataSourceMap.remove(ds); int underlineIndex = ds.indexOf(UNDERLINE); + // Only process if underscore exists and is not at the start (group name must not be empty) if (underlineIndex > 0) { // Extract group name without using split() for better performance String group = ds.substring(0, underlineIndex); diff --git a/dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/ds/GroupDataSource.java b/dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/ds/GroupDataSource.java index 0cd07eb2..82182a3d 100644 --- a/dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/ds/GroupDataSource.java +++ b/dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/ds/GroupDataSource.java @@ -64,6 +64,8 @@ public GroupDataSource(String groupName, DynamicDataSourceStrategy dynamicDataSo public DataSource addDatasource(String ds, DataSource dataSource) { DataSource result = dataSourceMap.put(ds, dataSource); // Update cached keys list after modification with immutable copy + // Note: Cache rebuild is acceptable here as add/remove operations are infrequent + // compared to determineDataSource calls which benefit from the cached list cachedDsKeys = Collections.unmodifiableList(new ArrayList<>(dataSourceMap.keySet())); return result; } @@ -74,6 +76,8 @@ public DataSource addDatasource(String ds, DataSource dataSource) { public DataSource removeDatasource(String ds) { DataSource result = dataSourceMap.remove(ds); // Update cached keys list after modification with immutable copy + // Note: Cache rebuild is acceptable here as add/remove operations are infrequent + // compared to determineDataSource calls which benefit from the cached list cachedDsKeys = Collections.unmodifiableList(new ArrayList<>(dataSourceMap.keySet())); return result; }