zoukankan      html  css  js  c++  java
  • 【Java】SPI在Shardingsphere中的使用总结

    前言
      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()));
        }
    }

  • 相关阅读:
    《JavaScript 闯关记》之初探
    《JavaScript 闯关记》之简介
    《JavaScript 闯关记》
    JavaScript检测之basevalidate.js
    如何排版 微信公众号「代码块」
    android开发之路03
    android开发之路02(浅谈BroadcastReceiver)
    android开发之路01
    软件工程复习(一)
    软件工程—人件(一)
  • 原文地址:https://www.cnblogs.com/gossip/p/14207125.html
Copyright © 2011-2022 走看看