This is an automated email from the ASF dual-hosted git repository.

chengzhang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git


The following commit(s) were added to refs/heads/master by this push:
     new f1bc9413622 Refactor OrderedSPILoader and RuleMetadata for improved 
performance (#37972)
f1bc9413622 is described below

commit f1bc9413622e580f60f8bd27e2daeaf604582bab
Author: Cong Hu <[email protected]>
AuthorDate: Fri Feb 6 18:09:17 2026 +0800

    Refactor OrderedSPILoader and RuleMetadata for improved performance (#37972)
---
 .../infra/metadata/database/rule/RuleMetaData.java | 130 +++++++++++--
 .../infra/spi/type/ordered/OrderedSPILoader.java   | 205 ++++++++++++++++++---
 .../spi/type/ordered/OrderedSPILoaderTest.java     |  37 ++++
 .../fixture/OrderedSPINonSingletonFixture.java     |  23 +++
 .../impl/OrderedSPINonSingletonFixtureImpl.java    |  33 ++++
 ...e.ordered.fixture.OrderedSPINonSingletonFixture |  18 ++
 6 files changed, 402 insertions(+), 44 deletions(-)

diff --git 
a/infra/common/src/main/java/org/apache/shardingsphere/infra/metadata/database/rule/RuleMetaData.java
 
b/infra/common/src/main/java/org/apache/shardingsphere/infra/metadata/database/rule/RuleMetaData.java
index d6a6b7f10d8..05fbe902fa1 100644
--- 
a/infra/common/src/main/java/org/apache/shardingsphere/infra/metadata/database/rule/RuleMetaData.java
+++ 
b/infra/common/src/main/java/org/apache/shardingsphere/infra/metadata/database/rule/RuleMetaData.java
@@ -17,10 +17,10 @@
 
 package org.apache.shardingsphere.infra.metadata.database.rule;
 
-import com.google.common.base.Preconditions;
 import lombok.Getter;
 import org.apache.shardingsphere.infra.config.rule.RuleConfiguration;
 import org.apache.shardingsphere.infra.datanode.DataNode;
+import org.apache.shardingsphere.infra.exception.ShardingSpherePreconditions;
 import org.apache.shardingsphere.infra.rule.ShardingSphereRule;
 import org.apache.shardingsphere.infra.rule.attribute.RuleAttribute;
 import 
org.apache.shardingsphere.infra.rule.attribute.datanode.DataNodeRuleAttribute;
@@ -34,19 +34,27 @@ import java.util.LinkedList;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 /**
  * Rule meta data.
  */
-@Getter
 public final class RuleMetaData {
     
+    private final Map<Class<?>, Collection<?>> ruleCache = new 
ConcurrentHashMap<>();
+    
+    private final Map<Class<?>, Optional<ShardingSphereRule>> singleRuleCache 
= new ConcurrentHashMap<>();
+    
+    private final Map<Class<?>, Collection<?>> attributeCache = new 
ConcurrentHashMap<>();
+    
+    @Getter
     private final Collection<ShardingSphereRule> rules;
     
     public RuleMetaData(final Collection<ShardingSphereRule> rules) {
-        this.rules = new CopyOnWriteArrayList<>(rules);
+        this.rules = new CacheInvalidatingCopyOnWriteArrayList(rules);
     }
     
     /**
@@ -65,11 +73,20 @@ public final class RuleMetaData {
      * @param <T> type of rule
      * @return found rules
      */
+    @SuppressWarnings("unchecked")
     public <T extends ShardingSphereRule> Collection<T> findRules(final 
Class<T> clazz) {
-        Collection<T> result = new LinkedList<>();
+        Collection<?> result = ruleCache.get(clazz);
+        if (null == result) {
+            result = ruleCache.computeIfAbsent(clazz, this::computeRules);
+        }
+        return (Collection<T>) result;
+    }
+    
+    private Collection<? extends ShardingSphereRule> computeRules(final 
Class<?> clazz) {
+        Collection<ShardingSphereRule> result = new LinkedList<>();
         for (ShardingSphereRule each : rules) {
             if (clazz.isAssignableFrom(each.getClass())) {
-                result.add(clazz.cast(each));
+                result.add(each);
             }
         }
         return result;
@@ -82,9 +99,13 @@ public final class RuleMetaData {
      * @param <T> type of rule
      * @return found single rule
      */
+    @SuppressWarnings("unchecked")
     public <T extends ShardingSphereRule> Optional<T> findSingleRule(final 
Class<T> clazz) {
-        Collection<T> foundRules = findRules(clazz);
-        return foundRules.isEmpty() ? Optional.empty() : 
Optional.of(foundRules.iterator().next());
+        Optional<ShardingSphereRule> result = singleRuleCache.get(clazz);
+        if (null == result) {
+            result = singleRuleCache.computeIfAbsent(clazz, 
this::computeSingleRule);
+        }
+        return (Optional<T>) result;
     }
     
     /**
@@ -94,10 +115,21 @@ public final class RuleMetaData {
      * @param <T> type of rule
      * @return found single rule
      */
+    @SuppressWarnings("unchecked")
     public <T extends ShardingSphereRule> T getSingleRule(final Class<T> 
clazz) {
-        Collection<T> foundRules = findRules(clazz);
-        Preconditions.checkState(1 == foundRules.size(), "Rule `%s` should 
have and only have one instance.", clazz.getSimpleName());
-        return foundRules.iterator().next();
+        Optional<ShardingSphereRule> shardingSphereRule = 
singleRuleCache.get(clazz);
+        if (null == shardingSphereRule) {
+            shardingSphereRule = singleRuleCache.computeIfAbsent(clazz, 
this::computeSingleRule);
+        }
+        ShardingSpherePreconditions.checkState(shardingSphereRule.isPresent(),
+                () -> new IllegalStateException(String.format("Rule `%s` 
should have and only have one instance.", clazz.getSimpleName())));
+        return (T) shardingSphereRule.get();
+    }
+    
+    @SuppressWarnings("unchecked")
+    private Optional<ShardingSphereRule> computeSingleRule(final Class<?> 
clazz) {
+        Collection<ShardingSphereRule> rules = 
findRules((Class<ShardingSphereRule>) clazz);
+        return 1 == rules.size() ? Optional.of(rules.iterator().next()) : 
Optional.empty();
     }
     
     /**
@@ -166,27 +198,87 @@ public final class RuleMetaData {
      * @param <T> type of rule attributes
      * @return rule attributes
      */
+    @SuppressWarnings("unchecked")
     public <T extends RuleAttribute> Collection<T> getAttributes(final 
Class<T> attributeClass) {
-        Collection<T> result = new LinkedList<>();
+        Collection<?> result = attributeCache.get(attributeClass);
+        if (null == result) {
+            result = attributeCache.computeIfAbsent(attributeClass, 
this::computeAttributes);
+        }
+        return (Collection<T>) result;
+    }
+    
+    private Collection<? extends RuleAttribute> computeAttributes(final 
Class<?> attributeClass) {
+        Collection<RuleAttribute> result = new LinkedList<>();
         for (ShardingSphereRule each : rules) {
-            
each.getAttributes().findAttribute(attributeClass).ifPresent(result::add);
+            
each.getAttributes().findAttribute(attributeClass.asSubclass(RuleAttribute.class)).ifPresent(result::add);
         }
         return result;
     }
     
     /**
-     * Get rule attributes.
+     * Find rule attribute.
      *
      * @param attributeClass rule attribute class
      * @param <T> type of rule attributes
-     * @return rule attributes
+     * @return rule attribute
      */
     public <T extends RuleAttribute> Optional<T> findAttribute(final Class<T> 
attributeClass) {
-        for (ShardingSphereRule each : rules) {
-            if 
(each.getAttributes().findAttribute(attributeClass).isPresent()) {
-                return each.getAttributes().findAttribute(attributeClass);
-            }
+        Collection<T> attributes = getAttributes(attributeClass);
+        return attributes.isEmpty() ? Optional.empty() : 
Optional.of(attributes.iterator().next());
+    }
+    
+    private final class CacheInvalidatingCopyOnWriteArrayList extends 
CopyOnWriteArrayList<ShardingSphereRule> {
+        
+        CacheInvalidatingCopyOnWriteArrayList(final 
Collection<ShardingSphereRule> rules) {
+            super(rules);
+        }
+        
+        private void invalidateCache() {
+            ruleCache.clear();
+            singleRuleCache.clear();
+            attributeCache.clear();
+        }
+        
+        @Override
+        public boolean add(final ShardingSphereRule rule) {
+            invalidateCache();
+            return super.add(rule);
+        }
+        
+        @Override
+        public boolean addAll(final Collection<? extends ShardingSphereRule> 
collection) {
+            invalidateCache();
+            return super.addAll(collection);
+        }
+        
+        @Override
+        public boolean remove(final Object o) {
+            invalidateCache();
+            return super.remove(o);
+        }
+        
+        @Override
+        public boolean removeAll(final Collection<?> objects) {
+            invalidateCache();
+            return super.removeAll(objects);
+        }
+        
+        @Override
+        public boolean removeIf(final Predicate<? super ShardingSphereRule> 
filter) {
+            invalidateCache();
+            return super.removeIf(filter);
+        }
+        
+        @Override
+        public boolean retainAll(final Collection<?> objects) {
+            invalidateCache();
+            return super.retainAll(objects);
+        }
+        
+        @Override
+        public void clear() {
+            invalidateCache();
+            super.clear();
         }
-        return Optional.empty();
     }
 }
diff --git 
a/infra/spi/src/main/java/org/apache/shardingsphere/infra/spi/type/ordered/OrderedSPILoader.java
 
b/infra/spi/src/main/java/org/apache/shardingsphere/infra/spi/type/ordered/OrderedSPILoader.java
index 077a7005ce1..e58bcb819ac 100644
--- 
a/infra/spi/src/main/java/org/apache/shardingsphere/infra/spi/type/ordered/OrderedSPILoader.java
+++ 
b/infra/spi/src/main/java/org/apache/shardingsphere/infra/spi/type/ordered/OrderedSPILoader.java
@@ -21,14 +21,22 @@ import com.google.common.base.Preconditions;
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
 import org.apache.shardingsphere.infra.spi.ShardingSphereServiceLoader;
-import 
org.apache.shardingsphere.infra.spi.type.ordered.cache.OrderedServicesCache;
+import org.apache.shardingsphere.infra.spi.annotation.SingletonSPI;
 
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
-import java.util.Optional;
+import java.util.Map.Entry;
+import java.util.Set;
 import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * Ordered SPI loader.
@@ -36,6 +44,14 @@ import java.util.TreeMap;
 @NoArgsConstructor(access = AccessLevel.PRIVATE)
 public final class OrderedSPILoader {
     
+    private static final ClassValue<OrderedSPIRegistry<?>> REGISTRY = new 
ClassValue<OrderedSPIRegistry<?>>() {
+        
+        @Override
+        protected OrderedSPIRegistry<?> computeValue(final Class<?> clazz) {
+            return null != clazz.getAnnotation(SingletonSPI.class) ? new 
CachedSingletonOrderedSPIRegistry<>(clazz) : new 
NoCachePrototypeOrderedSPIRegistry<>(clazz);
+        }
+    };
+    
     /**
      * Get services by class type.
      *
@@ -44,13 +60,9 @@ public final class OrderedSPILoader {
      * @param <T> type of ordered SPI class
      * @return got services
      */
+    @SuppressWarnings("unchecked")
     public static <T extends OrderedSPI<?>> Map<Class<?>, T> 
getServicesByClass(final Class<T> serviceInterface, final Collection<Class<?>> 
types) {
-        Collection<T> services = getServices(serviceInterface);
-        Map<Class<?>, T> result = new LinkedHashMap<>(services.size(), 1F);
-        for (T each : services) {
-            types.stream().filter(type -> each.getTypeClass() == 
type).forEach(type -> result.put(type, each));
-        }
-        return result;
+        return ((OrderedSPIRegistry<T>) 
REGISTRY.get(serviceInterface)).getServicesByClass(types);
     }
     
     /**
@@ -62,8 +74,9 @@ public final class OrderedSPILoader {
      * @param <V> type of ordered SPI class
      * @return got services
      */
+    @SuppressWarnings("unchecked")
     public static <K, V extends OrderedSPI<?>> Map<K, V> getServices(final 
Class<V> serviceInterface, final Collection<K> types) {
-        return getServices(serviceInterface, types, Comparator.naturalOrder());
+        return ((OrderedSPIRegistry<V>) 
REGISTRY.get(serviceInterface)).getServices(types);
     }
     
     /**
@@ -78,16 +91,12 @@ public final class OrderedSPILoader {
      */
     @SuppressWarnings("unchecked")
     public static <K, V extends OrderedSPI<?>> Map<K, V> getServices(final 
Class<V> serviceInterface, final Collection<K> types, final Comparator<Integer> 
orderComparator) {
-        Optional<Map<K, V>> cachedServices = 
OrderedServicesCache.findCachedServices(serviceInterface, types).map(optional 
-> (Map<K, V>) optional);
-        if (cachedServices.isPresent()) {
-            return cachedServices.get();
+        List<Entry<K, V>> orderServices = new 
ArrayList<>(((OrderedSPIRegistry<V>) 
REGISTRY.get(serviceInterface)).getServices(types).entrySet());
+        orderServices.sort((e1, e2) -> 
orderComparator.compare(e1.getValue().getOrder(), e2.getValue().getOrder()));
+        Map<K, V> result = new LinkedHashMap<>(orderServices.size(), 1F);
+        for (Entry<K, V> entry : orderServices) {
+            result.put(entry.getKey(), entry.getValue());
         }
-        Collection<V> services = getServices(serviceInterface, 
orderComparator);
-        Map<K, V> result = new LinkedHashMap<>(services.size(), 1F);
-        for (V each : services) {
-            types.stream().filter(type -> each.getTypeClass() == 
type.getClass()).forEach(type -> result.put(type, each));
-        }
-        OrderedServicesCache.cacheServices(serviceInterface, types, result);
         return result;
     }
     
@@ -98,16 +107,162 @@ public final class OrderedSPILoader {
      * @param <T> type of ordered SPI class
      * @return got services
      */
+    @SuppressWarnings("unchecked")
     public static <T extends OrderedSPI<?>> Collection<T> getServices(final 
Class<T> serviceInterface) {
-        return getServices(serviceInterface, Comparator.naturalOrder());
+        return ((OrderedSPIRegistry<T>) 
REGISTRY.get(serviceInterface)).getOrderedServices();
+    }
+    
+    private interface OrderedSPIRegistry<T extends OrderedSPI<?>> {
+        
+        Collection<T> getOrderedServices();
+        
+        Map<Class<?>, T> getServicesByClass(Collection<Class<?>> types);
+        
+        <K> Map<K, T> getServices(Collection<K> types);
+    }
+    
+    private static final class CachedSingletonOrderedSPIRegistry<T extends 
OrderedSPI<?>> implements OrderedSPIRegistry<T> {
+        
+        private final Collection<T> orderedServices;
+        
+        private final Map<Class<?>, T> singleTypeClassToService;
+        
+        private final Map<Set<Class<?>>, Map<Class<?>, T>> 
multiTypeClassToServices = new ConcurrentHashMap<>();
+        
+        private final Map<Collection<?>, Map<?, T>> multiObjectToServices = 
new ConcurrentHashMap<>();
+        
+        @SuppressWarnings({"rawtypes", "unchecked"})
+        CachedSingletonOrderedSPIRegistry(final Class<?> serviceInterface) {
+            Map<Integer, T> orderServices = new 
TreeMap<>(Comparator.naturalOrder());
+            for (Object each : 
ShardingSphereServiceLoader.getServiceInstances((Class) serviceInterface)) {
+                Preconditions.checkArgument(!orderServices.containsKey(((T) 
each).getOrder()),
+                        "Found same order `%s` with `%s` and `%s`", ((T) 
each).getOrder(), orderServices.get(((T) each).getOrder()), each);
+                orderServices.put(((T) each).getOrder(), (T) each);
+            }
+            orderedServices = orderServices.values();
+            singleTypeClassToService = new HashMap<>(orderedServices.size(), 
1F);
+            for (T each : orderedServices) {
+                singleTypeClassToService.put(each.getTypeClass(), each);
+            }
+        }
+        
+        @Override
+        public Collection<T> getOrderedServices() {
+            return orderedServices;
+        }
+        
+        @Override
+        public Map<Class<?>, T> getServicesByClass(final Collection<Class<?>> 
types) {
+            if (1 == types.size()) {
+                Class<?> type = types.iterator().next();
+                T service = singleTypeClassToService.get(type);
+                return null == service ? Collections.emptyMap() : 
Collections.singletonMap(type, service);
+            }
+            Set<Class<?>> typeClasses = types instanceof Set ? (Set<Class<?>>) 
types : new HashSet<>(types);
+            Map<Class<?>, T> result = 
multiTypeClassToServices.get(typeClasses);
+            if (null == result) {
+                result = multiTypeClassToServices.computeIfAbsent(typeClasses, 
this::computeServicesByClass);
+            }
+            return result;
+        }
+        
+        private Map<Class<?>, T> computeServicesByClass(final Set<Class<?>> 
types) {
+            Map<Class<?>, T> result = new LinkedHashMap<>(types.size(), 1F);
+            for (T each : orderedServices) {
+                if (types.contains(each.getTypeClass())) {
+                    result.put(each.getTypeClass(), each);
+                }
+            }
+            return result;
+        }
+        
+        @Override
+        @SuppressWarnings("unchecked")
+        public <K> Map<K, T> getServices(final Collection<K> types) {
+            if (1 == types.size()) {
+                K type = types.iterator().next();
+                T service = singleTypeClassToService.get(type.getClass());
+                return null == service ? Collections.emptyMap() : 
Collections.singletonMap(type, service);
+            }
+            Map<?, T> result = multiObjectToServices.get(types);
+            if (null == result) {
+                result = multiObjectToServices.computeIfAbsent(types, t -> 
computeServicesByObject((Collection<Object>) t));
+            }
+            return (Map<K, T>) result;
+        }
+        
+        private Map<Object, T> computeServicesByObject(final 
Collection<Object> types) {
+            Map<Class<?>, List<Object>> classTypeMap = new 
HashMap<>(types.size(), 1F);
+            Set<Class<?>> typeClasses = new HashSet<>(types.size(), 1F);
+            for (Object each : types) {
+                classTypeMap.computeIfAbsent(each.getClass(), clazz -> new 
LinkedList<>()).add(each);
+                typeClasses.add(each.getClass());
+            }
+            Map<Object, T> result = new LinkedHashMap<>(types.size(), 1F);
+            for (T each : orderedServices) {
+                if (typeClasses.contains(each.getTypeClass())) {
+                    for (Object type : classTypeMap.get(each.getTypeClass())) {
+                        result.put(type, each);
+                    }
+                }
+            }
+            return result;
+        }
     }
     
-    private static <T extends OrderedSPI<?>> Collection<T> getServices(final 
Class<T> serviceInterface, final Comparator<Integer> comparator) {
-        Map<Integer, T> result = new TreeMap<>(comparator);
-        for (T each : 
ShardingSphereServiceLoader.getServiceInstances(serviceInterface)) {
-            Preconditions.checkArgument(!result.containsKey(each.getOrder()), 
"Found same order `%s` with `%s` and `%s`", each.getOrder(), 
result.get(each.getOrder()), each);
-            result.put(each.getOrder(), each);
+    private static final class NoCachePrototypeOrderedSPIRegistry<T extends 
OrderedSPI<?>> implements OrderedSPIRegistry<T> {
+        
+        private final Class<T> serviceInterface;
+        
+        @SuppressWarnings("unchecked")
+        NoCachePrototypeOrderedSPIRegistry(final Class<?> serviceInterface) {
+            this.serviceInterface = (Class<T>) serviceInterface;
+        }
+        
+        @Override
+        public Collection<T> getOrderedServices() {
+            return loadOrderedServices();
+        }
+        
+        @Override
+        public Map<Class<?>, T> getServicesByClass(final Collection<Class<?>> 
types) {
+            Set<Class<?>> typeClasses = types instanceof Set ? (Set<Class<?>>) 
types : new HashSet<>(types);
+            Map<Class<?>, T> result = new LinkedHashMap<>(types.size(), 1F);
+            for (T each : loadOrderedServices()) {
+                if (typeClasses.contains(each.getTypeClass())) {
+                    result.put(each.getTypeClass(), each);
+                }
+            }
+            return result;
+        }
+        
+        @Override
+        public <K> Map<K, T> getServices(final Collection<K> types) {
+            Map<Class<?>, List<K>> classTypeMap = new HashMap<>(types.size(), 
1F);
+            Set<Class<?>> typeClasses = new HashSet<>(types.size(), 1F);
+            for (K each : types) {
+                classTypeMap.computeIfAbsent(each.getClass(), clazz -> new 
LinkedList<>()).add(each);
+                typeClasses.add(each.getClass());
+            }
+            Map<K, T> result = new LinkedHashMap<>(types.size(), 1F);
+            for (T each : loadOrderedServices()) {
+                if (typeClasses.contains(each.getTypeClass())) {
+                    for (K type : classTypeMap.get(each.getTypeClass())) {
+                        result.put(type, each);
+                    }
+                }
+            }
+            return result;
+        }
+        
+        private Collection<T> loadOrderedServices() {
+            Map<Integer, T> result = new TreeMap<>(Comparator.naturalOrder());
+            for (T each : 
ShardingSphereServiceLoader.getServiceInstances(serviceInterface)) {
+                
Preconditions.checkArgument(!result.containsKey(each.getOrder()),
+                        "Found same order `%s` with `%s` and `%s`", 
each.getOrder(), result.get(each.getOrder()), each);
+                result.put(each.getOrder(), each);
+            }
+            return result.values();
         }
-        return result.values();
     }
 }
diff --git 
a/infra/spi/src/test/java/org/apache/shardingsphere/infra/spi/type/ordered/OrderedSPILoaderTest.java
 
b/infra/spi/src/test/java/org/apache/shardingsphere/infra/spi/type/ordered/OrderedSPILoaderTest.java
index d6a45753d84..e31cef34060 100644
--- 
a/infra/spi/src/test/java/org/apache/shardingsphere/infra/spi/type/ordered/OrderedSPILoaderTest.java
+++ 
b/infra/spi/src/test/java/org/apache/shardingsphere/infra/spi/type/ordered/OrderedSPILoaderTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.shardingsphere.infra.spi.type.ordered;
 
+import 
org.apache.shardingsphere.infra.spi.type.ordered.fixture.OrderedSPINonSingletonFixture;
 import 
org.apache.shardingsphere.infra.spi.type.ordered.cache.OrderedServicesCache;
 import 
org.apache.shardingsphere.infra.spi.type.ordered.fixture.OrderedInterfaceFixture;
 import 
org.apache.shardingsphere.infra.spi.type.ordered.fixture.OrderedSPIFixture;
@@ -32,6 +33,8 @@ import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
 import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.sameInstance;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.isA;
 
@@ -66,4 +69,38 @@ class OrderedSPILoaderTest {
         assertThat(OrderedSPILoader.getServices(OrderedSPIFixture.class, 
Collections.singleton(key)),
                 is(OrderedSPILoader.getServices(OrderedSPIFixture.class, 
Collections.singleton(key))));
     }
+    
+    @SuppressWarnings("rawtypes")
+    @Test
+    void assertGetServicesByClassWithSingletonSPIReturnsSameInstance() {
+        Map<Class<?>, OrderedSPIFixture> firstCall = 
OrderedSPILoader.getServicesByClass(OrderedSPIFixture.class, 
Collections.singleton(OrderedInterfaceFixtureImpl.class));
+        Map<Class<?>, OrderedSPIFixture> secondCall = 
OrderedSPILoader.getServicesByClass(OrderedSPIFixture.class, 
Collections.singleton(OrderedInterfaceFixtureImpl.class));
+        assertThat(firstCall.get(OrderedInterfaceFixtureImpl.class), 
is(sameInstance(secondCall.get(OrderedInterfaceFixtureImpl.class))));
+    }
+    
+    @SuppressWarnings("rawtypes")
+    @Test
+    void assertGetServicesWithSingletonSPIReturnsSameInstance() {
+        OrderedInterfaceFixtureImpl key = new OrderedInterfaceFixtureImpl();
+        Map<OrderedInterfaceFixtureImpl, OrderedSPIFixture> firstCall = 
OrderedSPILoader.getServices(OrderedSPIFixture.class, 
Collections.singleton(key));
+        Map<OrderedInterfaceFixtureImpl, OrderedSPIFixture> secondCall = 
OrderedSPILoader.getServices(OrderedSPIFixture.class, 
Collections.singleton(key));
+        assertThat(firstCall.get(key), is(sameInstance(secondCall.get(key))));
+    }
+    
+    @SuppressWarnings("rawtypes")
+    @Test
+    void 
assertGetServicesByClassWithNonSingletonSPIReturnsDifferentInstances() {
+        Map<Class<?>, OrderedSPINonSingletonFixture> firstCall = 
OrderedSPILoader.getServicesByClass(OrderedSPINonSingletonFixture.class, 
Collections.singleton(OrderedInterfaceFixtureImpl.class));
+        Map<Class<?>, OrderedSPINonSingletonFixture> secondCall = 
OrderedSPILoader.getServicesByClass(OrderedSPINonSingletonFixture.class, 
Collections.singleton(OrderedInterfaceFixtureImpl.class));
+        assertThat(firstCall.get(OrderedInterfaceFixtureImpl.class), 
is(not(sameInstance(secondCall.get(OrderedInterfaceFixtureImpl.class)))));
+    }
+    
+    @SuppressWarnings("rawtypes")
+    @Test
+    void assertGetServicesWithNonSingletonSPIReturnsDifferentInstances() {
+        OrderedInterfaceFixtureImpl key = new OrderedInterfaceFixtureImpl();
+        Map<OrderedInterfaceFixtureImpl, OrderedSPINonSingletonFixture> 
firstCall = OrderedSPILoader.getServices(OrderedSPINonSingletonFixture.class, 
Collections.singleton(key));
+        Map<OrderedInterfaceFixtureImpl, OrderedSPINonSingletonFixture> 
secondCall = OrderedSPILoader.getServices(OrderedSPINonSingletonFixture.class, 
Collections.singleton(key));
+        assertThat(firstCall.get(key), 
is(not(sameInstance(secondCall.get(key)))));
+    }
 }
diff --git 
a/infra/spi/src/test/java/org/apache/shardingsphere/infra/spi/type/ordered/fixture/OrderedSPINonSingletonFixture.java
 
b/infra/spi/src/test/java/org/apache/shardingsphere/infra/spi/type/ordered/fixture/OrderedSPINonSingletonFixture.java
new file mode 100644
index 00000000000..53d8e2dffd9
--- /dev/null
+++ 
b/infra/spi/src/test/java/org/apache/shardingsphere/infra/spi/type/ordered/fixture/OrderedSPINonSingletonFixture.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.infra.spi.type.ordered.fixture;
+
+import org.apache.shardingsphere.infra.spi.type.ordered.OrderedSPI;
+
+public interface OrderedSPINonSingletonFixture<T> extends OrderedSPI<T> {
+}
diff --git 
a/infra/spi/src/test/java/org/apache/shardingsphere/infra/spi/type/ordered/fixture/impl/OrderedSPINonSingletonFixtureImpl.java
 
b/infra/spi/src/test/java/org/apache/shardingsphere/infra/spi/type/ordered/fixture/impl/OrderedSPINonSingletonFixtureImpl.java
new file mode 100644
index 00000000000..9a3dd743287
--- /dev/null
+++ 
b/infra/spi/src/test/java/org/apache/shardingsphere/infra/spi/type/ordered/fixture/impl/OrderedSPINonSingletonFixtureImpl.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.infra.spi.type.ordered.fixture.impl;
+
+import 
org.apache.shardingsphere.infra.spi.type.ordered.fixture.OrderedSPINonSingletonFixture;
+
+public final class OrderedSPINonSingletonFixtureImpl implements 
OrderedSPINonSingletonFixture<OrderedInterfaceFixtureImpl> {
+    
+    @Override
+    public Class<OrderedInterfaceFixtureImpl> getTypeClass() {
+        return OrderedInterfaceFixtureImpl.class;
+    }
+    
+    @Override
+    public int getOrder() {
+        return 1;
+    }
+}
diff --git 
a/infra/spi/src/test/resources/META-INF/services/org.apache.shardingsphere.infra.spi.type.ordered.fixture.OrderedSPINonSingletonFixture
 
b/infra/spi/src/test/resources/META-INF/services/org.apache.shardingsphere.infra.spi.type.ordered.fixture.OrderedSPINonSingletonFixture
new file mode 100644
index 00000000000..d396cdccd4c
--- /dev/null
+++ 
b/infra/spi/src/test/resources/META-INF/services/org.apache.shardingsphere.infra.spi.type.ordered.fixture.OrderedSPINonSingletonFixture
@@ -0,0 +1,18 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.apache.shardingsphere.infra.spi.type.ordered.fixture.impl.OrderedSPINonSingletonFixtureImpl

Reply via email to