【Java】SPI在Shardingsphere中的使用总结

本文详细介绍了Apache ShardingSphere如何利用SPI(Service Provider Interface)来增强其可扩展性,特别是在主键生成策略方面。以ShardingKeyGenerator接口为例,文章探讨了四种默认实现:自增、自减、UUID和SnowFlake。通过SPI的初始化过程,如NewInstanceServiceLoader的注册和加载机制,展示了如何配置和使用IncrementKeyGenerator作为默认实现。文中还提及了配置文件(如rdb/shardingNamespace.xml)在指定主键生成规则中的作用,并提供了获取和过滤具体实现类的方法。
部署运行你感兴趣的模型镜像

前言

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

 

您可能感兴趣的与本文相关的镜像

ACE-Step

ACE-Step

音乐合成
ACE-Step

ACE-Step是由中国团队阶跃星辰(StepFun)与ACE Studio联手打造的开源音乐生成模型。 它拥有3.5B参数量,支持快速高质量生成、强可控性和易于拓展的特点。 最厉害的是,它可以生成多种语言的歌曲,包括但不限于中文、英文、日文等19种语言

<think>我们参考了站内引用[1]和[2],它们提到了ShardingSphere自定义加密策略的实现方法。在ShardingSphere 4.1.1版本中,自定义加密算法需要实现特定的接口,并在配置文件中指定自定义的加密器。以下是详细步骤: ### 步骤1:实现加密算法接口 ShardingSphere提供了`ShardingEncryptor`接口(在4.1.1版本中,实际接口名为`org.apache.shardingsphere.encrypt.strategy.spi.Encryptor`),自定义加密算法需要实现该接口。该接口包含以下方法: - `init()`: 初始化方法。 - `encrypt(Object plaintext)`: 加密方法,将明文加密为密文。 - `decrypt(Object ciphertext)`: 解密方法,将密文解密为明文。 - `getType()`: 返回加密算法的类型(自定义名称)。 例如,我们实现一个自定义的SHA-256加密算法(注意:SHA-256是单向哈希,通常用于摘要,但这里为了演示同时实现加密和解密,实际中单向哈希无法解密,因此需要根据实际需求选择算法): ```java import org.apache.commons.codec.digest.DigestUtils; import org.apache.shardingsphere.encrypt.strategy.spi.Encryptor; import java.util.Properties; public class Sha256Encryptor implements Encryptor { private Properties properties = new Properties(); @Override public void init() { // 初始化操作,例如加载密钥等 } @Override public String encrypt(Object plaintext) { if (plaintext == null) { return null; } // 使用SHA-256进行加密(实际上是生成摘要) return DigestUtils.sha256Hex(plaintext.toString()); } @Override public Object decrypt(Object ciphertext) { // 注意:SHA-256是单向哈希,无法解密,这里为了接口实现,直接返回原密文 // 实际应用中,如果不需要解密(如密码存储),可以返回原密文或抛出异常 return ciphertext; } @Override public String getType() { return "SHA256"; // 自定义类型名称 } @Override public void setProperties(Properties properties) { this.properties = properties; } } ``` ### 步骤2:配置SPI文件 ShardingSphere通过SPI机制加载自定义加密器。在`resources/META-INF/services`目录下创建文件`org.apache.shardingsphere.encrypt.strategy.spi.Encryptor`,并在文件中写入自定义加密器的全限定类名: ``` com.example.encrypt.Sha256Encryptor ``` ### 步骤3:在ShardingSphere配置文件中使用自定义加密器 在YAML配置文件中,指定加密器类型为自定义的`SHA256`(即`getType()`方法返回的字符串): ```yaml encryptRule: encryptors: my_encryptor: # 自定义加密器的名称 type: SHA256 # 必须与getType()返回的字符串一致 tables: t_user: columns: password: cipherColumn: password_cipher # 存储密文的列 encryptor: my_encryptor # 引用上面定义的加密器 ``` ### 步骤4:验证自定义加密策略 在应用启动后,当向`t_user`表插入数据时,ShardingSphere会自动将`password`列的值使用自定义的`Sha256Encryptor`加密,并存储到`password_cipher`列。查询时,如果需要解密,则会调用`decrypt`方法(但注意,SHA-256不可逆,所以这里解密返回的是密文本身,因此查询条件中如果使用明文密码,需要应用层自己处理成密文后再查询)。 ### 注意事项 1. **算法选择**:如果加密后的数据需要解密(如身份证号等),应使用可逆加密算法(如AES),并实现`decrypt`方法。单向哈希(如SHA-256)适用于密码存储,但无法解密。 2. **密钥管理**:如果自定义加密算法需要密钥,可以通过`properties`属性传入: ```yaml encryptors: my_encryptor: type: SHA256 props: secret.key: your_secret_key # 在自定义加密器中通过this.properties.getProperty("secret.key")获取 ``` 3. **性能考虑**:加密解密操作会带来一定的性能开销,应选择高效算法。 通过以上步骤,即可在ShardingSphere 4.1.1中实现自定义加密算法。更多细节可参考官方文档和示例[^1][^2]。 §§相关问题§§ 1. 如何在ShardingSphere中配置AES加密算法? 2. 自定义加密算法时如何处理无法解密的情况(如哈希算法)? 3. ShardingSphere的加密功能如何与Spring Boot集成? [^1]: ShardingSphere应用专题--4.1.1版本--Sharding-JDBC 字段加密之自定义加密策略(十一) [^2]: ShardingSphere(四)数据脱敏-实现自定义加密策略</think>在 ShardingSphere 4.1.1 中实现自定义加密算法,需要遵循以下步骤(参考引用[1][2]中的实现方法): ### 一、实现自定义加密器 1. **创建加密器类**:实现 `Encryptor` 接口 ```java import org.apache.shardingsphere.encrypt.strategy.spi.Encryptor; import java.util.Properties; public class CustomAESEncryptor implements Encryptor { private static final String SECRET_KEY = "your-256-bit-secret"; private Properties properties; @Override public void init() { // 初始化操作(如密钥加载) } @Override public String encrypt(Object plaintext) { // 实现加密逻辑(示例:AES加密) return AESUtils.encrypt(plaintext.toString(), SECRET_KEY); } @Override public Object decrypt(String ciphertext) { // 实现解密逻辑 return AESUtils.decrypt(ciphertext, SECRET_KEY); } @Override public String getType() { return "CUSTOM_AES"; // 自定义类型标识 } @Override public void setProperties(Properties properties) { this.properties = properties; } } ``` 2. **注册SPI扩展**(关键步骤) 在 `resources/META-INF/services` 目录创建文件: **文件名**:`org.apache.shardingsphere.encrypt.strategy.spi.Encryptor` **内容**: ``` com.yourpackage.CustomAESEncryptor ``` ### 二、配置YAML文件 ```yaml encryptRule: encryptors: custom_encryptor: # 自定义加密器名称 type: CUSTOM_AES # 必须与getType()返回值一致 props: # 可添加自定义参数 key.length: 256 tables: your_table: columns: sensitive_column: # 需要加密的字段 cipherColumn: sensitive_column_cipher # 密文存储列 encryptor: custom_encryptor # 引用自定义加密器 ``` ### 三、验证自定义加密器 1. **启动时检查日志**:查找类似以下日志确认加载成功 ``` [INFO ] Loaded encryptor type: CUSTOM_AES from class: com.yourpackage.CustomAESEncryptor ``` 2. **数据操作验证**: ```sql INSERT INTO your_table (sensitive_column) VALUES ('plaintext'); -- 检查数据库中 sensitive_column_cipher 列是否为加密格式 ``` ### 关键注意事项 1. **SPI注册必须正确**(最常见错误) - 确保SPI文件路径:`resources/META-INF/services/org.apache.shardingsphere.encrypt.strategy.spi.Encryptor` - 文件内容为加密器的**全限定类名** 2. **加解密一致性**: ```java // 必须确保 decrypt(encrypt(plaintext)).equals(plaintext) ``` 3. **算法选择建议**: - 敏感数据使用AES-256等强加密算法 - 不可逆数据(如密码)使用SHA-256等哈希算法[^1] 4. **密钥管理**(生产环境): ```java // 推荐从安全来源获取密钥 String key = System.getenv("ENCRYPTION_KEY"); ``` > 自定义加密器在读写操作中的调用流程: > 写入:明文 → `encrypt()` → 存储密文 > 读取:密文 → `decrypt()` → 返回明文[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值