ShardingSphere如何完成与Spring家族无缝整合的?
来源:JavaEdge
系统集成,即ShardingSphere 和 Spring 框架的集成。
ShardingSphere实现两种系统集成机制:
命名空间(namespace),扩展 Spring Schema 来实现与 Spring 框架集成编写自定义 starter 组件完成与 Spring Boot 集成1 基于命名空间集成 Spring
扩展性角度,基于 XML Schema 的扩展机制常见而实用。Spring允许我们自定义 XML 结构,并且用自己的 Bean 解析器解析。通过对 Spring Schema 的扩展,ShardingSphere 可以完成与 Spring 框架的有效集成。
1.1 基于命名空间集成 Spring 的通用开发流程
基于命名空间机制实现与 Spring 的整合,开发通常采用固定流程:
编写业务对象编写XSD文件编写BeanDefinitionParser实现类编写NamespaceHandler实现类编写 spring.handlers 和 spring.schemas 配置文件1.2 ShardingSphere 集成 Spring
ShardingSphere存在两个“spring-namespace”结尾的代码工程:
sharding-jdbc-spring-namespace
sharding-jdbc-orchestration-spring-namespace
关注编排治理相关功能的集成,相对简单。命名空间机制的实现过程也基本一致,因此,以 sharding-jdbc-spring-namespace 为例讨论。
sharding-jdbc-spring-namespace又包含:
普通分片读写分离数据脱敏三块核心功能的集成内容,实现也都是采用类似方式,因此也不重复说明,以普通分片为例介绍。
1.3 SpringShardingDataSource
专门用于与 Spring 进行集成的业务对象类:
public class SpringShardingDataSource extends ShardingDataSource{
public SpringShardingDataSource(final Map<String, DataSource> dataSourceMap, final ShardingRuleConfiguration shardingRuleConfiguration, final Properties props) throws SQLException{
super(dataSourceMap, newShardingRule(shardingRuleConfiguration, dataSourceMap.keySet()), props);
}
}
只是对 ShardingDataSource 的简单封装,无任何实际操作。
1.4 配置项标签的定义类
定义标签的名称。ShardingSphere的这些类都以“BeanDefinitionParserTag”结尾,如ShardingDataSourceBeanDefinitionParserTag:
public final class ShardingDataSourceBeanDefinitionParserTag{
public static final String ROOT_TAG = "data-source";
public static final String SHARDING_RULE_CONFIG_TAG = sharding-rule";
public static final String PROPS_TAG = " props";
public static final String DATA_SOURCE_NAMES_TAG = " data-source-names";
public static final String DEFAULT_DATA_SOURCE_NAME_TAG = " default-data-source-name";
public static final String TABLE_RULES_TAG = " table-rules";
…
}
定义一批 Tag、Attribute。可以对照如下所示的基于 XML 的配置示例来对这些定义的配置项进行理解:
<sharding:data-source id="shardingDataSource"> <sharding:sharding-rule data-source-names="ds0,ds1"> <sharding:table-rules> <sharding:table-rule …/> <sharding:table-rule …/>…
</sharding:table-rules>…
</sharding:sharding-rule></sharding:data-source>在 sharding-jdbc-spring-namespace 代码工程的 META-INF/namespace 文件夹找到 sharding.xsd 文件,其基本结构:
<xsd:schema xmlns="http://shardingsphere.apache.org/schema/shardingsphere/sharding" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:encrypt="http://shardingsphere.apache.org/schema/shardingsphere/encrypt" targetNamespace="http://shardingsphere.apache.org/schema/shardingsphere/sharding" elementFormDefault="qualified" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://shardingsphere.apache.org/schema/shardingsphere/encrypt http://shardingsphere.apache.org/schema/shardingsphere/encrypt/encrypt.xsd"> <xsd:import namespace="http://www.springframework.org/schema/beans" schemaLocation="http://www.springframework.org/schema/beans/spring-beans.xsd" /> <xsd:import namespace="http://shardingsphere.apache.org/schema/shardingsphere/encrypt" schemaLocation="http://shardingsphere.apache.org/schema/shardingsphere/encrypt/encrypt.xsd"/> <xsd:element name="data-source"> <xsd:complexType> <xsd:all> <xsd:element ref="sharding-rule" /> <xsd:element ref="props" minOccurs="0" /> </xsd:all> <xsd:attribute name="id" type="xsd:string" use="required" /> </xsd:complexType> </xsd:element>…
</xsd:schema>“data-source”这 element包含“sharding-rule”和“props”这两个子 element。
“data-source”还包含一个“id”属性。对“sharding-rule”,可有很多内嵌的属性,sharding.xsd 文件中对这些属性都做了定义。
sharding.xsd 中通过使用 xsd:import 标签还引入两个 namespace:
Spring 中的http://www.springframework.org/schema/beansShardingSphere 自身的http://shardingsphere.apache.org/schema/shardingsphere/encrypt,该命名空间的定义位于与 sharding.xsd 同目录下的 encrypt.xsd文件中有了业务对象类、XSD 文件的定义,来看 NamespaceHandler 实现类 ShardingNamespaceHandler:
public final class ShardingNamespaceHandler extends NamespaceHandlerSupport{
@Overridepublic void init() { registerBeanDefinitionParser(ShardingDataSourceBeanDefinitionParserTag.ROOT_TAG, new ShardingDataSourceBeanDefinitionParser()); registerBeanDefinitionParser(ShardingStrategyBeanDefinitionParserTag.STANDARD_STRATEGY_ROOT_TAG, newShardingStrategyBeanDefinitionParser()); … } }
直接使用 registerBeanDefinitionParser 方法来完成标签项与具体的 BeanDefinitionParser 类之间的对应关系。
看ShardingDataSourceBeanDefinitionParser#parseInternal:
@Overrideprotected AbstractBeanDefinition parseInternal(final Element element, final ParserContext parserContext){
//构建针对 SpringShardingDataSource 的 BeanDefinitionBuilder BeanDefinitionBuilder factory = BeanDefinitionBuilder.rootBeanDefinition(SpringShardingDataSource.class);
//解析构造函数中的 DataSource 参数factory.addConstructorArgValue(parseDataSources(element));
//解析构造函数中 ShardingRuleConfiguration 参数 factory.addConstructorArgValue(parseShardingRuleConfiguration(element)); //解析构造函数中 Properties 参数factory.addConstructorArgValue(parseProperties(element, parserContext));
factory.setDestroyMethodName("close");
returnfactory.getBeanDefinition();
}
自定义一个 BeanDefinitionBuilder 并将其绑定到前面定义的业务对象类 SpringShardingDataSource。然后,通过三个 addConstructorArgValue 方法的调用,分别为 SpringShardingDataSource 构造函数中所需的 dataSourceMap、shardingRuleConfiguration 以及 props 参数进行赋值。
parseDataSources方法private Map<String, RuntimeBeanReference> parseDataSources(final Element element){
Element shardingRuleElement = DomUtils.getChildElementByTagName(element, ShardingDataSourceBeanDefinitionParserTag.SHARDING_RULE_CONFIG_TAG);
List<String> dataSources = Splitter.on(",").trimResults().splitToList(shardingRuleElement.getAttribute(ShardingDataSourceBeanDefinitionParserTag.DATA_SOURCE_NAMES_TAG));
Map<String, RuntimeBeanReference> result = newManagedMap<>(dataSources.size());
for(String each : dataSources) {
result.put(each, newRuntimeBeanReference(each));
}
returnresult;
}
获取配置的“ds0,ds1”字符串并拆分,然后基于每个代表具体 DataSource 的名称构建 RuntimeBeanReference 对象并进行返回,这样就可以把在 Spring 容器中定义的其他 Bean 加载到 BeanDefinitionBuilder。
最后,在 META-INF 目录提供spring.schemas 文件:
http\://shardingsphere.apache.org/schema/shardingsphere/sharding/sharding.xsd=META-INF/namespace/sharding.xsd
http\://shardingsphere.apache.org/schema/shardingsphere/masterslave/master-slave.xsd=META-INF/namespace/master-slave.xsd
http\://shardingsphere.apache.org/schema/shardingsphere/encrypt/encrypt.xsd=META-INF/namespace/encrypt.xsd
spring.handlers 内容:
http\://shardingsphere.apache.org/schema/shardingsphere/sharding=org.apache.shardingsphere.shardingjdbc.spring.namespace.handler.ShardingNamespaceHandler
http\://shardingsphere.apache.org/schema/shardingsphere/masterslave=org.apache.shardingsphere.shardingjdbc.spring.namespace.handler.MasterSlaveNamespaceHandler
http\://shardingsphere.apache.org/schema/shardingsphere/encrypt=org.apache.shardingsphere.shardingjdbc.spring.namespace.handler.EncryptNamespaceHandler
ShardingSphere 中基于命名空间机制与 Spring 进行系统集成的实现过程介绍完。
2 自定义 starter 集成 Spring Boot
ShardingSphere提供:
sharding-jdbc-spring-boot-startersharding-jdbc-orchestration-spring-boot-starterSpring Boot先关注 META-INF 的 spring.factories 文件。Spring Boot 提供
2.1 SpringFactoriesLoader 类
类似SPI 机制,只不过以服务接口命名的文件放在 META-INF/spring.factories,对应 Key 为 EnableAutoConfiguration。
SpringFactoriesLoader 查找所有 META-INF/spring.factories 下的配置文件,把 Key=EnableAutoConfiguration 对应配置项通过反射,实例化为配置类并加载到容器。如sharding-jdbc-spring-boot-starter 中的文件内容:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.apache.shardingsphere.shardingjdbc.spring.boot.SpringBootConfiguration
SpringBootConfiguration类就会在 Spring Boot 启动过程中都能够通过 SpringFactoriesLoader 被加载到运行时环境。
2.2 SpringBootConfiguration的注解

