点击下方“JavaEdge”,选择“设为星标”
第一时间关注技术干货!
免责声明~ 任何文章不要过度深思! 万事万物都经不起审视,因为世上没有同样的成长环境,也没有同样的认知水平,更「没有适用于所有人的解决方案」; 不要急着评判文章列出的观点,只需代入其中,适度审视一番自己即可,能「跳脱出来从外人的角度看看现在的自己处在什么样的阶段」才不为俗人。 怎么想、怎么做,全在乎自己「不断实践中寻找适合自己的大道」
系统集成,即ShardingSphere 和 Spring 框架的集成。
ShardingSphere实现两种系统集成机制:
命名空间(namespace),扩展 Spring Schema 来实现与 Spring 框架集成
编写自定义 starter 组件完成与 Spring Boot 集成
扩展性角度,基于 XML Schema 的扩展机制常见而实用。Spring允许我们自定义 XML 结构,并且用自己的 Bean 解析器解析。通过对 Spring Schema 的扩展,ShardingSphere 可以完成与 Spring 框架的有效集成。
1.1 基于命名空间集成 Spring 的通用开发流程
基于命名空间机制实现与 Spring 的整合,开发通常采用固定流程:
编写业务对象
编写XSD文件
编写BeanDefinitionParser实现类
编写NamespaceHandler实现类
编写 spring.handlers 和 spring.schemas 配置文件
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 dataSourceMap, final ShardingRuleConfiguration shardingRuleConfiguration, final Properties props) throws SQLException {
super(dataSourceMap, new ShardingRule(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:table-rules> … sharding:sharding-rule> sharding:data-source>
在 sharding-jdbc-spring-namespace 代码工程的 META-INF/namespace 文件夹找到 sharding.xsd 文件,其基本结构:
xsd:all>
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/beans
ShardingSphere 自身的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, new ShardingStrategyBeanDefinitionParser()); … } }
直接使用 registerBeanDefinitionParser 方法来完成标签项与具体的 BeanDefinitionParser 类之间的对应关系。
看ShardingDataSourceBeanDefinitionParser#parseInternal:
@Override
protected 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");
return factory.getBeanDefinition();
}
自定义一个 BeanDefinitionBuilder 并将其绑定到前面定义的业务对象类 SpringShardingDataSource。然后,通过三个 addConstructorArgValue 方法的调用,分别为 SpringShardingDataSource 构造函数中所需的 dataSourceMap、shardingRuleConfiguration 以及 props 参数进行赋值。
parseDataSources方法
private Map parseDataSources (final Element element) {
Element shardingRuleElement = DomUtils.getChildElementByTagName(element, ShardingDataSourceBeanDefinitionParserTag.SHARDING_RULE_CONFIG_TAG);
List
dataSources = Splitter.on( ",").trimResults().splitToList(shardingRuleElement.getAttribute(ShardingDataSourceBeanDefinitionParserTag.DATA_SOURCE_NAMES_TAG)); Map result = new ManagedMap<>(dataSources.size()); for (String each : dataSources) { result.put(each, new RuntimeBeanReference(each)); } return result; }
获取配置的“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-starter
sharding-jdbc-orchestration-spring-boot-starter
Spring 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 被加载到运行时环境。
![](http://dingyue.ws.126.net/2024/0521/63cedaa7j00sdud090031d200u0007fg00i9004i.jpg)
@Configuration:表明该类是一个配置类,可启动组件扫描,将@Bean 注解的实例化为 bean
@ComponentScan:扫描基于 @Component 等注解所标注的类所在包下的所有需要注入的类,并把相关 Bean 定义批量加载到IoC容器
需扫描的包路径位于另一工程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, new ShardingRuleConfigurationYamlSwapper().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 = new MasterSlaveRuleCondition().getMatchOutcome(conditionContext, annotatedTypeMetadata).isMatch();
boolean isEncryptRule = new EncryptRuleCondition().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 :
@Override
public 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 (final ReflectiveOperationException ex) {
throw new ShardingException("Can't find datasource type!", ex);
} catch (final NamingException namingEx) {
throw new ShardingException("Can't 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
类:
import org.apache.shardingsphere.driver.api.yaml.YamlShardingSphereDataSourceFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.io.File;
@SpringBootApplication
@EnableTransactionManagement
public 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 的分库分表和读写分离功能,构建一个高效且可扩展的分布式数据库系统。
公众号:JavaEdge 专注分享软件开发全生态相关技术文章、视频教程资源、热点资讯等,如果喜欢我的分享,给 点一个赞 或者 ➕关注 都是对我最大的支持。
欢迎长按图片加好友
,我会第一时间和你分享软件行业趋势
,面试资源
,学习途径
等等。
添加好友备注【技术群交流】拉你进技术交流群
关注公众号后,在后台私信:
更多教程资源应有尽有,欢迎
关注,慢慢获取
热门跟贴