前言
Shardingsphere大量使用了SPI技術,提供良好的可擴展性
本文以ShardingKeyGenerator接口為例說明SPI在Shardingsphere中的使用
本文的代碼都可以在項目源碼中找到:
https://github.com/apache/shardingsphere/tree/4.1.1
1. ShardingKeyGenerator接口及其子類
org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator { Comparable<?> generateKey(); }
ShardingKeyGenerator默認實現了四種生成主鍵的方式,分別是自增、自減、UUID、SnowFlake方式




org.apache.shardingsphere.shardingjdbc.orchestration.spring.fixture.IncrementKeyGenerator
org.apache.shardingsphere.shardingjdbc.orchestration.spring.fixture.DecrementKeyGenerator
org.apache.shardingsphere.core.strategy.keygen.UUIDShardingKeyGenerator
org.apache.shardingsphere.core.strategy.keygen.SnowflakeShardingKeyGenerator
2. ShardingKeyGenerator SPI的初始化
package org.apache.shardingsphere.spi.algorithm.keygen; public final class ShardingKeyGeneratorServiceLoader extends TypeBasedSPIServiceLoader<ShardingKeyGenerator> { // 靜態代碼塊注冊ShardingKeyGenerator所有實現類 static { NewInstanceServiceLoader.register(ShardingKeyGenerator.class); } public ShardingKeyGeneratorServiceLoader() { super(ShardingKeyGenerator.class); } }
NewInstanceServiceLoader.register方法實現
- NewInstanceServiceLoader 初始化接口和子類關系的集合,緩存了所有SPI接口和子類的關系
- 集合類型為:Map<Class, Collection<Class<?>>> SERVICE_MAP
package org.apache.shardingsphere.spi; public final class NewInstanceServiceLoader { private static final Map<Class, Collection<Class<?>>> SERVICE_MAP = new HashMap<>(); public static <T> void register(final Class<T> service) { // 循環時注冊子類及建立接口和子類關系,放在map中 for (T each : ServiceLoader.load(service)) { registerServiceClass(service, each); } } @SuppressWarnings("unchecked") private static <T> void registerServiceClass(final Class<T> service, final T instance) { Collection<Class<?>> serviceClasses = SERVICE_MAP.get(service); if (null == serviceClasses) { serviceClasses = new LinkedHashSet<>(); } serviceClasses.add(instance.getClass()); SERVICE_MAP.put(service, serviceClasses); } @SneakyThrows @SuppressWarnings("unchecked") public static <T> Collection<T> newServiceInstances(final Class<T> service) { Collection<T> result = new LinkedList<>(); if (null == SERVICE_MAP.get(service)) { return result; } for (Class<?> each : SERVICE_MAP.get(service)) { result.add((T) each.newInstance()); } return result; } }
3. 使用IncrementKeyGenerator作為ShardingKeyGenerator的默認實現
package org.apache.shardingsphere.shardingjdbc.spring; public class GenerateKeyJUnitTest extends AbstractSpringJUnitTest { @Test public void assertGenerateKeyColumn() { // ShardingRuntimeContext 默認實現:org.apache.shardingsphere.shardingjdbc.spring.datasource.SpringShardingDataSource ShardingRuntimeContext runtimeContext = shardingDataSource.getRuntimeContext(); ShardingRule shardingRule = runtimeContext.getRule(); // ShardingKeyGenerator默認實現:org.apache.shardingsphere.shardingjdbc.spring.fixture.IncrementKeyGenerator ShardingKeyGenerator defaultKeyGenerator = shardingRule.getDefaultShardingKeyGenerator(); } }
上述代碼中:ShardingKeyGenerator初始化為IncrementKeyGenerator類的原因
配置信息:classpath:META-INF/rdb/shardingNamespace.xml
// 這里指定了初始化的默認實現 <sharding:key-generator id="defaultKeyGenerator" type="INCREMENT" column="id" /> <sharding:data-source id="shardingDataSource"> <sharding:sharding-rule data-source-names="dbtbl_0,dbtbl_1" default-data-source-name="dbtbl_0" default-key-generator-ref="defaultKeyGenerator"> </sharding:sharding-rule> </sharding:data-source>
- 通過配置(default-key-generator-ref="defaultKeyGenerator")指定了shardingDataSource數據源默認的主鍵生成規則
- <sharding:key-generator id="defaultKeyGenerator" type="INCREMENT" column="id" />
- type="INCREMENT" 對應的是IncrementShardingKeyGenerator類的type屬性值
package org.apache.shardingsphere.shardingjdbc.orchestration.api.yaml.fixture; public final class IncrementShardingKeyGenerator implements ShardingKeyGenerator { private static final AtomicInteger SEQUENCE = new AtomicInteger(100); @Getter private final String type = "INCREMENT"; @Getter @Setter private Properties properties = new Properties(); @Override public Comparable<?> generateKey() { return SEQUENCE.incrementAndGet(); } }
- 使用其它的主鍵生成規則
<sharding:key-generator id="defaultKeyGenerator" type="SNOWFLAKE" column="id" />
ShardingRule的初始化(ShardingRule包含了ShardingKeyGenerator的默認實現)
package org.apache.shardingsphere.shardingjdbc.spring.datasource; public final class SpringShardingDataSource extends ShardingDataSource { public SpringShardingDataSource(final Map<String, DataSource> dataSourceMap, final ShardingRuleConfiguration shardingRuleConfiguration, final Properties props) throws SQLException { super(dataSourceMap, new ShardingRule(shardingRuleConfiguration, dataSourceMap.keySet()), props); } }
初始化defaultShardingKeyGenerator 的代碼
defaultShardingKeyGenerator = createDefaultKeyGenerator(shardingRuleConfig.getDefaultKeyGeneratorConfig());
package org.apache.shardingsphere.core.rule; public class ShardingRule implements BaseRule { public ShardingRule(final ShardingRuleConfiguration shardingRuleConfig, final Collection<String> dataSourceNames) { // 初始化defaultShardingKeyGenerator defaultShardingKeyGenerator = createDefaultKeyGenerator(shardingRuleConfig.getDefaultKeyGeneratorConfig()); } private ShardingKeyGenerator createDefaultKeyGenerator(final KeyGeneratorConfiguration keyGeneratorConfiguration) { ShardingKeyGeneratorServiceLoader serviceLoader = new ShardingKeyGeneratorServiceLoader(); return containsKeyGeneratorConfiguration(keyGeneratorConfiguration) ? serviceLoader.newService(keyGeneratorConfiguration.getType(), keyGeneratorConfiguration.getProperties()) : serviceLoader.newService(); } }
獲取指定的主鍵實現類(loadTypeBasedServices方法)
1. NewInstanceServiceLoader.newServiceInstances(classType):獲取ShardingKeyGenerator接口的所有已實現類緩存集合
2. Collections2.filter(NewInstanceServiceLoader.newServiceInstances(classType), input -> type.equalsIgnoreCase(input.getType())):根據參數type從緩存集合獲取具體的實現,這里是IncrementShardingKeyGenerator
package org.apache.shardingsphere.spi; @RequiredArgsConstructor public abstract class TypeBasedSPIServiceLoader<T extends TypeBasedSPI> { private final Class<T> classType; public final T newService(final String type, final Properties props) { Collection<T> typeBasedServices = loadTypeBasedServices(type); if (typeBasedServices.isEmpty()) { throw new RuntimeException(String.format("Invalid `%s` SPI type `%s`.", classType.getName(), type)); } T result = typeBasedServices.iterator().next(); result.setProperties(props); return result; } public final T newService() { T result = loadFirstTypeBasedService(); result.setProperties(new Properties()); return result; } private Collection<T> loadTypeBasedServices(final String type) { return Collections2.filter(NewInstanceServiceLoader.newServiceInstances(classType), input -> type.equalsIgnoreCase(input.getType())); } }