前面我们花了两篇内容介绍了ShardingSphere中的微内核模式。微内核是一种插件化的架构模式,通过SPI实现动态加载系统扩展点机制,在ShardingSphere应用非常广泛,在本系列后续的很多主题中都会涉及到该模式的具体应用场景。
今天我们继续回到ShardingSphere的SQL解析引擎的主流程,我们回顾SQLParseEngine中的核心逻辑,即通过解析获取一个SQLStatement对象,核心代码就是下面这句:
SQLStatement result = new SQLParseKernel(ParseRuleRegistry.getInstance(), databaseTypeName, sql).parse();
我们看到这里有一个ParseRuleRegistry对象,该类就是一个规则注册表,保存着各种解析规则信息。ParseRuleRegistry类在《ShardingSphere源码解析之SQL解析引擎(二)》中已经有所介绍,该类位于shardingsphere-sql-parser-engine工程的org.apache.shardingsphere.sql.parser.core.rule.registry包中。我们同样关注ParseRuleRegistry中的核心变量,首先是如下所示的三个Loader类:
private final ExtractorRuleDefinitionEntityLoader extractorRuleLoader = new ExtractorRuleDefinitionEntityLoader();
private final FillerRuleDefinitionEntityLoader fillerRuleLoader = new FillerRuleDefinitionEntityLoader();
private final SQLStatementRuleDefinitionEntityLoader statementRuleLoader = new SQLStatementRuleDefinitionEntityLoader();
我们在《ShardingSphere源码解析之SQL解析引擎(一)》中知道整个SQL解析引擎需要完成SQLSegment的提取(SQLSegmentExtractor)以及SQLStatement的填充(SQLStatementFiller),而这些都依赖于SQLStatementRule的解析。所以在ParseRuleRegistry中就包含了针对如上所示的SQLStatementRule、ExtractorRule和FillerRule的三种RuleDefinitionEntityLoader,而它们都实现了RuleDefinitionEntityLoader接口,我们来看一下这个接口定义,如下所示:
public interface RuleDefinitionEntityLoader {
RuleDefinitionEntity load(String ruleDefinitionFile);
}
可以看到这个接口从基于XML的规则定义文件加载规则定义实体RuleDefinitionEntity,前面提到的三种RuleDefinitionEntityLoader都实现了这个接口。我们先来看SQLStatementRuleDefinitionEntityLoader,如下所示:
public final class SQLStatementRuleDefinitionEntityLoader implements RuleDefinitionEntityLoader {
@Override
@SneakyThrows
public SQLStatementRuleDefinitionEntity load(final String sqlStatementRuleDefinitionFile) {
InputStream inputStream = SQLStatementRuleDefinitionEntityLoader.class.getClassLoader().getResourceAsStream(sqlStatementRuleDefinitionFile);
Preconditions.checkNotNull(inputStream, "Cannot load SQL statement rule definition file: %s, ", sqlStatementRuleDefinitionFile);
return (SQLStatementRuleDefinitionEntity) JAXBContext.newInstance(SQLStatementRuleDefinitionEntity.class).createUnmarshaller().unmarshal(inputStream);
}
}
在这里我们发现了JAXBContext对象,显然用到了JAXB。JAXB(Java Architecture for XML Binding) 是一个业界的标准,是一项可以根据XML Schema产生Java类的技术。该过程中,JAXB也提供了将XML实例文档反向生成Java对象树的方法,并能将Java对象树的内容重新写到XML实例文档。从另一方面来讲,JAXB提供了快速而简便的方法将XML模式绑定到Java表示,从而使得Java开发者在Java应用程序中能方便地结合XML数据和处理函数。关于JAXB的介绍不是我们的重点,这里不做具体展开。
在ShardingSphere的shardingsphere-sql-parser-engine工程中,提供了基于JAXB的各种实体定义以及RuleDefinitionEntityLoader的实现,如下所示:
以SQLStatementRuleDefinitionEntity为例,我们看到它的定义如下所示:
@XmlRootElement(name = "sql-statement-rule-definition")
@Getter
public final class SQLStatementRuleDefinitionEntity implements RuleDefinitionEntity {
@XmlElement(name = "sql-statement-rule")
private Collection<SQLStatementRuleEntity> rules = new LinkedList<>();
}
这里出现了JAXB中的XML注解@XmlRootElement和@XmlElement。然后我们再看其中的SQLStatementRuleEntity定义,如下所示:
@XmlAccessorType(XmlAccessType.FIELD)
@Getter
public final class SQLStatementRuleEntity {
@XmlAttribute(required = true)
private String context;
@XmlAttribute(name = "sql-statement-class", required = true)
private String sqlStatementClass;
@XmlAttribute(name = "extractor-rule-refs", required = true)
private String extractorRuleRefs;
}
这里用到了@XmlAttribute注解。通过这些注解我们就能根据XML文件自动将配置项转化为Java对象。
JAXB是根据文件名获取XML然后再进行解析,我们在RuleDefinitionFileConstant类中找到了获取XML文件名的定义,如下所示:
public static String getSQLStatementRuleDefinitionFile(final String databaseTypeName) {
return Joiner.on('/').join(ROOT_PATH, databaseTypeName.toLowerCase(), SQL_STATEMENT_RULE_DEFINITION_FILE_NAME);
}
该文件是根据数据库类型进行获取,在shardingsphere-sql-parser-mysql工程中,我们找到了该路径,发现用于SQL解析的三个配置文件都是放在这个路径之下:
我们打开sql-statement-rule-definition.xml文件,截取其中的一条配置项,如下所示:
<sql-statement-rule-definition>
<sql-statement-rule context="select" sql-statement-class="org.apache.shardingsphere.sql.parser.sql.statement.dml.SelectStatement" extractor-rule-refs="tableReferences, columns, selectItems, where, predicate, groupBy, orderBy, limit, subqueryPredicate, lock" />
</sql-statement-rule-definition>
可以看到这里的配置项属性与SQLStatementRuleDefinitionEntity和SQLStatementRuleEntity是一致的。其他的RuleDefinitionEntity和RuleEntity也是一样的处理方式。
最后,我们回到ParseRuleRegistry,发现如下代码:
static {
NewInstanceServiceLoader.register(SQLParserEntry.class);
instance = new ParseRuleRegistry();
}
在这里,我们发现SQLParserEntry的注册过程实际上发生在ParseRuleRegistry的这个静态方法中。当系统启动时,ShardingSphere就把SQLParserEntry相关的SPI实现类加载到了内存中。
然后,在ParseRuleRegistry的构造函数中,我们也发现了通过各种RuleDefinitionEntityLoader加载RuleDefinitionEntity的入口,如下所示:
private ParseRuleRegistry() {
initParseRuleDefinition();
}
private void initParseRuleDefinition() {
ExtractorRuleDefinitionEntity generalExtractorRuleEntity = extractorRuleLoader.load(RuleDefinitionFileConstant.getExtractorRuleDefinitionFile());
FillerRuleDefinitionEntity generalFillerRuleEntity = fillerRuleLoader.load(RuleDefinitionFileConstant.getFillerRuleDefinitionFile());
for (SQLParserEntry each : NewInstanceServiceLoader.newServiceInstances(SQLParserEntry.class)) {
String databaseTypeName = each.getDatabaseTypeName();
fillerRuleDefinitions.put(databaseTypeName, createFillerRuleDefinition(generalFillerRuleEntity, databaseTypeName));
sqlStatementRuleDefinitions.put(databaseTypeName, createSQLStatementRuleDefinition(generalExtractorRuleEntity, databaseTypeName));
}
}
这里,我们再次看到了通过NewInstanceServiceLoader加载SQLParserEntry的SPI实现类的过程,用于获取所需的DatabaseTypeName。然后根据 DatabaseTypeName找到对应的数据库并加载该数据库对应的配置文件。
请注意,在ParseRuleRegistry中,我们只找到了如下所示的两个变量定义,以及它们对外暴露领域对象SQLStatementRule和SQLSegmentFiller的方法:
private final Map<String, FillerRuleDefinition> fillerRuleDefinitions = new HashMap<>();
private final Map<String, SQLStatementRuleDefinition> sqlStatementRuleDefinitions = new HashMap<>();
public SQLStatementRule getSQLStatementRule(final String databaseTypeName, final String contextClassName) {
return sqlStatementRuleDefinitions.get(databaseTypeName).getSQLStatementRule(contextClassName);
}
public Optional<SQLSegmentFiller> findSQLSegmentFiller(final String databaseTypeName, final Class<? extends SQLSegment> sqlSegmentClass) {
return Optional.fromNullable(fillerRuleDefinitions.get(databaseTypeName).getFiller(sqlSegmentClass));
}
为什么没有单独的ExtractorRuleDefinition呢?这是因为SQLStatementRuleDefinition中包含了ExtractorRuleDefinition,也就是说SQLStatementRule中包含了SQLSegmentExtractor的定义,这点从sql-statement-rule-definition.xml的“extractor-rule-refs”配置项定义以及SQLStatementRule的变量定义中就能得到明确:
<sql-statement-rule context="select" sql-statement-class="org.apache.shardingsphere.sql.parser.sql.statement.dml.SelectStatement" extractor-rule-refs="tableReferences, columns, selectItems, where, predicate, groupBy, orderBy, limit, subqueryPredicate, lock" />
public final class SQLStatementRule {
private final String contextName;
private final Class<? extends SQLStatement> sqlStatementClass;
private final Collection<SQLSegmentExtractor> extractors;
}
显然SQLStatementRule中包含了SQLSegmentExtractor的一个集合,而理解这种设计的实际上需要把握ShardingSphere中的SQL解析过程,这是接下来我们要搞清楚的内容。
至此,我们从解析规则的角度对ParseRuleRegistry进行了分析。这块内容涉及一大批JAXB工具类的定义,内容虽多,但并不复杂。核心类之间的关系如下图所示:
ParseRuleRegistry中存储的规则是ShardingSphere中进行SQL解析的基础数据,我们在后面的文章中继续介绍解析引擎时会反复引用今天所讲的内容。
转载:https://blog.csdn.net/lantian08251/article/details/104530453