需扫描的包路径位于另一工程sharding-spring-boot-util的org.apache.shardingsphere.spring.boot.converter 包。
@EnableConfigurationProperties 注解:使添加 @ConfigurationProperties 注解的类生效Spring Boot 中,若某类只用 @ConfigurationProperties 注解,然后该类没有在扫描路径下或没使用 @Component 等注解,就会导致无法被扫描为 bean,须在配置类使用 @EnableConfigurationProperties 注解去指定这个类,才能使 @ConfigurationProperties 生效,并作为一个 bean 添加进 spring 容器。
@EnableConfigurationProperties 注解包含四个具体的ConfigurationProperties,如SpringBootShardingRuleConfigurationProperties定义,直接继承 sharding-core-common 的 YamlShardingRuleConfiguration:
@ConfigurationProperties(prefix = "spring.shardingsphere.sharding")
public class SpringBootShardingRuleConfigurationProperties extends YamlShardingRuleConfiguration{
}
@ConditionalOnProperty,只有当所提供的属性属于 true 时才实例化 Bean。
@AutoConfigureBefore用在类名上,标识在加载当前类之前需要加载注解中所设置的配置类。明确在加载 SpringBootConfiguration 类之前,Spring Boot 会先加载 DataSourceAutoConfiguration。这作用与创建各种 DataSource 相关。
2.3 SpringBootConfiguration 中的功能
ShardingSphere对外入口就是各种 DataSource,因此SpringBootConfiguration中提供一批创建不同 DataSource 的入口方法,如shardingDataSource:
@Bean@Conditional(ShardingRuleCondition.class)
public DataSource shardingDataSource() throws SQLException{
return ShardingDataSourceFactory.createDataSource(dataSourceMap, newShardingRuleConfigurationYamlSwapper().swap(shardingRule), props.getProps());
}
该方法上添加两个注解:
@Bean@Conditional 注解,只有满足指定条件的情况下才加载这 Bean。@Conditional 注解设置了一个 ShardingRuleCondition:public final class ShardingRuleCondition extends SpringBootCondition{
@Override public ConditionOutcome getMatchOutcome(final ConditionContext conditionContext, final AnnotatedTypeMetadata annotatedTypeMetadata){
boolean isMasterSlaveRule = newMasterSlaveRuleCondition().getMatchOutcome(conditionContext, annotatedTypeMetadata).isMatch();
boolean isEncryptRule = newEncryptRuleCondition().getMatchOutcome(conditionContext, annotatedTypeMetadata).isMatch();
return isMasterSlaveRule || isEncryptRule ? ConditionOutcome.noMatch("Have found master-slave or encrypt rule in environment") : ConditionOutcome.match();
}
}
标准的 SpringBootCondition,实现 getMatchOutcome 抽象方法。代表一种用于注册类或加载 Bean 的条件。ShardingRuleCondition 类的实现上分别调用MasterSlaveRuleCondition、EncryptRuleCondition 判断是否满足这两个 SpringBootCondition。对ShardingRuleCondition,只有在两个条件都不满足的情况下才被加载。
SpringBootConfiguration 实现 Spring 的 EnvironmentAware 接口。Spring Boot中,当一个类实现了 EnvironmentAware 接口并重写setEnvironment,在代码工程启动时就可获得 application.properties 配置文件中各个配置项的属性值。SpringBootConfiguration#setEnvironment :
@Overridepublic final void setEnvironment(final Environment environment){
String prefix = "spring.shardingsphere.datasource.";
for(String each : getDataSourceNames(environment, prefix)) {
try{
dataSourceMap.put(each, getDataSource(environment, prefix, each));
} catch (finalReflectiveOperationException ex) {
throw new ShardingException("Cant find datasource type!", ex);
} catch (finalNamingException namingEx) {
throw new ShardingException("Cant find JNDI datasource!", namingEx);
}
}
}
获取“spring.shardingsphere.datasource.name”或“spring.shardingsphere.datasource.names”配置项,然后根据该配置项中所指定的 DataSource 信息构建新的 DataSource 并加载到 dataSourceMap 这个 LinkedHashMap。
spring.shardingsphere.datasource.names=ds0,ds1
spring.shardingsphere.datasource.ds0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds0.url=jdbc:mysql://localhost/ds0
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=root
spring.shardingsphere.datasource.ds1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds1.url=jdbc:mysql://localhost/ds1
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=root
3 总结
如需要实现一个自定义的框架或工具类,从面向开发人员的角度讲,最好能与 Spring 等主流的开发框架进行集成,以便提供最低的学习和维护成本。与 Spring 框架的集成过程都有固定的开发步骤,就可模仿 ShardingSphere 中的做法自己实现这些步骤。
FAQ
Q:ShardingSphere 集成 Spring Boot 时,SpringBootConfiguration 类上的注解有哪些,分别起啥作用?
在集成 ShardingSphere 和 Spring Boot 时,通常在 SpringBootConfiguration 类上使用以下几个注解:
@SpringBootApplication
@Configuration:表明这个类是一个 Spring 配置类,可以定义 Spring beans。@EnableAutoConfiguration:启用 Spring Boot 的自动配置机制,尝试根据类路径下的 jar 包和已定义的 beans 自动配置 Spring 应用程序。@ComponentScan:告诉 Spring 扫描 @Component (包括 @Service, @Repository, @Controller 等) 注解所在的包,以便发现和注册这些 beans。作用:这是一个组合注解,包含了 @Configuration, @EnableAutoConfiguration, 和 @ComponentScan 三个注解的功能。详细解释:@EnableTransactionManagement
作用:启用 Spring 的注解驱动的事务管理功能,允许在方法上使用 @Transactional 注解进行事务管理。@ShardingSphereDataSource
作用:这是一个 ShardingSphere 提供的注解,用于创建和配置 ShardingSphere 的数据源,支持分库分表和读写分离等功能。详细解释:该注解的具体作用包括配置 ShardingSphere 的数据源策略、分片策略、读写分离策略等。典型的 SpringBootConfiguration 类:
importorg.apache.shardingsphere.driver.api.yaml.YamlShardingSphereDataSourceFactory;
importorg.springframework.boot.SpringApplication;
importorg.springframework.boot.autoconfigure.SpringBootApplication;
importorg.springframework.context.annotation.Bean;
importorg.springframework.transaction.annotation.EnableTransactionManagement;
importjavax.sql.DataSource;
importjava.io.File;
@SpringBootApplication@EnableTransactionManagementpublic class SpringBootConfiguration{
public static void main(String[] args){
SpringApplication.run(SpringBootConfiguration.class, args);
}
@Bean public DataSource dataSource() throws Exception{
// 假设有一个 sharding 配置文件 sharding-databases-tables.yaml return YamlShardingSphereDataSourceFactory.createDataSource(new File("path/to/sharding-databases-tables.yaml"));
}
}
详解各个注解的作用
@SpringBootApplication
@Configuration:将类标识为配置类,可以用来定义 Spring beans。@EnableAutoConfiguration:启用 Spring Boot 自动配置,这样 Spring Boot 会根据类路径下的 jar 包和已定义的 beans 自动配置 Spring 应用。@ComponentScan:告诉 Spring 在指定包下扫描 @Component 注解的类并注册这些类为 Spring beans。@EnableTransactionManagement
允许使用 @Transactional 注解来管理事务,使得可以通过注解的方式声明事务的范围。@ShardingSphereDataSource
配置 ShardingSphere 数据源,使得可以使用 ShardingSphere 提供的分库分表和读写分离功能。这个注解需要结合具体的 ShardingSphere 配置来使用,如上面的 YamlShardingSphereDataSourceFactory.createDataSource 方法。通过这些注解,可以快速且简洁地配置和使用 ShardingSphere 与 Spring Boot 集成,利用 Spring Boot 的自动配置和管理功能,加上 ShardingSphere 的分库分表和读写分离功能,构建一个高效且可扩展的分布式数据库系统。
扫一扫,关注我们