From 62efb78d4e26d568d726e27ed833a3a8adea724a Mon Sep 17 00:00:00 2001 From: cdfive <31885791+cdfive@users.noreply.github.com> Date: Wed, 27 Jan 2021 09:45:56 +0800 Subject: [PATCH] Refactor SpiLoader and enhance SPI mechanism (#1383) * Add `@Spi` annotation as the general annotation for SPI definition. * Add isDefault in @Spi, add loadDefaultInstance and improve loadFirstInstanceOrDefault method, improve test cases * Add SpiLoaderException class for thrown when something goes wrong while loading Provider * Rearrange packages of base SPI mechanism NOTE: this PR contains breaking changes regarding API. --- .../api/GatewayApiDefinitionManager.java | 4 +- .../gateway/common/slot/GatewayFlowSlot.java | 4 +- .../common/slot/GatewaySlotChainBuilder.java | 3 +- .../codec/ClientEntityCodecProvider.java | 6 +- .../cluster/flow/DefaultTokenService.java | 2 + .../cluster/server/TokenServiceProvider.java | 5 +- .../codec/ServerEntityCodecProvider.java | 6 +- .../processor/RequestProcessorProvider.java | 10 +- .../com/alibaba/csp/sentinel/Constants.java | 12 + .../cluster/client/TokenClientProvider.java | 4 +- .../EmbeddedClusterTokenServerProvider.java | 4 +- .../csp/sentinel/config/SentinelConfig.java | 14 + .../csp/sentinel/init/InitExecutor.java | 6 +- .../extension/MetricExtensionProvider.java | 4 +- .../sentinel/slotchain/SlotChainProvider.java | 4 +- .../slots/DefaultSlotChainBuilder.java | 7 +- .../slots/block/authority/AuthoritySlot.java | 5 +- .../slots/block/degrade/DegradeSlot.java | 5 +- .../sentinel/slots/block/flow/FlowSlot.java | 5 +- .../clusterbuilder/ClusterBuilderSlot.java | 5 +- .../csp/sentinel/slots/logger/LogSlot.java | 5 +- .../slots/nodeselector/NodeSelectorSlot.java | 5 +- .../slots/statistic/StatisticSlot.java | 4 +- .../csp/sentinel/slots/system/SystemSlot.java | 5 +- .../csp/sentinel/spi/ServiceLoaderUtil.java | 45 -- .../com/alibaba/csp/sentinel/spi/Spi.java | 54 ++ .../alibaba/csp/sentinel/spi/SpiLoader.java | 534 ++++++++++++++++++ .../csp/sentinel/spi/SpiLoaderException.java | 36 ++ .../alibaba/csp/sentinel/spi/SpiOrder.java | 48 -- .../alibaba/csp/sentinel/util/SpiLoader.java | 291 ---------- .../csp/sentinel/spi/SpiLoaderTest.java | 321 +++++++++++ .../csp/sentinel/spi/TestFiveProvider.java | 9 + .../csp/sentinel/spi/TestFourProvider.java | 11 + .../csp/sentinel/spi/TestInterface.java | 8 + .../sentinel/spi/TestNoProviderInterface.java | 8 + .../sentinel/spi/TestNoSpiFileInterface.java | 8 + .../csp/sentinel/spi/TestOneProvider.java | 8 + .../csp/sentinel/spi/TestThreeProvider.java | 9 + .../csp/sentinel/spi/TestTwoProvider.java | 9 + .../csp/sentinel/util/SpiLoaderTest.java | 163 ------ ...com.alibaba.csp.sentinel.spi.TestInterface | 5 + ...a.csp.sentinel.spi.TestNoProviderInterface | 0 .../csp/sentinel/demo/slot/DemoSlot.java | 4 +- .../slots/HotParamSlotChainBuilder.java | 3 +- .../slots/block/flow/param/ParamFlowSlot.java | 4 +- .../command/CommandCenterProvider.java | 4 +- .../command/CommandHandlerProvider.java | 15 +- .../heartbeat/HeartbeatSenderProvider.java | 4 +- .../command/NettyHttpCommandCenter.java | 4 +- .../heartbeat/HttpHeartbeatSender.java | 4 +- 50 files changed, 1124 insertions(+), 619 deletions(-) delete mode 100644 sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/ServiceLoaderUtil.java create mode 100644 sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/Spi.java create mode 100644 sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/SpiLoader.java create mode 100644 sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/SpiLoaderException.java delete mode 100644 sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/SpiOrder.java delete mode 100644 sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/SpiLoader.java create mode 100644 sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/SpiLoaderTest.java create mode 100644 sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestFiveProvider.java create mode 100644 sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestFourProvider.java create mode 100644 sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestInterface.java create mode 100644 sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestNoProviderInterface.java create mode 100644 sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestNoSpiFileInterface.java create mode 100644 sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestOneProvider.java create mode 100644 sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestThreeProvider.java create mode 100644 sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestTwoProvider.java delete mode 100644 sentinel-core/src/test/java/com/alibaba/csp/sentinel/util/SpiLoaderTest.java create mode 100644 sentinel-core/src/test/resources/META-INF/services/com.alibaba.csp.sentinel.spi.TestInterface create mode 100644 sentinel-core/src/test/resources/META-INF/services/com.alibaba.csp.sentinel.spi.TestNoProviderInterface diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/GatewayApiDefinitionManager.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/GatewayApiDefinitionManager.java index 8f24803c..595109d7 100644 --- a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/GatewayApiDefinitionManager.java +++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/GatewayApiDefinitionManager.java @@ -27,7 +27,7 @@ import com.alibaba.csp.sentinel.property.DynamicSentinelProperty; import com.alibaba.csp.sentinel.property.PropertyListener; import com.alibaba.csp.sentinel.property.SentinelProperty; import com.alibaba.csp.sentinel.util.AssertUtil; -import com.alibaba.csp.sentinel.util.SpiLoader; +import com.alibaba.csp.sentinel.spi.SpiLoader; import com.alibaba.csp.sentinel.util.StringUtil; /** @@ -59,7 +59,7 @@ public final class GatewayApiDefinitionManager { } private static void initializeApiChangeObserverSpi() { - List listeners = SpiLoader.loadInstanceList(ApiDefinitionChangeObserver.class); + List listeners = SpiLoader.of(ApiDefinitionChangeObserver.class).loadInstanceList(); for (ApiDefinitionChangeObserver e : listeners) { API_CHANGE_OBSERVERS.put(e.getClass().getCanonicalName(), e); RecordLog.info("[GatewayApiDefinitionManager] ApiDefinitionChangeObserver added: {}" diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewayFlowSlot.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewayFlowSlot.java index b0932b7c..2b6fcaa3 100644 --- a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewayFlowSlot.java +++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewayFlowSlot.java @@ -27,13 +27,13 @@ import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowChecker; import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException; import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule; import com.alibaba.csp.sentinel.slots.block.flow.param.ParameterMetricStorage; -import com.alibaba.csp.sentinel.spi.SpiOrder; +import com.alibaba.csp.sentinel.spi.Spi; /** * @author Eric Zhao * @since 1.6.1 */ -@SpiOrder(-4000) +@Spi(order = -4000) public class GatewayFlowSlot extends AbstractLinkedProcessorSlot { @Override diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewaySlotChainBuilder.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewaySlotChainBuilder.java index 204b4619..78cc0997 100644 --- a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewaySlotChainBuilder.java +++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewaySlotChainBuilder.java @@ -21,8 +21,9 @@ import com.alibaba.csp.sentinel.slots.DefaultSlotChainBuilder; * @author Eric Zhao * @since 1.6.1 * - * @deprecated since 1.7.2, we can use @SpiOrder(-4000) to adjust the order of {@link GatewayFlowSlot}, + * @deprecated since 1.7.2, we can use @Spi(order = -4000) to adjust the order of {@link GatewayFlowSlot}, * this class is reserved for compatibility with older versions. + * * @see GatewayFlowSlot * @see DefaultSlotChainBuilder */ diff --git a/sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/ClientEntityCodecProvider.java b/sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/ClientEntityCodecProvider.java index e27ce682..a01654cb 100644 --- a/sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/ClientEntityCodecProvider.java +++ b/sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/ClientEntityCodecProvider.java @@ -15,7 +15,7 @@ */ package com.alibaba.csp.sentinel.cluster.client.codec; -import com.alibaba.csp.sentinel.util.SpiLoader; +import com.alibaba.csp.sentinel.spi.SpiLoader; import com.alibaba.csp.sentinel.cluster.codec.request.RequestEntityWriter; import com.alibaba.csp.sentinel.cluster.codec.response.ResponseEntityDecoder; import com.alibaba.csp.sentinel.log.RecordLog; @@ -34,7 +34,7 @@ public final class ClientEntityCodecProvider { } private static void resolveInstance() { - RequestEntityWriter writer = SpiLoader.loadFirstInstance(RequestEntityWriter.class); + RequestEntityWriter writer = SpiLoader.of(RequestEntityWriter.class).loadFirstInstance(); if (writer == null) { RecordLog.warn("[ClientEntityCodecProvider] No existing request entity writer, resolve failed"); } else { @@ -42,7 +42,7 @@ public final class ClientEntityCodecProvider { RecordLog.info("[ClientEntityCodecProvider] Request entity writer resolved: {}", requestEntityWriter.getClass().getCanonicalName()); } - ResponseEntityDecoder decoder = SpiLoader.loadFirstInstance(ResponseEntityDecoder.class); + ResponseEntityDecoder decoder = SpiLoader.of(ResponseEntityDecoder.class).loadFirstInstance(); if (decoder == null) { RecordLog.warn("[ClientEntityCodecProvider] No existing response entity decoder, resolve failed"); } else { diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/flow/DefaultTokenService.java b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/flow/DefaultTokenService.java index 4e78a50d..21d82acd 100644 --- a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/flow/DefaultTokenService.java +++ b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/flow/DefaultTokenService.java @@ -22,6 +22,7 @@ import com.alibaba.csp.sentinel.cluster.flow.rule.ClusterFlowRuleManager; import com.alibaba.csp.sentinel.cluster.flow.rule.ClusterParamFlowRuleManager; import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule; +import com.alibaba.csp.sentinel.spi.Spi; import java.util.Collection; @@ -31,6 +32,7 @@ import java.util.Collection; * @author Eric Zhao * @since 1.4.0 */ +@Spi(isDefault = true) public class DefaultTokenService implements TokenService { @Override diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/TokenServiceProvider.java b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/TokenServiceProvider.java index 1f9cd1c7..481e7a0f 100644 --- a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/TokenServiceProvider.java +++ b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/TokenServiceProvider.java @@ -16,9 +16,8 @@ package com.alibaba.csp.sentinel.cluster.server; import com.alibaba.csp.sentinel.cluster.TokenService; -import com.alibaba.csp.sentinel.cluster.flow.DefaultTokenService; import com.alibaba.csp.sentinel.log.RecordLog; -import com.alibaba.csp.sentinel.util.SpiLoader; +import com.alibaba.csp.sentinel.spi.SpiLoader; /** * @author Eric Zhao @@ -37,7 +36,7 @@ public final class TokenServiceProvider { } private static void resolveTokenServiceSpi() { - service = SpiLoader.loadFirstInstanceOrDefault(TokenService.class, DefaultTokenService.class); + service = SpiLoader.of(TokenService.class).loadFirstInstanceOrDefault(); if (service != null) { RecordLog.info("[TokenServiceProvider] Global token service resolved: " + service.getClass().getCanonicalName()); diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/codec/ServerEntityCodecProvider.java b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/codec/ServerEntityCodecProvider.java index 741570e2..3ef4aaa5 100644 --- a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/codec/ServerEntityCodecProvider.java +++ b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/codec/ServerEntityCodecProvider.java @@ -18,7 +18,7 @@ package com.alibaba.csp.sentinel.cluster.server.codec; import com.alibaba.csp.sentinel.cluster.codec.request.RequestEntityDecoder; import com.alibaba.csp.sentinel.cluster.codec.response.ResponseEntityWriter; import com.alibaba.csp.sentinel.log.RecordLog; -import com.alibaba.csp.sentinel.util.SpiLoader; +import com.alibaba.csp.sentinel.spi.SpiLoader; /** * @author Eric Zhao @@ -34,7 +34,7 @@ public final class ServerEntityCodecProvider { } private static void resolveInstance() { - ResponseEntityWriter writer = SpiLoader.loadFirstInstance(ResponseEntityWriter.class); + ResponseEntityWriter writer = SpiLoader.of(ResponseEntityWriter.class).loadFirstInstance(); if (writer == null) { RecordLog.warn("[ServerEntityCodecProvider] No existing response entity writer, resolve failed"); } else { @@ -42,7 +42,7 @@ public final class ServerEntityCodecProvider { RecordLog.info("[ServerEntityCodecProvider] Response entity writer resolved: {}", responseEntityWriter.getClass().getCanonicalName()); } - RequestEntityDecoder decoder = SpiLoader.loadFirstInstance(RequestEntityDecoder.class); + RequestEntityDecoder decoder = SpiLoader.of(RequestEntityDecoder.class).loadFirstInstance(); if (decoder == null) { RecordLog.warn("[ServerEntityCodecProvider] No existing request entity decoder, resolve failed"); } else { diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/processor/RequestProcessorProvider.java b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/processor/RequestProcessorProvider.java index ca1bd87d..0dcffce9 100644 --- a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/processor/RequestProcessorProvider.java +++ b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/processor/RequestProcessorProvider.java @@ -15,12 +15,12 @@ */ package com.alibaba.csp.sentinel.cluster.server.processor; +import java.util.List; import java.util.Map; -import java.util.ServiceLoader; import java.util.concurrent.ConcurrentHashMap; import com.alibaba.csp.sentinel.cluster.annotation.RequestType; -import com.alibaba.csp.sentinel.spi.ServiceLoaderUtil; +import com.alibaba.csp.sentinel.spi.SpiLoader; import com.alibaba.csp.sentinel.util.AssertUtil; /** @@ -31,15 +31,13 @@ public final class RequestProcessorProvider { private static final Map PROCESSOR_MAP = new ConcurrentHashMap<>(); - private static final ServiceLoader SERVICE_LOADER = ServiceLoaderUtil.getServiceLoader( - RequestProcessor.class); - static { loadAndInit(); } private static void loadAndInit() { - for (RequestProcessor processor : SERVICE_LOADER) { + List processors = SpiLoader.of(RequestProcessor.class).loadInstanceList(); + for (RequestProcessor processor : processors) { Integer type = parseRequestType(processor); if (type != null) { PROCESSOR_MAP.put(type, processor); diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java index 9c50ddf8..b7c3f808 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java @@ -70,5 +70,17 @@ public final class Constants { */ public static volatile boolean ON = true; + /** + * Order of default processor slots + */ + public static final int ORDER_NODE_SELECTOR_SLOT = -10000; + public static final int ORDER_CLUSTER_BUILDER_SLOT = -9000; + public static final int ORDER_LOG_SLOT = -8000; + public static final int ORDER_STATISTIC_SLOT = -7000; + public static final int ORDER_AUTHORITY_SLOT = -6000; + public static final int ORDER_SYSTEM_SLOT = -5000; + public static final int ORDER_FLOW_SLOT = -2000; + public static final int ORDER_DEGRADE_SLOT = -1000; + private Constants() {} } diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/client/TokenClientProvider.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/client/TokenClientProvider.java index 85c9da2f..4029ed2c 100644 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/client/TokenClientProvider.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/client/TokenClientProvider.java @@ -16,7 +16,7 @@ package com.alibaba.csp.sentinel.cluster.client; import com.alibaba.csp.sentinel.log.RecordLog; -import com.alibaba.csp.sentinel.util.SpiLoader; +import com.alibaba.csp.sentinel.spi.SpiLoader; /** * Provider for a universal {@link ClusterTokenClient} instance. @@ -38,7 +38,7 @@ public final class TokenClientProvider { } private static void resolveTokenClientInstance() { - ClusterTokenClient resolvedClient = SpiLoader.loadFirstInstance(ClusterTokenClient.class); + ClusterTokenClient resolvedClient = SpiLoader.of(ClusterTokenClient.class).loadFirstInstance(); if (resolvedClient == null) { RecordLog.info( "[TokenClientProvider] No existing cluster token client, cluster client mode will not be activated"); diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/server/EmbeddedClusterTokenServerProvider.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/server/EmbeddedClusterTokenServerProvider.java index 2f55e811..560a25ce 100644 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/server/EmbeddedClusterTokenServerProvider.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/server/EmbeddedClusterTokenServerProvider.java @@ -16,7 +16,7 @@ package com.alibaba.csp.sentinel.cluster.server; import com.alibaba.csp.sentinel.log.RecordLog; -import com.alibaba.csp.sentinel.util.SpiLoader; +import com.alibaba.csp.sentinel.spi.SpiLoader; /** * @author Eric Zhao @@ -31,7 +31,7 @@ public final class EmbeddedClusterTokenServerProvider { } private static void resolveInstance() { - EmbeddedClusterTokenServer s = SpiLoader.loadFirstInstance(EmbeddedClusterTokenServer.class); + EmbeddedClusterTokenServer s = SpiLoader.of(EmbeddedClusterTokenServer.class).loadFirstInstance(); if (s == null) { RecordLog.warn("[EmbeddedClusterTokenServerProvider] No existing cluster token server, cluster server mode will not be activated"); } else { diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/config/SentinelConfig.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/config/SentinelConfig.java index 788f97c2..ea9a0f02 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/config/SentinelConfig.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/config/SentinelConfig.java @@ -41,6 +41,11 @@ public final class SentinelConfig { */ public static final int APP_TYPE_COMMON = 0; + /** + * Parameter value for using context classloader. + */ + private static final String CLASSLOADER_CONTEXT = "context"; + private static final Map props = new ConcurrentHashMap<>(); private static int appType = APP_TYPE_COMMON; @@ -307,6 +312,15 @@ public final class SentinelConfig { private static String toEnvKey(/*@NotBlank*/ String propKey) { return propKey.toUpperCase().replace('.', '_'); } + /** + * Whether use context classloader via config parameter + * + * @return Whether use context classloader + */ + public static boolean shouldUseContextClassloader() { + String classloaderConf = SentinelConfig.getConfig(SentinelConfig.SPI_CLASSLOADER); + return CLASSLOADER_CONTEXT.equalsIgnoreCase(classloaderConf); + } private SentinelConfig() {} } diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/init/InitExecutor.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/init/InitExecutor.java index 8e63889d..f0feb3e9 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/init/InitExecutor.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/init/InitExecutor.java @@ -21,7 +21,7 @@ import java.util.ServiceLoader; import java.util.concurrent.atomic.AtomicBoolean; import com.alibaba.csp.sentinel.log.RecordLog; -import com.alibaba.csp.sentinel.spi.ServiceLoaderUtil; +import com.alibaba.csp.sentinel.spi.SpiLoader; /** * Load registered init functions and execute in order. @@ -43,9 +43,9 @@ public final class InitExecutor { return; } try { - ServiceLoader loader = ServiceLoaderUtil.getServiceLoader(InitFunc.class); + List initFuncs = SpiLoader.of(InitFunc.class).loadInstanceListSorted(); List initList = new ArrayList(); - for (InitFunc initFunc : loader) { + for (InitFunc initFunc : initFuncs) { RecordLog.info("[InitExecutor] Found init func: {}", initFunc.getClass().getCanonicalName()); insertSorted(initList, initFunc); } diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtensionProvider.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtensionProvider.java index 75da7fe7..e938ff64 100644 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtensionProvider.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtensionProvider.java @@ -19,7 +19,7 @@ import java.util.ArrayList; import java.util.List; import com.alibaba.csp.sentinel.log.RecordLog; -import com.alibaba.csp.sentinel.util.SpiLoader; +import com.alibaba.csp.sentinel.spi.SpiLoader; /** * Get all {@link MetricExtension} via SPI. @@ -35,7 +35,7 @@ public class MetricExtensionProvider { } private static void resolveInstance() { - List extensions = SpiLoader.loadInstanceList(MetricExtension.class); + List extensions = SpiLoader.of(MetricExtension.class).loadInstanceList(); if (extensions.isEmpty()) { RecordLog.info("[MetricExtensionProvider] No existing MetricExtension found"); diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/SlotChainProvider.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/SlotChainProvider.java index 49ab60a3..5eca5064 100644 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/SlotChainProvider.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/SlotChainProvider.java @@ -17,7 +17,7 @@ package com.alibaba.csp.sentinel.slotchain; import com.alibaba.csp.sentinel.log.RecordLog; import com.alibaba.csp.sentinel.slots.DefaultSlotChainBuilder; -import com.alibaba.csp.sentinel.util.SpiLoader; +import com.alibaba.csp.sentinel.spi.SpiLoader; /** * A provider for creating slot chains via resolved slot chain builder SPI. @@ -41,7 +41,7 @@ public final class SlotChainProvider { } // Resolve the slot chain builder SPI. - slotChainBuilder = SpiLoader.loadFirstInstanceOrDefault(SlotChainBuilder.class, DefaultSlotChainBuilder.class); + slotChainBuilder = SpiLoader.of(SlotChainBuilder.class).loadFirstInstanceOrDefault(); if (slotChainBuilder == null) { // Should not go through here. diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/DefaultSlotChainBuilder.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/DefaultSlotChainBuilder.java index cf0f72c5..968a65c6 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/DefaultSlotChainBuilder.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/DefaultSlotChainBuilder.java @@ -21,7 +21,8 @@ import com.alibaba.csp.sentinel.slotchain.DefaultProcessorSlotChain; import com.alibaba.csp.sentinel.slotchain.ProcessorSlot; import com.alibaba.csp.sentinel.slotchain.ProcessorSlotChain; import com.alibaba.csp.sentinel.slotchain.SlotChainBuilder; -import com.alibaba.csp.sentinel.util.SpiLoader; +import com.alibaba.csp.sentinel.spi.Spi; +import com.alibaba.csp.sentinel.spi.SpiLoader; import java.util.List; @@ -31,14 +32,14 @@ import java.util.List; * @author qinan.qn * @author leyou */ +@Spi(isDefault = true) public class DefaultSlotChainBuilder implements SlotChainBuilder { @Override public ProcessorSlotChain build() { ProcessorSlotChain chain = new DefaultProcessorSlotChain(); - // Note: the instances of ProcessorSlot should be different, since they are not stateless. - List sortedSlotList = SpiLoader.loadPrototypeInstanceListSorted(ProcessorSlot.class); + List sortedSlotList = SpiLoader.of(ProcessorSlot.class).loadInstanceListSorted(); for (ProcessorSlot slot : sortedSlotList) { if (!(slot instanceof AbstractLinkedProcessorSlot)) { RecordLog.warn("The ProcessorSlot(" + slot.getClass().getCanonicalName() + ") is not an instance of AbstractLinkedProcessorSlot, can't be added into ProcessorSlotChain"); diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthoritySlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthoritySlot.java index 617521a6..5217b716 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthoritySlot.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthoritySlot.java @@ -18,12 +18,13 @@ package com.alibaba.csp.sentinel.slots.block.authority; import java.util.Map; import java.util.Set; +import com.alibaba.csp.sentinel.Constants; import com.alibaba.csp.sentinel.context.Context; import com.alibaba.csp.sentinel.node.DefaultNode; import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot; import com.alibaba.csp.sentinel.slotchain.ProcessorSlot; import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; -import com.alibaba.csp.sentinel.spi.SpiOrder; +import com.alibaba.csp.sentinel.spi.Spi; /** * A {@link ProcessorSlot} that dedicates to {@link AuthorityRule} checking. @@ -31,7 +32,7 @@ import com.alibaba.csp.sentinel.spi.SpiOrder; * @author leyou * @author Eric Zhao */ -@SpiOrder(-6000) +@Spi(order = Constants.ORDER_AUTHORITY_SLOT) public class AuthoritySlot extends AbstractLinkedProcessorSlot { @Override diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeSlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeSlot.java index c66d056d..bd3a2e13 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeSlot.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeSlot.java @@ -17,6 +17,7 @@ package com.alibaba.csp.sentinel.slots.block.degrade; import java.util.List; +import com.alibaba.csp.sentinel.Constants; import com.alibaba.csp.sentinel.Entry; import com.alibaba.csp.sentinel.context.Context; import com.alibaba.csp.sentinel.node.DefaultNode; @@ -25,7 +26,7 @@ import com.alibaba.csp.sentinel.slotchain.ProcessorSlot; import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker; -import com.alibaba.csp.sentinel.spi.SpiOrder; +import com.alibaba.csp.sentinel.spi.Spi; /** * A {@link ProcessorSlot} dedicates to circuit breaking. @@ -33,7 +34,7 @@ import com.alibaba.csp.sentinel.spi.SpiOrder; * @author Carpenter Lee * @author Eric Zhao */ -@SpiOrder(-1000) +@Spi(order = Constants.ORDER_DEGRADE_SLOT) public class DegradeSlot extends AbstractLinkedProcessorSlot { @Override diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowSlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowSlot.java index d05d51c8..96b051a7 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowSlot.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowSlot.java @@ -15,12 +15,13 @@ */ package com.alibaba.csp.sentinel.slots.block.flow; +import com.alibaba.csp.sentinel.Constants; import com.alibaba.csp.sentinel.context.Context; import com.alibaba.csp.sentinel.node.DefaultNode; import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot; import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; import com.alibaba.csp.sentinel.slots.block.BlockException; -import com.alibaba.csp.sentinel.spi.SpiOrder; +import com.alibaba.csp.sentinel.spi.Spi; import com.alibaba.csp.sentinel.util.AssertUtil; import com.alibaba.csp.sentinel.util.function.Function; @@ -137,7 +138,7 @@ import java.util.Map; * @author jialiang.linjl * @author Eric Zhao */ -@SpiOrder(-2000) +@Spi(order = Constants.ORDER_FLOW_SLOT) public class FlowSlot extends AbstractLinkedProcessorSlot { private final FlowRuleChecker checker; diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/clusterbuilder/ClusterBuilderSlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/clusterbuilder/ClusterBuilderSlot.java index 5994be47..8750bfea 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/clusterbuilder/ClusterBuilderSlot.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/clusterbuilder/ClusterBuilderSlot.java @@ -18,6 +18,7 @@ package com.alibaba.csp.sentinel.slots.clusterbuilder; import java.util.HashMap; import java.util.Map; +import com.alibaba.csp.sentinel.Constants; import com.alibaba.csp.sentinel.EntryType; import com.alibaba.csp.sentinel.context.Context; import com.alibaba.csp.sentinel.context.ContextUtil; @@ -30,7 +31,7 @@ import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot; import com.alibaba.csp.sentinel.slotchain.ProcessorSlotChain; import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper; -import com.alibaba.csp.sentinel.spi.SpiOrder; +import com.alibaba.csp.sentinel.spi.Spi; /** *

@@ -45,7 +46,7 @@ import com.alibaba.csp.sentinel.spi.SpiOrder; * * @author jialiang.linjl */ -@SpiOrder(-9000) +@Spi(isSingleton = false, order = Constants.ORDER_CLUSTER_BUILDER_SLOT) public class ClusterBuilderSlot extends AbstractLinkedProcessorSlot { /** diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/logger/LogSlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/logger/LogSlot.java index a7515b01..948b9101 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/logger/LogSlot.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/logger/LogSlot.java @@ -15,19 +15,20 @@ */ package com.alibaba.csp.sentinel.slots.logger; +import com.alibaba.csp.sentinel.Constants; import com.alibaba.csp.sentinel.log.RecordLog; import com.alibaba.csp.sentinel.context.Context; import com.alibaba.csp.sentinel.node.DefaultNode; import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot; import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; import com.alibaba.csp.sentinel.slots.block.BlockException; -import com.alibaba.csp.sentinel.spi.SpiOrder; +import com.alibaba.csp.sentinel.spi.Spi; /** * A {@link com.alibaba.csp.sentinel.slotchain.ProcessorSlot} that is response for logging block exceptions * to provide concrete logs for troubleshooting. */ -@SpiOrder(-8000) +@Spi(order = Constants.ORDER_LOG_SLOT) public class LogSlot extends AbstractLinkedProcessorSlot { @Override diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/nodeselector/NodeSelectorSlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/nodeselector/NodeSelectorSlot.java index c965137f..553c48e8 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/nodeselector/NodeSelectorSlot.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/nodeselector/NodeSelectorSlot.java @@ -15,6 +15,7 @@ */ package com.alibaba.csp.sentinel.slots.nodeselector; +import com.alibaba.csp.sentinel.Constants; import com.alibaba.csp.sentinel.context.Context; import com.alibaba.csp.sentinel.context.ContextUtil; import com.alibaba.csp.sentinel.node.ClusterNode; @@ -22,7 +23,7 @@ import com.alibaba.csp.sentinel.node.DefaultNode; import com.alibaba.csp.sentinel.node.EntranceNode; import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot; import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; -import com.alibaba.csp.sentinel.spi.SpiOrder; +import com.alibaba.csp.sentinel.spi.Spi; import java.util.HashMap; import java.util.Map; @@ -123,7 +124,7 @@ import java.util.Map; * @see EntranceNode * @see ContextUtil */ -@SpiOrder(-10000) +@Spi(isSingleton = false, order = Constants.ORDER_NODE_SELECTOR_SLOT) public class NodeSelectorSlot extends AbstractLinkedProcessorSlot { /** diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/StatisticSlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/StatisticSlot.java index d491a16a..32a1b93d 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/StatisticSlot.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/StatisticSlot.java @@ -21,7 +21,7 @@ import com.alibaba.csp.sentinel.node.Node; import com.alibaba.csp.sentinel.slotchain.ProcessorSlotEntryCallback; import com.alibaba.csp.sentinel.slotchain.ProcessorSlotExitCallback; import com.alibaba.csp.sentinel.slots.block.flow.PriorityWaitException; -import com.alibaba.csp.sentinel.spi.SpiOrder; +import com.alibaba.csp.sentinel.spi.Spi; import com.alibaba.csp.sentinel.util.TimeUtil; import com.alibaba.csp.sentinel.Constants; import com.alibaba.csp.sentinel.EntryType; @@ -48,7 +48,7 @@ import com.alibaba.csp.sentinel.slots.block.BlockException; * @author jialiang.linjl * @author Eric Zhao */ -@SpiOrder(-7000) +@Spi(order = Constants.ORDER_STATISTIC_SLOT) public class StatisticSlot extends AbstractLinkedProcessorSlot { @Override diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemSlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemSlot.java index 5912e622..c98e491c 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemSlot.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemSlot.java @@ -15,12 +15,13 @@ */ package com.alibaba.csp.sentinel.slots.system; +import com.alibaba.csp.sentinel.Constants; import com.alibaba.csp.sentinel.context.Context; import com.alibaba.csp.sentinel.node.DefaultNode; import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot; import com.alibaba.csp.sentinel.slotchain.ProcessorSlot; import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; -import com.alibaba.csp.sentinel.spi.SpiOrder; +import com.alibaba.csp.sentinel.spi.Spi; /** * A {@link ProcessorSlot} that dedicates to {@link SystemRule} checking. @@ -28,7 +29,7 @@ import com.alibaba.csp.sentinel.spi.SpiOrder; * @author jialiang.linjl * @author leyou */ -@SpiOrder(-5000) +@Spi(order = Constants.ORDER_SYSTEM_SLOT) public class SystemSlot extends AbstractLinkedProcessorSlot { @Override diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/ServiceLoaderUtil.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/ServiceLoaderUtil.java deleted file mode 100644 index f7fe6fde..00000000 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/ServiceLoaderUtil.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 1999-2019 Alibaba Group Holding Ltd. - * - * Licensed 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 com.alibaba.csp.sentinel.spi; - -import java.util.ServiceLoader; - -import com.alibaba.csp.sentinel.config.SentinelConfig; - -/** - * @author Eric Zhao - * @since 1.7.0 - */ -public final class ServiceLoaderUtil { - - private static final String CLASSLOADER_DEFAULT = "default"; - private static final String CLASSLOADER_CONTEXT = "context"; - - public static ServiceLoader getServiceLoader(Class clazz) { - if (shouldUseContextClassloader()) { - return ServiceLoader.load(clazz); - } else { - return ServiceLoader.load(clazz, clazz.getClassLoader()); - } - } - - public static boolean shouldUseContextClassloader() { - String classloaderConf = SentinelConfig.getConfig(SentinelConfig.SPI_CLASSLOADER); - return CLASSLOADER_CONTEXT.equalsIgnoreCase(classloaderConf); - } - - private ServiceLoaderUtil() {} -} diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/Spi.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/Spi.java new file mode 100644 index 00000000..007f5c8e --- /dev/null +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/Spi.java @@ -0,0 +1,54 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.csp.sentinel.spi; + +import java.lang.annotation.*; + +/** + * Annotation for Provider class of SPI. + * + * @see SpiLoader + * @author cdfive + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +@Documented +public @interface Spi { + + /** + * Alias name of Provider class + */ + String value() default ""; + + /** + * Whether create singleton instance + */ + boolean isSingleton() default true; + + /** + * Whether is the default Provider + */ + boolean isDefault() default false; + + /** + * Order priority of Provider class + */ + int order() default 0; + + int ORDER_HIGHEST = Integer.MIN_VALUE; + + int ORDER_LOWEST = Integer.MAX_VALUE; +} diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/SpiLoader.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/SpiLoader.java new file mode 100644 index 00000000..eb6d9fc5 --- /dev/null +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/SpiLoader.java @@ -0,0 +1,534 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.csp.sentinel.spi; + +import com.alibaba.csp.sentinel.config.SentinelConfig; +import com.alibaba.csp.sentinel.log.RecordLog; +import com.alibaba.csp.sentinel.util.AssertUtil; +import com.alibaba.csp.sentinel.util.StringUtil; + +import java.io.*; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * A simple SPI loading facility. + * + *

SPI is short for Service Provider Interface.

+ * + *

+ * Service is represented by a single type, that is, a single interface or an abstract class. + * Provider is implementations of Service, that is, some classes which implement the interface or extends the abstract class. + *

+ * + *

+ * For Service type: + * Must interface or abstract class. + *

+ * + *

+ * For Provider class: + * Must have a zero-argument constructor so that they can be instantiated during loading. + *

+ * + *

+ * For Provider configuration file: + * 1. The file contains a list of fully-qualified binary names of concrete provider classes, one per line. + * 2. Space and tab characters surrounding each name, as well as blank lines, are ignored. + * 3. The comment line character is #, all characters following it are ignored. + *

+ * + *

+ * Provide common functions, such as: + * Load all Provider instance unsorted/sorted list. + * Load highest/lowest order priority instance. + * Load first-found or default instance. + * Load instance by aliasname or provider class. + *

+ * + * @author Eric Zhao + * @author cdfive + * @since 1.4.0 + * @see com.alibaba.csp.sentinel.spi.Spi + * @see java.util.ServiceLoader + */ +public final class SpiLoader { + + // Default path for the folder of Provider configuration file + private static final String SPI_FILE_PREFIX = "META-INF/services/"; + + // Cache the SpiLoader instances, key: classname of Service, value: SpiLoader instance + private static final ConcurrentHashMap SPI_LOADER_MAP = new ConcurrentHashMap<>(); + + // Cache the classes of Provider + private final List> classList = Collections.synchronizedList(new ArrayList>()); + + // Cache the sorted classes of Provider + private final List> sortedClassList = Collections.synchronizedList(new ArrayList>()); + + /** + * Cache the classes of Provider, key: aliasName, value: class of Provider. + * Note: aliasName is the value of {@link Spi} when the Provider class has {@link Spi} annotation and value is not empty, + * otherwise use classname of the Provider. + */ + private final ConcurrentHashMap> classMap = new ConcurrentHashMap<>(); + + // Cache the singleton instance of Provider, key: classname of Provider, value: Provider instance + private final ConcurrentHashMap singletonMap = new ConcurrentHashMap<>(); + + // Whether this SpiLoader has beend loaded, that is, loaded the Provider configuration file + private final AtomicBoolean loaded = new AtomicBoolean(false); + + // Default provider class + private Class defaultClass = null; + + // The Service class, must be interface or abstract class + private Class service; + + /** + * Create SpiLoader instance via Service class + * Cached by className, and load from cache first + * + * @param service Service class + * @param Service type + * @return SpiLoader instance + */ + public static SpiLoader of(Class service) { + AssertUtil.notNull(service, "SPI class cannot be null"); + AssertUtil.isTrue(service.isInterface() || Modifier.isAbstract(service.getModifiers()), + "SPI class[" + service.getName() + "] must be interface or abstract class"); + + String className = service.getName(); + SpiLoader spiLoader = SPI_LOADER_MAP.get(className); + if (spiLoader == null) { + synchronized (SpiLoader.class) { + spiLoader = SPI_LOADER_MAP.get(className); + if (spiLoader == null) { + SPI_LOADER_MAP.putIfAbsent(className, new SpiLoader<>(service)); + spiLoader = SPI_LOADER_MAP.get(className); + } + } + } + + return spiLoader; + } + + /** + * Reset and clear all SpiLoader instances. + * Package privilege, used only in test cases. + */ + synchronized static void resetAndClearAll() { + Set> entries = SPI_LOADER_MAP.entrySet(); + for (Map.Entry entry : entries) { + SpiLoader spiLoader = entry.getValue(); + spiLoader.resetAndClear(); + } + SPI_LOADER_MAP.clear(); + } + + // Private access + private SpiLoader(Class service) { + this.service = service; + } + + /** + * Load all Provider instances of the specified Service + * + * @return Provider instances list + */ + public List loadInstanceList() { + load(); + + return createInstanceList(classList); + } + + /** + * Load all Provider instances of the specified Service, sorted by order value in class's {@link Spi} annotation + * + * @return Sorted Provider instances list + */ + public List loadInstanceListSorted() { + load(); + + return createInstanceList(sortedClassList); + } + + /** + * Load highest order priority instance, order value is defined in class's {@link Spi} annotation + * + * @return Provider instance of highest order priority + */ + public S loadHighestPriorityInstance() { + load(); + + if (sortedClassList.size() == 0) { + return null; + } + + Class highestClass = sortedClassList.get(0); + return createInstance(highestClass); + } + + /** + * Load lowest order priority instance, order value is defined in class's {@link Spi} annotation + * + * @return Provider instance of lowest order priority + */ + public S loadLowestPriorityInstance() { + load(); + + if (sortedClassList.size() == 0) { + return null; + } + + Class lowestClass = sortedClassList.get(sortedClassList.size() - 1); + return createInstance(lowestClass); + } + + /** + * Load the first-found Provider instance + * + * @return Provider instance of first-found specific + */ + public S loadFirstInstance() { + load(); + + if (classList.size() == 0) { + return null; + } + + Class serviceClass = classList.get(0); + S instance = createInstance(serviceClass); + return instance; + } + + /** + * Load the first-found Provider instance,if not found, return default Provider instance + * + * @return Provider instance + */ + public S loadFirstInstanceOrDefault() { + load(); + + for (Class clazz : classList) { + if (defaultClass == null || clazz != defaultClass) { + return createInstance(clazz); + } + } + + return loadDefaultInstance(); + } + + /** + * Load default Provider instance + * Provider class with @Spi(isDefault = true) + * + * @return default Provider instance + */ + public S loadDefaultInstance() { + load(); + + if (defaultClass == null) { + return null; + } + + return createInstance(defaultClass); + } + + /** + * Load instance by specific class type + * + * @param clazz class type + * @return Provider instance + */ + public S loadInstance(Class clazz) { + AssertUtil.notNull(clazz, "SPI class cannot be null"); + + if (clazz.equals(service)) { + fail(clazz.getName() + " is not subtype of " + service.getName()); + } + + load(); + + if (!classMap.containsValue(clazz)) { + fail(clazz.getName() + " is not Provider class of " + service.getName() + ",check if it is in the SPI configuration file?"); + } + + return createInstance(clazz); + } + + /** + * Load instance by aliasName of Provider class + * + * @param aliasName aliasName of Provider class + * @return Provider instance + */ + public S loadInstance(String aliasName) { + AssertUtil.notEmpty(aliasName, "aliasName cannot be empty"); + + load(); + + Class clazz = classMap.get(aliasName); + if (clazz == null) { + fail("no Provider class's aliasName is " + aliasName); + } + + return createInstance(clazz); + } + + /** + * Reset and clear all fields of current SpiLoader instance and remove instance in SPI_LOADER_MAP + */ + synchronized void resetAndClear() { + SPI_LOADER_MAP.remove(service.getName()); + classList.clear(); + sortedClassList.clear(); + classMap.clear(); + singletonMap.clear(); + defaultClass = null; + loaded.set(false); + } + + /** + * Load the Provider class from Provider configuration file + */ + public void load() { + if (!loaded.compareAndSet(false, true)) { + return; + } + + String fullFileName = SPI_FILE_PREFIX + service.getName(); + ClassLoader classLoader; + if (SentinelConfig.shouldUseContextClassloader()) { + classLoader = Thread.currentThread().getContextClassLoader(); + } else { + classLoader = service.getClassLoader(); + } + if (classLoader == null) { + classLoader = ClassLoader.getSystemClassLoader(); + } + Enumeration urls = null; + try { + urls = classLoader.getResources(fullFileName); + } catch (IOException e) { + fail("Error locating SPI configuration file,filename=" + fullFileName + ",classloader=" + classLoader, e); + } + + if (urls == null || !urls.hasMoreElements()) { + RecordLog.warn("No SPI configuration file,filename=" + fullFileName + ",classloader=" + classLoader); + return; + } + + while (urls.hasMoreElements()) { + URL url = urls.nextElement(); + + InputStream in = null; + BufferedReader br = null; + try { + in = url.openStream(); + br = new BufferedReader(new InputStreamReader(in, "utf-8")); + String line; + while ((line = br.readLine()) != null) { + if (StringUtil.isBlank(line)) { + // Skip blank line + continue; + } + + line = line.trim(); + int commentIndex = line.indexOf("#"); + if (commentIndex == 0) { + // Skip comment line + continue; + } + + if (commentIndex > 0) { + line = line.substring(0, commentIndex); + } + line = line.trim(); + + Class clazz = null; + try { + clazz = (Class) Class.forName(line, false, classLoader); + } catch (ClassNotFoundException e) { + fail("class " + line + " not found", e); + } + + if (!service.isAssignableFrom(clazz)) { + fail("class " + clazz.getName() + "is not subtype of " + service.getName() + ",SPI configuration file=" + fullFileName); + } + + classList.add(clazz); + Spi spi = clazz.getAnnotation(Spi.class); + String aliasName = spi == null || "".equals(spi.value()) ? clazz.getName() : spi.value(); + if (classMap.containsKey(aliasName)) { + Class existClass = classMap.get(aliasName); + fail("Found repeat aliasname for " + clazz.getName() + " and " + + existClass.getName() + ",SPI configuration file=" + fullFileName); + } + classMap.put(aliasName, clazz); + + if (spi != null && spi.isDefault()) { + if (defaultClass != null) { + fail("Found more than one default Provider,SPI configuration file=" + fullFileName); + } + defaultClass = clazz; + } + + RecordLog.info("[SpiLoader]Found SPI,Service={},Provider={},aliasname={},isSingleton={},isDefault={},order={}", + service.getName(), line, aliasName + , spi == null ? true : spi.isSingleton() + , spi == null ? false : spi.isDefault() + , spi == null ? 0 : spi.order()); + } + } catch (IOException e) { + fail("error reading SPI configuration file", e); + } finally { + closeResources(in, br); + } + } + + sortedClassList.addAll(classList); + Collections.sort(sortedClassList, new Comparator>() { + @Override + public int compare(Class o1, Class o2) { + Spi spi1 = o1.getAnnotation(Spi.class); + int order1 = spi1 == null ? 0 : spi1.order(); + + Spi spi2 = o2.getAnnotation(Spi.class); + int order2 = spi2 == null ? 0 : spi2.order(); + + return Integer.compare(order1, order2); + } + }); + } + + @Override + public String toString() { + return "com.alibaba.csp.sentinel.spi.SpiLoader[" + service.getName() + "]"; + } + + /** + * Create Provider instance list + * + * @param clazzList class types of Providers + * @return Provider instance list + */ + private List createInstanceList(List> clazzList) { + if (clazzList == null || clazzList.size() == 0) { + return Collections.emptyList(); + } + + List instances = new ArrayList<>(clazzList.size()); + for (Class clazz : clazzList) { + S instance = createInstance(clazz); + instances.add(instance); + } + return instances; + } + + /** + * Create Provider instance + * + * @param clazz class type of Provider + * @return Provider class + */ + private S createInstance(Class clazz) { + Spi spi = clazz.getAnnotation(Spi.class); + boolean singleton = true; + if (spi != null) { + singleton = spi.isSingleton(); + } + return createInstance(clazz, singleton); + } + + /** + * Create Provider instance + * + * @param clazz class type of Provider + * @param singleton if instance is singleton or prototype + * @return Provider instance + */ + private S createInstance(Class clazz, boolean singleton) { + S instance = null; + try { + if (singleton) { + instance = singletonMap.get(clazz.getName()); + if (instance == null) { + synchronized (this) { + instance = singletonMap.get(clazz.getName()); + if (instance == null) { + instance = service.cast(clazz.newInstance()); + singletonMap.put(clazz.getName(), instance); + } + } + } + } else { + instance = service.cast(clazz.newInstance()); + } + } catch (Throwable e) { + fail(clazz.getName() + " could not be instantiated"); + } + return instance; + } + + /** + * Close all resources + * + * @param closeables {@link Closeable} resources + */ + private void closeResources(Closeable... closeables) { + if (closeables == null || closeables.length == 0) { + return; + } + + Exception firstException = null; + for (Closeable closeable : closeables) { + try { + closeable.close(); + } catch (Exception e) { + if (firstException != null) { + firstException = e; + } + } + } + if (firstException != null) { + fail("error closing resources", firstException); + } + } + + /** + * Throw {@link SpiLoaderException} with message + * + * @param msg error message + */ + private void fail(String msg) { + RecordLog.error(msg); + throw new SpiLoaderException("[" + service.getName() + "]" + msg); + } + + /** + * Throw {@link SpiLoaderException} with message and Throwable + * + * @param msg error message + */ + private void fail(String msg, Throwable e) { + RecordLog.error(msg, e); + throw new SpiLoaderException("[" + service.getName() + "]" + msg, e); + } +} diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/SpiLoaderException.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/SpiLoaderException.java new file mode 100644 index 00000000..c2cc1a95 --- /dev/null +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/SpiLoaderException.java @@ -0,0 +1,36 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.csp.sentinel.spi; + +/** + * Error thrown when something goes wrong while loading Provider via {@link SpiLoader}. + * + * @author cdfive + */ +public class SpiLoaderException extends RuntimeException { + + public SpiLoaderException() { + super(); + } + + public SpiLoaderException(String message) { + super(message); + } + + public SpiLoaderException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/SpiOrder.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/SpiOrder.java deleted file mode 100644 index 279ed310..00000000 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/spi/SpiOrder.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 1999-2019 Alibaba Group Holding Ltd. - * - * Licensed 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 - * - * https://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 com.alibaba.csp.sentinel.spi; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @author Eric Zhao - * @since 1.6.0 - */ -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.TYPE}) -@Documented -public @interface SpiOrder { - - /** - * Represents the lowest precedence. - */ - int LOWEST_PRECEDENCE = Integer.MAX_VALUE; - /** - * Represents the highest precedence. - */ - int HIGHEST_PRECEDENCE = Integer.MIN_VALUE; - - /** - * The SPI precedence value. Lowest precedence by default. - * - * @return the precedence value - */ - int value() default LOWEST_PRECEDENCE; -} diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/SpiLoader.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/SpiLoader.java deleted file mode 100644 index 47b3342a..00000000 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/SpiLoader.java +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright 1999-2018 Alibaba Group Holding Ltd. - * - * Licensed 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 com.alibaba.csp.sentinel.util; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.ServiceLoader; -import java.util.concurrent.ConcurrentHashMap; - -import com.alibaba.csp.sentinel.log.RecordLog; -import com.alibaba.csp.sentinel.spi.ServiceLoaderUtil; -import com.alibaba.csp.sentinel.spi.SpiOrder; - -/** - * @author Eric Zhao - * @since 1.4.0 - */ -public final class SpiLoader { - - private static final Map SERVICE_LOADER_MAP = new ConcurrentHashMap(); - - /** - * Load the first-found specific SPI instance - * - * @param clazz class of the SPI interface - * @param SPI type - * @return the first specific SPI instance if exists, or else return null - * @since 1.7.0 - */ - public static T loadFirstInstance(Class clazz) { - AssertUtil.notNull(clazz, "SPI class cannot be null"); - try { - String key = clazz.getName(); - // Not thread-safe, as it's expected to be resolved in a thread-safe context. - ServiceLoader serviceLoader = SERVICE_LOADER_MAP.get(key); - if (serviceLoader == null) { - serviceLoader = ServiceLoaderUtil.getServiceLoader(clazz); - SERVICE_LOADER_MAP.put(key, serviceLoader); - } - - Iterator iterator = serviceLoader.iterator(); - if (iterator.hasNext()) { - return iterator.next(); - } else { - return null; - } - } catch (Throwable t) { - RecordLog.error("[SpiLoader] ERROR: loadFirstInstance failed", t); - t.printStackTrace(); - return null; - } - } - - /** - * Load the first-found specific SPI instance (excluding provided default SPI class). - * If no other SPI implementation found, then create a default SPI instance. - * - * @param clazz class of the SPI interface - * @param defaultClass class of the default SPI implementation (if no other implementation found) - * @param SPI type - * @return the first specific SPI instance if exists, or else the default SPI instance - * @since 1.7.0 - */ - public static T loadFirstInstanceOrDefault(Class clazz, Class defaultClass) { - AssertUtil.notNull(clazz, "SPI class cannot be null"); - AssertUtil.notNull(defaultClass, "default SPI class cannot be null"); - try { - String key = clazz.getName(); - // Not thread-safe, as it's expected to be resolved in a thread-safe context. - ServiceLoader serviceLoader = SERVICE_LOADER_MAP.get(key); - if (serviceLoader == null) { - serviceLoader = ServiceLoaderUtil.getServiceLoader(clazz); - SERVICE_LOADER_MAP.put(key, serviceLoader); - } - - for (T instance : serviceLoader) { - if (instance.getClass() != defaultClass) { - return instance; - } - } - return defaultClass.newInstance(); - } catch (Throwable t) { - RecordLog.error("[SpiLoader] ERROR: loadFirstInstanceOrDefault failed", t); - t.printStackTrace(); - return null; - } - } - - /** - * Load the SPI instance with highest priority. - * - * Note: each call return same instances. - * - * @param clazz class of the SPI - * @param SPI type - * @return the SPI instance with highest priority if exists, or else false - * @since 1.6.0 - */ - public static T loadHighestPriorityInstance(Class clazz) { - try { - String key = clazz.getName(); - // Not thread-safe, as it's expected to be resolved in a thread-safe context. - ServiceLoader serviceLoader = SERVICE_LOADER_MAP.get(key); - if (serviceLoader == null) { - serviceLoader = ServiceLoaderUtil.getServiceLoader(clazz); - SERVICE_LOADER_MAP.put(key, serviceLoader); - } - - SpiOrderWrapper w = null; - for (T spi : serviceLoader) { - int order = SpiOrderResolver.resolveOrder(spi); - RecordLog.info("[SpiLoader] Found {} SPI: {} with order {}", clazz.getSimpleName(), - spi.getClass().getCanonicalName(), order); - if (w == null || order < w.order) { - w = new SpiOrderWrapper<>(order, spi); - } - } - return w == null ? null : w.spi; - } catch (Throwable t) { - RecordLog.error("[SpiLoader] ERROR: loadHighestPriorityInstance failed", t); - t.printStackTrace(); - return null; - } - } - - /** - * Load and sorted SPI instance list. - * Load the SPI instance list for provided SPI interface. - * - * Note: each call return same instances. - * - * @param clazz class of the SPI - * @param SPI type - * @return sorted SPI instance list - * @since 1.6.0 - */ - public static List loadInstanceList(Class clazz) { - try { - String key = clazz.getName(); - // Not thread-safe, as it's expected to be resolved in a thread-safe context. - ServiceLoader serviceLoader = SERVICE_LOADER_MAP.get(key); - if (serviceLoader == null) { - serviceLoader = ServiceLoaderUtil.getServiceLoader(clazz); - SERVICE_LOADER_MAP.put(key, serviceLoader); - } - - List list = new ArrayList<>(); - for (T spi : serviceLoader) { - RecordLog.info("[SpiLoader] Found {} SPI: {}", clazz.getSimpleName(), - spi.getClass().getCanonicalName()); - list.add(spi); - } - return list; - } catch (Throwable t) { - RecordLog.error("[SpiLoader] ERROR: loadInstanceList failed", t); - t.printStackTrace(); - return new ArrayList<>(); - } - } - - /** - * Load the sorted SPI instance list for provided SPI interface. - * - * Note: each call return same instances. - * - * @param clazz class of the SPI - * @param SPI type - * @return sorted SPI instance list - * @since 1.6.0 - */ - public static List loadInstanceListSorted(Class clazz) { - try { - String key = clazz.getName(); - // Not thread-safe, as it's expected to be resolved in a thread-safe context. - ServiceLoader serviceLoader = SERVICE_LOADER_MAP.get(key); - if (serviceLoader == null) { - serviceLoader = ServiceLoaderUtil.getServiceLoader(clazz); - SERVICE_LOADER_MAP.put(key, serviceLoader); - } - - List> orderWrappers = new ArrayList<>(); - for (T spi : serviceLoader) { - int order = SpiOrderResolver.resolveOrder(spi); - // Since SPI is lazy initialized in ServiceLoader, we use online sort algorithm here. - SpiOrderResolver.insertSorted(orderWrappers, spi, order); - RecordLog.info("[SpiLoader] Found {} SPI: {} with order {}", clazz.getSimpleName(), - spi.getClass().getCanonicalName(), order); - } - List list = new ArrayList<>(orderWrappers.size()); - for (int i = 0; i < orderWrappers.size(); i++) { - list.add(orderWrappers.get(i).spi); - } - return list; - } catch (Throwable t) { - RecordLog.error("[SpiLoader] ERROR: loadInstanceListSorted failed", t); - t.printStackTrace(); - return new ArrayList<>(); - } - } - - /** - * Load the sorted and prototype SPI instance list for provided SPI interface. - * - * Note: each call return different instances, i.e. prototype instance, not singleton instance. - * - * @param clazz class of the SPI - * @param SPI type - * @return sorted and different SPI instance list - * @since 1.7.2 - */ - public static List loadPrototypeInstanceListSorted(Class clazz) { - try { - // Not use SERVICE_LOADER_MAP, to make sure the instances loaded are different. - ServiceLoader serviceLoader = ServiceLoaderUtil.getServiceLoader(clazz); - - List> orderWrappers = new ArrayList<>(); - for (T spi : serviceLoader) { - int order = SpiOrderResolver.resolveOrder(spi); - // Since SPI is lazy initialized in ServiceLoader, we use online sort algorithm here. - SpiOrderResolver.insertSorted(orderWrappers, spi, order); - RecordLog.debug("[SpiLoader] Found {} SPI: {} with order {}", clazz.getSimpleName(), - spi.getClass().getCanonicalName(), order); - } - List list = new ArrayList<>(orderWrappers.size()); - for (int i = 0; i < orderWrappers.size(); i++) { - list.add(orderWrappers.get(i).spi); - } - return list; - } catch (Throwable t) { - RecordLog.error("[SpiLoader] ERROR: loadPrototypeInstanceListSorted failed", t); - t.printStackTrace(); - return new ArrayList<>(); - } - } - - private static class SpiOrderResolver { - private static void insertSorted(List> list, T spi, int order) { - int idx = 0; - for (; idx < list.size(); idx++) { - if (list.get(idx).getOrder() > order) { - break; - } - } - list.add(idx, new SpiOrderWrapper<>(order, spi)); - } - - private static int resolveOrder(T spi) { - if (!spi.getClass().isAnnotationPresent(SpiOrder.class)) { - // Lowest precedence by default. - return SpiOrder.LOWEST_PRECEDENCE; - } else { - return spi.getClass().getAnnotation(SpiOrder.class).value(); - } - } - } - - private static class SpiOrderWrapper { - private final int order; - private final T spi; - - SpiOrderWrapper(int order, T spi) { - this.order = order; - this.spi = spi; - } - - int getOrder() { - return order; - } - - T getSpi() { - return spi; - } - } - - private SpiLoader() {} -} diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/SpiLoaderTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/SpiLoaderTest.java new file mode 100644 index 00000000..7eb8f13e --- /dev/null +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/SpiLoaderTest.java @@ -0,0 +1,321 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.csp.sentinel.spi; + +import com.alibaba.csp.sentinel.SphU; +import com.alibaba.csp.sentinel.init.InitFunc; +import com.alibaba.csp.sentinel.metric.extension.MetricCallbackInit; +import com.alibaba.csp.sentinel.slotchain.ProcessorSlot; +import com.alibaba.csp.sentinel.slotchain.SlotChainBuilder; +import com.alibaba.csp.sentinel.slots.DefaultSlotChainBuilder; +import com.alibaba.csp.sentinel.slots.block.authority.AuthoritySlot; +import com.alibaba.csp.sentinel.slots.block.degrade.DegradeSlot; +import com.alibaba.csp.sentinel.slots.block.flow.FlowSlot; +import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot; +import com.alibaba.csp.sentinel.slots.logger.LogSlot; +import com.alibaba.csp.sentinel.slots.nodeselector.NodeSelectorSlot; +import com.alibaba.csp.sentinel.slots.statistic.StatisticSlot; +import com.alibaba.csp.sentinel.slots.system.SystemSlot; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; + +/** + * Test cases for {@link SpiLoader}. + * + * @author cdfive + */ +public class SpiLoaderTest { + + @Before + public void setUp() { + SpiLoader.resetAndClearAll(); + } + + @Before + public void tearDown() { + SpiLoader.resetAndClearAll(); + } + + @Test + public void testCreateSpiLoader() { + SpiLoader slotLoader1 = SpiLoader.of(ProcessorSlot.class); + assertNotNull(slotLoader1); + + SpiLoader slotLoader2 = SpiLoader.of(ProcessorSlot.class); + assertNotNull(slotLoader2); + + assertSame(slotLoader1, slotLoader2); + + SpiLoader initFuncLoader1 = SpiLoader.of(InitFunc.class); + assertNotNull(initFuncLoader1); + assertNotSame(slotLoader1, initFuncLoader1); + assertNotEquals(slotLoader1, initFuncLoader1); + + SpiLoader initFuncLoader2 = SpiLoader.of(InitFunc.class); + assertNotNull(initFuncLoader2); + + assertSame(initFuncLoader1, initFuncLoader2); + } + + @Test + public void testCreateSpiLoaderNotInterface() { + try { + SpiLoader.of(SphU.class); + fail(); + } catch (Exception e) { + assertTrue(e instanceof IllegalArgumentException); + assertThat(e.getMessage(), containsString("must be interface or abstract class")); + } + } + + @Test + public void testLoadInstanceList() { + SpiLoader spiLoader = SpiLoader.of(ProcessorSlot.class); + List slots1 = spiLoader.loadInstanceList(); + List slots2 = spiLoader.loadInstanceList(); + assertNotSame(slots1, slots2); + + List> prototypeSlotClasses = new ArrayList<>(2); + prototypeSlotClasses.add(NodeSelectorSlot.class); + prototypeSlotClasses.add(ClusterBuilderSlot.class); + + List> singletonSlotClasses = new ArrayList<>(6); + singletonSlotClasses.add(LogSlot.class); + singletonSlotClasses.add(StatisticSlot.class); + singletonSlotClasses.add(AuthoritySlot.class); + singletonSlotClasses.add(SystemSlot.class); + singletonSlotClasses.add(FlowSlot.class); + singletonSlotClasses.add(DegradeSlot.class); + + for (int i = 0; i < slots1.size(); i++) { + ProcessorSlot slot1 = slots1.get(i); + ProcessorSlot slot2 = slots2.get(i); + assertSame(slot1.getClass(), slot2.getClass()); + + boolean found = false; + for (Class prototypeSlotClass : prototypeSlotClasses) { + if (prototypeSlotClass.equals(slot1.getClass())) { + found = true; + assertTrue(prototypeSlotClass.equals(slot2.getClass())); + // Verify prototype function + assertNotSame(slot1, slot2); + break; + } + } + + if (found) { + continue; + } + + for (Class singletonSlotClass : singletonSlotClasses) { + if (singletonSlotClass.equals(slot1.getClass())) { + found = true; + assertTrue(singletonSlotClass.equals(slot2.getClass())); + // Verify single function + assertSame(slot1, slot2); + break; + } + } + + if (!found) { + fail("Should found and not go through here"); + } + } + } + + @Test + public void testLoadInstanceListSorted() { + List sortedSlots = SpiLoader.of(ProcessorSlot.class).loadInstanceListSorted(); + assertNotNull(sortedSlots); + + // Total 8 default slot in sentinel-core + assertEquals(8, sortedSlots.size()); + + // Verify the order of slot + int index = 0; + assertTrue(sortedSlots.get(index++) instanceof NodeSelectorSlot); + assertTrue(sortedSlots.get(index++) instanceof ClusterBuilderSlot); + assertTrue(sortedSlots.get(index++) instanceof LogSlot); + assertTrue(sortedSlots.get(index++) instanceof StatisticSlot); + assertTrue(sortedSlots.get(index++) instanceof AuthoritySlot); + assertTrue(sortedSlots.get(index++) instanceof SystemSlot); + assertTrue(sortedSlots.get(index++) instanceof FlowSlot); + assertTrue(sortedSlots.get(index++) instanceof DegradeSlot); + } + + @Test + public void testLoadHighestPriorityInstance() { + ProcessorSlot slot = SpiLoader.of(ProcessorSlot.class).loadHighestPriorityInstance(); + assertNotNull(slot); + + // NodeSelectorSlot is highest order priority with @Spi(order = -10000) among all slots + assertTrue(slot instanceof NodeSelectorSlot); + } + + @Test + public void testLoadLowestPriorityInstance() { + ProcessorSlot slot = SpiLoader.of(ProcessorSlot.class).loadLowestPriorityInstance(); + assertNotNull(slot); + + // NodeSelectorSlot is lowest order priority with @Spi(order = -1000) among all slots + assertTrue(slot instanceof DegradeSlot); + } + + @Test + public void testLoadFirstInstance() { + ProcessorSlot slot = SpiLoader.of(ProcessorSlot.class).loadFirstInstance(); + assertNotNull(slot); + assertTrue(slot instanceof NodeSelectorSlot); + + SlotChainBuilder chainBuilder = SpiLoader.of(SlotChainBuilder.class).loadFirstInstance(); + assertNotNull(chainBuilder); + assertTrue(chainBuilder instanceof SlotChainBuilder); + + InitFunc initFunc = SpiLoader.of(InitFunc.class).loadFirstInstance(); + assertNotNull(initFunc); + assertTrue(initFunc instanceof MetricCallbackInit); + } + + @Test + public void testLoadFirstInstanceOrDefault() { + SlotChainBuilder slotChainBuilder = SpiLoader.of(SlotChainBuilder.class).loadFirstInstanceOrDefault(); + assertNotNull(slotChainBuilder); + assertTrue(slotChainBuilder instanceof DefaultSlotChainBuilder); + } + + @Test + public void testLoadDefaultInstance() { + SlotChainBuilder slotChainBuilder = SpiLoader.of(SlotChainBuilder.class).loadDefaultInstance(); + assertNotNull(slotChainBuilder); + assertTrue(slotChainBuilder instanceof DefaultSlotChainBuilder); + } + + @Test + public void testLoadInstanceByClass() { + ProcessorSlot slot = SpiLoader.of(ProcessorSlot.class).loadInstance(StatisticSlot.class); + assertNotNull(slot); + assertTrue(slot instanceof StatisticSlot); + } + + @Test + public void testLoadInstanceByAliasName() { + ProcessorSlot slot = SpiLoader.of(ProcessorSlot.class).loadInstance("com.alibaba.csp.sentinel.slots.statistic.StatisticSlot"); + assertNotNull(slot); + assertTrue(slot instanceof StatisticSlot); + } + + @Test + public void testToString() { + SpiLoader spiLoader = SpiLoader.of(ProcessorSlot.class); + assertEquals("com.alibaba.csp.sentinel.spi.SpiLoader[com.alibaba.csp.sentinel.slotchain.ProcessorSlot]" + , spiLoader.toString()); + } + + /** + * Following test cases are for some test Interfaces. + */ + @Test + public void test_TestNoSpiFileInterface() { + SpiLoader loader = SpiLoader.of(TestNoSpiFileInterface.class); + + List providers = loader.loadInstanceList(); + assertTrue(providers.size() == 0); + + List sortedProviders = loader.loadInstanceListSorted(); + assertTrue(sortedProviders.size() == 0); + + TestNoSpiFileInterface firstProvider = loader.loadFirstInstance(); + assertNull(firstProvider); + + TestNoSpiFileInterface defaultProvider = loader.loadDefaultInstance(); + assertNull(defaultProvider); + } + + @Test + public void test_TestNoProviderInterface() { + List providers = SpiLoader.of(TestNoProviderInterface.class).loadInstanceList(); + assertTrue(providers.size() == 0); + } + + @Test + public void test_TestInterface() { + SpiLoader loader = SpiLoader.of(TestInterface.class); + + List providers = loader.loadInstanceList(); + assertTrue(providers.size() == 4); + assertTrue(providers.get(0) instanceof TestOneProvider); + assertTrue(providers.get(1) instanceof TestTwoProvider); + assertTrue(providers.get(2) instanceof TestThreeProvider); + assertTrue(providers.get(3) instanceof TestFiveProvider); + + List sortedProviders = loader.loadInstanceListSorted(); + assertEquals(sortedProviders.size(), 4); + assertTrue(sortedProviders.get(0) instanceof TestThreeProvider); + assertTrue(sortedProviders.get(1) instanceof TestFiveProvider); + assertTrue(sortedProviders.get(2) instanceof TestTwoProvider); + assertTrue(sortedProviders.get(3) instanceof TestOneProvider); + + assertSame(providers.get(0), sortedProviders.get(3)); + assertSame(providers.get(1), sortedProviders.get(2)); + assertNotSame(providers.get(2), sortedProviders.get(0)); + assertSame(providers.get(3), sortedProviders.get(1)); + + assertTrue(loader.loadDefaultInstance() instanceof TestFiveProvider); + + assertTrue(loader.loadHighestPriorityInstance() instanceof TestThreeProvider); + assertTrue(loader.loadLowestPriorityInstance() instanceof TestOneProvider); + + assertTrue(loader.loadInstance("two") instanceof TestTwoProvider); + assertSame(loader.loadInstance("two"), loader.loadInstance("two")); + + try { + loader.loadInstance("one"); + fail(); + } catch (Exception e) { + assertTrue(e instanceof SpiLoaderException); + assertThat(e.getMessage(), containsString("no Provider class's aliasName is one")); + } + + TestInterface oneProvider1 = loader.loadInstance(TestOneProvider.class); + assertNotNull(oneProvider1); + TestInterface oneProvider2 = loader.loadInstance(TestOneProvider.class); + assertNotNull(oneProvider2); + assertSame(oneProvider1, oneProvider2); + + try { + loader.loadInstance(TestInterface.class); + fail(); + } catch (Exception e) { + assertTrue(e instanceof SpiLoaderException); + assertThat(e.getMessage(), containsString("is not subtype of")); + } + + try { + loader.loadInstance(TestFourProvider.class); + fail(); + } catch (Exception e) { + assertTrue(e instanceof SpiLoaderException); + assertThat(e.getMessage(), allOf(containsString("is not Provider class of") + , containsString("check if it is in the SPI configuration file?"))); + } + } +} diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestFiveProvider.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestFiveProvider.java new file mode 100644 index 00000000..bcad41aa --- /dev/null +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestFiveProvider.java @@ -0,0 +1,9 @@ +package com.alibaba.csp.sentinel.spi; + +/** + * @author cdfive + */ +@Spi(value = "five", isDefault = true, order = -270) +public class TestFiveProvider implements TestInterface { + +} diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestFourProvider.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestFourProvider.java new file mode 100644 index 00000000..6e8b7b5c --- /dev/null +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestFourProvider.java @@ -0,0 +1,11 @@ +package com.alibaba.csp.sentinel.spi; + +/** + * This Provider class isn't configured in SPI file. + * + * @author cdfive + */ +@Spi(value = "four", isSingleton = true, order = -400) +public class TestFourProvider implements TestInterface { + +} diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestInterface.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestInterface.java new file mode 100644 index 00000000..4005be9d --- /dev/null +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestInterface.java @@ -0,0 +1,8 @@ +package com.alibaba.csp.sentinel.spi; + +/** + * @author cdfive + */ +public interface TestInterface { + +} diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestNoProviderInterface.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestNoProviderInterface.java new file mode 100644 index 00000000..3c1d5a55 --- /dev/null +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestNoProviderInterface.java @@ -0,0 +1,8 @@ +package com.alibaba.csp.sentinel.spi; + +/** + * @author cdfive + */ +public interface TestNoProviderInterface { + +} diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestNoSpiFileInterface.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestNoSpiFileInterface.java new file mode 100644 index 00000000..dd678880 --- /dev/null +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestNoSpiFileInterface.java @@ -0,0 +1,8 @@ +package com.alibaba.csp.sentinel.spi; + +/** + * @author cdfive + */ +public interface TestNoSpiFileInterface { + +} diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestOneProvider.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestOneProvider.java new file mode 100644 index 00000000..67e83475 --- /dev/null +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestOneProvider.java @@ -0,0 +1,8 @@ +package com.alibaba.csp.sentinel.spi; + +/** + * @author cdfive + */ +public class TestOneProvider implements TestInterface { + +} diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestThreeProvider.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestThreeProvider.java new file mode 100644 index 00000000..1912ef02 --- /dev/null +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestThreeProvider.java @@ -0,0 +1,9 @@ +package com.alibaba.csp.sentinel.spi; + +/** + * @author cdfive + */ +@Spi(order = -300, isSingleton = false) +public class TestThreeProvider implements TestInterface { + +} diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestTwoProvider.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestTwoProvider.java new file mode 100644 index 00000000..710c6268 --- /dev/null +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/TestTwoProvider.java @@ -0,0 +1,9 @@ +package com.alibaba.csp.sentinel.spi; + +/** + * @author cdfive + */ +@Spi(value = "two", isSingleton = true, order = -200) +public class TestTwoProvider implements TestInterface { + +} diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/util/SpiLoaderTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/util/SpiLoaderTest.java deleted file mode 100644 index 622f7fd2..00000000 --- a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/util/SpiLoaderTest.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 1999-2018 Alibaba Group Holding Ltd. - * - * Licensed 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 com.alibaba.csp.sentinel.util; - -import com.alibaba.csp.sentinel.slotchain.ProcessorSlot; -import com.alibaba.csp.sentinel.slotchain.SlotChainBuilder; -import com.alibaba.csp.sentinel.slots.DefaultSlotChainBuilder; -import com.alibaba.csp.sentinel.slots.block.authority.AuthoritySlot; -import com.alibaba.csp.sentinel.slots.block.degrade.DegradeSlot; -import com.alibaba.csp.sentinel.slots.block.flow.FlowSlot; -import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot; -import com.alibaba.csp.sentinel.slots.logger.LogSlot; -import com.alibaba.csp.sentinel.slots.nodeselector.NodeSelectorSlot; -import com.alibaba.csp.sentinel.slots.statistic.StatisticSlot; -import com.alibaba.csp.sentinel.slots.system.SystemSlot; -import org.junit.Test; - -import java.util.List; - -import static org.junit.Assert.*; - -/** - * Test cases for {@link SpiLoader}. - * - * @author cdfive - */ -public class SpiLoaderTest { - - @Test - public void testLoadFirstInstance() { - ProcessorSlot processorSlot = SpiLoader.loadFirstInstance(ProcessorSlot.class); - assertNotNull(processorSlot); - - ProcessorSlot processorSlot2 = SpiLoader.loadFirstInstance(ProcessorSlot.class); - // As SERVICE_LOADER_MAP in SpiLoader cached the instance, so they're same instances - assertSame(processorSlot, processorSlot2); - - SlotChainBuilder slotChainBuilder = SpiLoader.loadFirstInstance(SlotChainBuilder.class); - assertNotNull(slotChainBuilder); - assertTrue(slotChainBuilder instanceof DefaultSlotChainBuilder); - - SlotChainBuilder slotChainBuilder2 = SpiLoader.loadFirstInstance(SlotChainBuilder.class); - // As SERVICE_LOADER_MAP in SpiLoader cached the instance, so they're same instances - assertSame(slotChainBuilder, slotChainBuilder2); - } - - @Test - public void testLoadHighestPriorityInstance() { - ProcessorSlot processorSlot = SpiLoader.loadHighestPriorityInstance(ProcessorSlot.class); - assertNotNull(processorSlot); - - // NodeSelectorSlot is highest order with @SpiOrder(-10000), among all slots - assertTrue(processorSlot instanceof NodeSelectorSlot); - - ProcessorSlot processorSlot2 = SpiLoader.loadHighestPriorityInstance(ProcessorSlot.class); - // As SERVICE_LOADER_MAP in SpiLoader cached the instance, so they're same instances - assertSame(processorSlot, processorSlot2); - } - - @Test - public void testLoadInstanceList() { - List slots = SpiLoader.loadInstanceList(ProcessorSlot.class); - assertNotNull(slots); - - // Total 8 default slot in sentinel-core - assertEquals(8, slots.size()); - - // Get the first slot of slots - ProcessorSlot firstSlot = slots.get(0); - - // Call loadInstanceList again - List slots2 = SpiLoader.loadInstanceList(ProcessorSlot.class); - // Note: the return list are different, and the item instances in list are same - assertNotSame(slots, slots2); - - // Get the first slot of slots2 - ProcessorSlot firstSlot2 = slots2.get(0); - - // As SERVICE_LOADER_MAP in SpiLoader cached the instance, so they're same instances - assertSame(firstSlot, firstSlot2); - } - - @Test - public void testLoadInstanceListSorted() { - List sortedSlots = SpiLoader.loadInstanceListSorted(ProcessorSlot.class); - assertNotNull(sortedSlots); - - // Total 8 default slot in sentinel-core - assertEquals(8, sortedSlots.size()); - - // Verify the order of slot - int index = 0; - assertTrue(sortedSlots.get(index++) instanceof NodeSelectorSlot); - assertTrue(sortedSlots.get(index++) instanceof ClusterBuilderSlot); - assertTrue(sortedSlots.get(index++) instanceof LogSlot); - assertTrue(sortedSlots.get(index++) instanceof StatisticSlot); - assertTrue(sortedSlots.get(index++) instanceof AuthoritySlot); - assertTrue(sortedSlots.get(index++) instanceof SystemSlot); - assertTrue(sortedSlots.get(index++) instanceof FlowSlot); - assertTrue(sortedSlots.get(index++) instanceof DegradeSlot); - - // Verify each call return different instances - // Note: the return list are different, and the item instances in list are same - List sortedSlots2 = SpiLoader.loadInstanceListSorted(ProcessorSlot.class); - assertNotSame(sortedSlots, sortedSlots2); - assertEquals(sortedSlots.size(), sortedSlots2.size()); - for (int i = 0; i < sortedSlots.size(); i++) { - ProcessorSlot slot = sortedSlots.get(i); - ProcessorSlot slot2 = sortedSlots2.get(i); - assertEquals(slot.getClass(), slot2.getClass()); - - // As SERVICE_LOADER_MAP in SpiLoader cached the instance, so they're same instances - assertSame(slot, slot2); - } - } - - @Test - public void testLoadPrototypeInstanceListSorted() { - List sortedSlots = SpiLoader.loadInstanceListSorted(ProcessorSlot.class); - assertNotNull(sortedSlots); - - // Total 8 default slot in sentinel-core - assertEquals(8, sortedSlots.size()); - - // Verify the order of slot - int index = 0; - assertTrue(sortedSlots.get(index++) instanceof NodeSelectorSlot); - assertTrue(sortedSlots.get(index++) instanceof ClusterBuilderSlot); - assertTrue(sortedSlots.get(index++) instanceof LogSlot); - assertTrue(sortedSlots.get(index++) instanceof StatisticSlot); - assertTrue(sortedSlots.get(index++) instanceof AuthoritySlot); - assertTrue(sortedSlots.get(index++) instanceof SystemSlot); - assertTrue(sortedSlots.get(index++) instanceof FlowSlot); - assertTrue(sortedSlots.get(index++) instanceof DegradeSlot); - - // Verify each call return new instances - List sortedSlots2 = SpiLoader.loadPrototypeInstanceListSorted(ProcessorSlot.class); - assertNotSame(sortedSlots, sortedSlots2); - assertEquals(sortedSlots.size(), sortedSlots2.size()); - for (int i = 0; i < sortedSlots.size(); i++) { - ProcessorSlot slot = sortedSlots.get(i); - ProcessorSlot slot2 = sortedSlots2.get(i); - assertEquals(slot.getClass(), slot2.getClass()); - - // Verify the instances are different - assertNotSame(slot, slot2); - assertNotEquals(slot, slot2); - } - } -} diff --git a/sentinel-core/src/test/resources/META-INF/services/com.alibaba.csp.sentinel.spi.TestInterface b/sentinel-core/src/test/resources/META-INF/services/com.alibaba.csp.sentinel.spi.TestInterface new file mode 100644 index 00000000..7960b39c --- /dev/null +++ b/sentinel-core/src/test/resources/META-INF/services/com.alibaba.csp.sentinel.spi.TestInterface @@ -0,0 +1,5 @@ +# One +com.alibaba.csp.sentinel.spi.TestOneProvider +com.alibaba.csp.sentinel.spi.TestTwoProvider # Two +com.alibaba.csp.sentinel.spi.TestThreeProvider +com.alibaba.csp.sentinel.spi.TestFiveProvider \ No newline at end of file diff --git a/sentinel-core/src/test/resources/META-INF/services/com.alibaba.csp.sentinel.spi.TestNoProviderInterface b/sentinel-core/src/test/resources/META-INF/services/com.alibaba.csp.sentinel.spi.TestNoProviderInterface new file mode 100644 index 00000000..e69de29b diff --git a/sentinel-demo/sentinel-demo-slot-chain-spi/src/main/java/com/alibaba/csp/sentinel/demo/slot/DemoSlot.java b/sentinel-demo/sentinel-demo-slot-chain-spi/src/main/java/com/alibaba/csp/sentinel/demo/slot/DemoSlot.java index f43ae3ae..ab15c39d 100644 --- a/sentinel-demo/sentinel-demo-slot-chain-spi/src/main/java/com/alibaba/csp/sentinel/demo/slot/DemoSlot.java +++ b/sentinel-demo/sentinel-demo-slot-chain-spi/src/main/java/com/alibaba/csp/sentinel/demo/slot/DemoSlot.java @@ -19,14 +19,14 @@ import com.alibaba.csp.sentinel.context.Context; import com.alibaba.csp.sentinel.node.DefaultNode; import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot; import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; -import com.alibaba.csp.sentinel.spi.SpiOrder; +import com.alibaba.csp.sentinel.spi.Spi; /** * An example slot that records current context and entry resource. * * @author Eric Zhao */ -@SpiOrder(-3500) +@Spi(order = -3500) public class DemoSlot extends AbstractLinkedProcessorSlot { @Override diff --git a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/HotParamSlotChainBuilder.java b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/HotParamSlotChainBuilder.java index d44aab3d..17aebaa5 100644 --- a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/HotParamSlotChainBuilder.java +++ b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/HotParamSlotChainBuilder.java @@ -21,8 +21,9 @@ import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowSlot; * @author Eric Zhao * @since 0.2.0 * - * @deprecated since 1.7.2, we can use @SpiOrder(-3000) to adjust the order of {@link ParamFlowSlot}, + * @deprecated since 1.7.2, we can use @Spi(order = -3000) to adjust the order of {@link ParamFlowSlot}, * this class is reserved for compatibility with older versions. + * * @see ParamFlowSlot * @see DefaultSlotChainBuilder */ diff --git a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowSlot.java b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowSlot.java index 8bf0dff0..0739a06a 100644 --- a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowSlot.java +++ b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowSlot.java @@ -20,7 +20,7 @@ import com.alibaba.csp.sentinel.node.DefaultNode; import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot; import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; import com.alibaba.csp.sentinel.slots.block.BlockException; -import com.alibaba.csp.sentinel.spi.SpiOrder; +import com.alibaba.csp.sentinel.spi.Spi; import java.util.List; @@ -31,7 +31,7 @@ import java.util.List; * @author Eric Zhao * @since 0.2.0 */ -@SpiOrder(-3000) +@Spi(order = -3000) public class ParamFlowSlot extends AbstractLinkedProcessorSlot { @Override diff --git a/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/command/CommandCenterProvider.java b/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/command/CommandCenterProvider.java index 8d94e630..92df74e7 100644 --- a/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/command/CommandCenterProvider.java +++ b/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/command/CommandCenterProvider.java @@ -17,7 +17,7 @@ package com.alibaba.csp.sentinel.command; import com.alibaba.csp.sentinel.log.RecordLog; import com.alibaba.csp.sentinel.transport.CommandCenter; -import com.alibaba.csp.sentinel.util.SpiLoader; +import com.alibaba.csp.sentinel.spi.SpiLoader; /** * Provider for a universal {@link CommandCenter} instance. @@ -34,7 +34,7 @@ public final class CommandCenterProvider { } private static void resolveInstance() { - CommandCenter resolveCommandCenter = SpiLoader.loadHighestPriorityInstance(CommandCenter.class); + CommandCenter resolveCommandCenter = SpiLoader.of(CommandCenter.class).loadHighestPriorityInstance(); if (resolveCommandCenter == null) { RecordLog.warn("[CommandCenterProvider] WARN: No existing CommandCenter found"); diff --git a/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/command/CommandHandlerProvider.java b/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/command/CommandHandlerProvider.java index 00b8e5de..f4a46bc9 100755 --- a/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/command/CommandHandlerProvider.java +++ b/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/command/CommandHandlerProvider.java @@ -15,13 +15,10 @@ */ package com.alibaba.csp.sentinel.command; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.ServiceLoader; +import java.util.*; import com.alibaba.csp.sentinel.command.annotation.CommandMapping; -import com.alibaba.csp.sentinel.spi.ServiceLoaderUtil; +import com.alibaba.csp.sentinel.spi.SpiLoader; import com.alibaba.csp.sentinel.util.StringUtil; /** @@ -31,8 +28,7 @@ import com.alibaba.csp.sentinel.util.StringUtil; */ public class CommandHandlerProvider implements Iterable { - private final ServiceLoader serviceLoader = ServiceLoaderUtil.getServiceLoader( - CommandHandler.class); + private final SpiLoader spiLoader = SpiLoader.of(CommandHandler.class); /** * Get all command handlers annotated with {@link CommandMapping} with command name. @@ -41,7 +37,8 @@ public class CommandHandlerProvider implements Iterable { */ public Map namedHandlers() { Map map = new HashMap(); - for (CommandHandler handler : serviceLoader) { + List handlers = spiLoader.loadInstanceList(); + for (CommandHandler handler : handlers) { String name = parseCommandName(handler); if (!StringUtil.isEmpty(name)) { map.put(name, handler); @@ -61,7 +58,7 @@ public class CommandHandlerProvider implements Iterable { @Override public Iterator iterator() { - return serviceLoader.iterator(); + return spiLoader.loadInstanceList().iterator(); } private static final CommandHandlerProvider INSTANCE = new CommandHandlerProvider(); diff --git a/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/heartbeat/HeartbeatSenderProvider.java b/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/heartbeat/HeartbeatSenderProvider.java index 57b68662..4891d057 100644 --- a/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/heartbeat/HeartbeatSenderProvider.java +++ b/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/heartbeat/HeartbeatSenderProvider.java @@ -17,7 +17,7 @@ package com.alibaba.csp.sentinel.heartbeat; import com.alibaba.csp.sentinel.log.RecordLog; import com.alibaba.csp.sentinel.transport.HeartbeatSender; -import com.alibaba.csp.sentinel.util.SpiLoader; +import com.alibaba.csp.sentinel.spi.SpiLoader; /** * @author Eric Zhao @@ -32,7 +32,7 @@ public final class HeartbeatSenderProvider { } private static void resolveInstance() { - HeartbeatSender resolved = SpiLoader.loadHighestPriorityInstance(HeartbeatSender.class); + HeartbeatSender resolved = SpiLoader.of(HeartbeatSender.class).loadHighestPriorityInstance(); if (resolved == null) { RecordLog.warn("[HeartbeatSenderProvider] WARN: No existing HeartbeatSender found"); } else { diff --git a/sentinel-transport/sentinel-transport-netty-http/src/main/java/com/alibaba/csp/sentinel/transport/command/NettyHttpCommandCenter.java b/sentinel-transport/sentinel-transport-netty-http/src/main/java/com/alibaba/csp/sentinel/transport/command/NettyHttpCommandCenter.java index 16db0df1..0db625ac 100755 --- a/sentinel-transport/sentinel-transport-netty-http/src/main/java/com/alibaba/csp/sentinel/transport/command/NettyHttpCommandCenter.java +++ b/sentinel-transport/sentinel-transport-netty-http/src/main/java/com/alibaba/csp/sentinel/transport/command/NettyHttpCommandCenter.java @@ -22,7 +22,7 @@ import java.util.concurrent.Executors; import com.alibaba.csp.sentinel.command.CommandHandler; import com.alibaba.csp.sentinel.command.CommandHandlerProvider; import com.alibaba.csp.sentinel.concurrent.NamedThreadFactory; -import com.alibaba.csp.sentinel.spi.SpiOrder; +import com.alibaba.csp.sentinel.spi.Spi; import com.alibaba.csp.sentinel.transport.command.netty.HttpServer; import com.alibaba.csp.sentinel.log.RecordLog; import com.alibaba.csp.sentinel.transport.CommandCenter; @@ -32,7 +32,7 @@ import com.alibaba.csp.sentinel.transport.CommandCenter; * * @author Eric Zhao */ -@SpiOrder(SpiOrder.LOWEST_PRECEDENCE - 100) +@Spi(order = Spi.ORDER_LOWEST - 100) public class NettyHttpCommandCenter implements CommandCenter { private final HttpServer server = new HttpServer(); diff --git a/sentinel-transport/sentinel-transport-netty-http/src/main/java/com/alibaba/csp/sentinel/transport/heartbeat/HttpHeartbeatSender.java b/sentinel-transport/sentinel-transport-netty-http/src/main/java/com/alibaba/csp/sentinel/transport/heartbeat/HttpHeartbeatSender.java index 7fe68379..9aaad80b 100755 --- a/sentinel-transport/sentinel-transport-netty-http/src/main/java/com/alibaba/csp/sentinel/transport/heartbeat/HttpHeartbeatSender.java +++ b/sentinel-transport/sentinel-transport-netty-http/src/main/java/com/alibaba/csp/sentinel/transport/heartbeat/HttpHeartbeatSender.java @@ -18,7 +18,7 @@ package com.alibaba.csp.sentinel.transport.heartbeat; import com.alibaba.csp.sentinel.Constants; import com.alibaba.csp.sentinel.config.SentinelConfig; import com.alibaba.csp.sentinel.log.RecordLog; -import com.alibaba.csp.sentinel.spi.SpiOrder; +import com.alibaba.csp.sentinel.spi.Spi; import com.alibaba.csp.sentinel.transport.HeartbeatSender; import com.alibaba.csp.sentinel.transport.config.TransportConfig; import com.alibaba.csp.sentinel.transport.endpoint.Protocol; @@ -42,7 +42,7 @@ import java.util.List; * @author Carpenter Lee * @author Leo Li */ -@SpiOrder(SpiOrder.LOWEST_PRECEDENCE - 100) +@Spi(order = Spi.ORDER_LOWEST - 100) public class HttpHeartbeatSender implements HeartbeatSender { private final CloseableHttpClient client;