前言
Shardingsphere大量使用了SPI技术,提供良好的可扩展性
参考:https://shardingsphere.apache.org/document/legacy/4.x/document/en/features/spi/
SPI基本概念:https://www.cnblogs.com/jy107600/p/11464985.html
本文以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 的代码
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()));
}
}