在MyBatis初始化过程中,大致会有以下几个步骤:拉勾IT课小编为大家分解
创建Configuration全局配置对象,会往TypeAliasRegistry别名注册中心添加Mybatis需要用到的相关类,并设置默认的语言驱动类为XMLLanguageDriver
加载mybatis-config.xml配置文件、Mapper接口中的注解信息和XML映射文件,解析后的配置信息会形成相应的对象并保存到Configuration全局配置对象中
构建DefaultSqlSessionFactory对象,通过它可以创建DefaultSqlSession对象,MyBatis中SqlSession的默认实现类
因为整个初始化过程涉及到的代码比较多,所以拆分成了四个模块依次对MyBatis的初始化进行分析:
《MyBatis初始化(一)之加载mybatis-config.xml》
《MyBatis初始化(二)之加载Mapper接口与XML映射文件》
《MyBatis初始化(三)之SQL初始化(上)》
《MyBatis初始化(四)之SQL初始化(下)》
由于在MyBatis的初始化过程中去解析Mapper接口与XML映射文件涉及到的篇幅比较多,XML映射文件的解析过程也比较复杂,所以才分成了后面三个模块,逐步分析,这样便于理解
初始化(三)之SQL初始化(上)
在前面的MyBatis初始化相关文档中已经大致讲完了MyBatis初始化的整个流程,其中遗漏了一部分,就是在解析节点的过程中,是如何解析SQL语句,如何实现动态SQL语句,最终会生成一个org.apache.ibatis.mapping.SqlSource对象的,对于这烦琐且易出错的过程,我们来看看MyBatis如何实现的?
我们回顾org.apache.ibatis.builder.xml.XMLStatementBuilder的parseStatementNode()解析Statement节点时,通过下面的方法创建对应的SqlSource对象
//创建对应的SqlSource对象,保存了该节点下SQL相关信息
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
langDriver是从Configuration全局配置对象中获取的默认实现类,对应的也就是XMLLanguageDriver,在Configuration初始化的时候设置的
public Configuration() {
languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
languageRegistry.register(RawLanguageDriver.class);
}
主要包路径:org.apache.ibatis.scripting、org.apache.ibatis.builder、org.apache.ibatis.mapping
主要涉及到的类:
org.apache.ibatis.scripting.xmltags.XMLLanguageDriver:语言驱动接口的默认实现,创建ParameterHandler参数处理器对象和SqlSource资源对象
org.apache.ibatis.scripting.xmltags.XMLScriptBuilder:继承BaseBuilder抽象类,负责将SQL脚本(XML或者注解中定义的SQL语句)解析成SqlSource(DynamicSqlSource或者RawSqlSource)资源对象
org.apache.ibatis.scripting.xmltags.XMLScriptBuilder.NodeHandler:定义在XMLScriptBuilder内部的一个接口,用于处理MyBatis自定义标签(等),生成对应的SqlNode对象,不同的实现类处理不同的标签
org.apache.ibatis.scripting.xmltags.DynamicContext:解析动态SQL语句时的上下文,用于解析SQL时,记录动态SQL处理后的SQL语句,内部提供ContextMap对象保存上下文的参数
org.apache.ibatis.scripting.xmltags.SqlNode:SQL Node接口,每个XML Node会解析成对应的SQL Node对象,通过上下文可以对动态SQL进行逻辑处理,生成需要的结果
org.apache.ibatis.scripting.xmltags.OgnlCache:用于处理Ognl表达式
语言驱动接口的实现类如下图所示:
LanguageDriver
org.apache.ibatis.scripting.LanguageDriver:语言驱动接口,代码如下:
public interface LanguageDriver {
/**
* Creates a {@link ParameterHandler} that passes the actual parameters to the the JDBC statement.
*创建ParameterHandler对象
*
* @param mappedStatement The mapped statement that is being executed
* @param parameterObject The input parameter object (can be null)
* @param boundSqlThe resulting SQL once the dynamic language has been executed.
* @return参数处理器
* @author Frank D. Martinez [mnesarco]
* @see DefaultParameterHandler
*/
ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql);
/**
* Creates an {@link SqlSource} that will hold the statement read from a mapper xml file.
* It is called during startup, when the mapped statement is read from a class or an xml file.
*创建SqlSource对象,从Mapper XML配置的Statement标签中,即等。
*
* @param configuration The MyBatis configuration
* @param scriptXNode parsed from a XML file
* @param parameterType input parameter type got from a mapper method or specified in the parameterType xml attribute. Can be null.
* @return SQL资源
*/
SqlSource createSqlSource(Configuration configuration, XNode script, Class> parameterType);
/**
* Creates an {@link SqlSource} that will hold the statement read from an annotation.
* It is called during startup, when the mapped statement is read from a class or an xml file.
*创建SqlSource对象,从方法注解配置,即@Select等。
*
* @param configuration The MyBatis configuration
* @param scriptThe content of the annotation
* @param parameterType input parameter type got from a mapper method or specified in the parameterType xml attribute. Can be null.
* @return SQL资源
*/
SqlSource createSqlSource(Configuration configuration, String script, Class> parameterType);
}
定义了三个方法:
createParameterHandler:获取ParameterHandler参数处理器对象
createSqlSource:创建SqlSource对象,解析Mapper XML配置的Statement标签中,即
createSqlSource:创建SqlSource对象,从方法注解配置,即@Select等
XMLLanguageDriver
org.apache.ibatis.scripting.xmltags.XMLLanguageDriver:语言驱动接口的默认实现,代码如下:
public class XMLLanguageDriver implements LanguageDriver {
@Override
public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
//创建DefaultParameterHandler对象
return new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
}
/**
*用于解析XML映射文件中的SQL
*
* @param configuration The MyBatis configuration
* @param scriptXNode parsed from a XML file
* @param parameterType input parameter type got from a mapper method or
*specified in the parameterType xml attribute. Can be
*null.
* @return SQL资源
*/
@Override
public SqlSource createSqlSource(Configuration configuration, XNode script, Class> parameterType) {
//创建XMLScriptBuilder对象,执行解析
XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
return builder.parseScriptNode();
}
/**
*用于解析注解中的SQL
*
* @param configuration The MyBatis configuration
* @param scriptThe content of the annotation
* @param parameterType input parameter type got from a mapper method or
*specified in the parameterType xml attribute. Can be
*null.
* @return SQL资源
*/
@Override
public SqlSource createSqlSource(Configuration configuration, String script, Class> parameterType) {
// issue #3
//如果是开头,表示是在注解中使用的动态SQL
if (script.startsWith("
//创建XPathParser对象,解析出节点
XPathParser parser = new XPathParser(script, false, configuration.getVariables(), new XMLMapperEntityResolver());
return createSqlSource(configuration, parser.evalNode("/script"), parameterType);
} else {
// issue #127
//变量替换
script = PropertyParser.parse(script, configuration.getVariables());
//创建TextSqlNode对象
TextSqlNode textSqlNode = new TextSqlNode(script);
if (textSqlNode.isDynamic()) { //如果是动态SQL,则创建DynamicSqlSource对象
return new DynamicSqlSource(configuration, textSqlNode);
} else { //如果非动态SQL,则创建RawSqlSource对象
return new RawSqlSource(configuration, script, parameterType);
}
}
}
}
实现了LanguageDriver接口:
创建DefaultParameterHandler默认参数处理器并返回
解析XML映射文件中的SQL,通过创建XMLScriptBuilder对象,调用其parseScriptNode()方法解析
解析注解定义的SQL
如果是开头,表示是在注解中使用的动态SQL,将其转换成XNode然后调用上述方法,不了解的可以看看MyBatis三种动态SQL配置方式
先将注解中定义的SQL中包含的变量进行转换,然后创建对应的SqlSource对象
RawLanguageDriver
org.apache.ibatis.scripting.defaults.RawLanguageDriver:继承了XMLLanguageDriver,在的基础上增加了是否为静态SQL语句的校验,也就是判断创建的SqlSource是否为RawSqlSource静态SQL资源
XMLScriptBuilder
org.apache.ibatis.scripting.xmltags.XMLScriptBuilder:继承BaseBuilder抽象类,负责将SQL脚本(XML或者注解中定义的SQL)解析成SqlSource对象
构造方法
public class XMLScriptBuilder extends BaseBuilder {
/**
*当前SQL的XNode对象
*/
private final XNode context;
/**
*是否为动态SQL
*/
private boolean isDynamic;
/**
* SQL的Java入参类型
*/
private final Class> parameterType;
/**
* NodeNodeHandler的映射
*/
private final Map nodeHandlerMap = new HashMap();
public XMLScriptBuilder(Configuration configuration, XNode context) {
this(configuration, context, null);
}
public XMLScriptBuilder(Configuration configuration, XNode context, Class> parameterType) {
super(configuration);
this.context = context;
this.parameterType = parameterType;
initNodeHandlerMap();
}
private void initNodeHandlerMap() {
nodeHandlerMap.put("trim", new TrimHandler());
nodeHandlerMap.put("where", new WhereHandler());
nodeHandlerMap.put("set", new SetHandler());
nodeHandlerMap.put("foreach", new ForEachHandler());
nodeHandlerMap.put("if", new IfHandler());
nodeHandlerMap.put("choose", new ChooseHandler());
nodeHandlerMap.put("when", new IfHandler());
nodeHandlerMap.put("otherwise", new OtherwiseHandler());
nodeHandlerMap.put("bind", new BindHandler());
}
}
在构造函数中会初始化NodeHandler处理器,分别用于处理不同的MyBatis自定义的XML标签,例如等标签
parseScriptNode方法
parseScriptNode()方法将SQL脚本(XML或者注解中定义的SQL)解析成SqlSource对象,代码如下:
public SqlSource parseScriptNode() {
//解析XML或者注解中定义的SQL
MixedSqlNode rootSqlNode = parseDynamicTags(context);
SqlSource sqlSource;
if (isDynamic) {
//动态语句,使用了${}也算
sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
} else {
sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
}
return sqlSource;
}
通过调用parseDynamicTags(XNode node)方法,将解析SQL成MixedSqlNode对象,主要是将一整个SQL解析成一系列的SqlNode对象
如果是动态SQL语句,使用了MyBatis自定义的XML标签(等)或者使用了${},则封装成DynamicSqlSource对象
否则就是静态SQL语句,封装成RawSqlSource对象
parseDynamicTags方法
parseDynamicTags()将SQL脚本(XML或者注解中定义的SQL)解析成MixedSqlNode对象,代码如下:
protected MixedSqlNode parseDynamicTags(XNode node) {
//创建SqlNode数组
List contents = new ArrayList();
/*
*遍历SQL节点中所有子节点
*这里会对该节点内的所有内容进行处理然后返回NodeList对象
* 1.文本内容会被解析成'#text>'节点,就算一个换行符也会解析成这个
* 2.会被解析成'content#cdata-section>'节点
* 3.其他动态
*/
NodeList children = node.getNode().getChildNodes();
for (int i = 0; i
//当前子节点
XNode child = node.newXNode(children.item(i));
//如果类型是Node.CDATA_SECTION_NODE或者Node.TEXT_NODE时
if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE //节点
|| child.getNode().getNodeType() == Node.TEXT_NODE) { //纯文本
//获得内容
String data = child.getStringBody("");
//创建TextSqlNode对象
TextSqlNode textSqlNode = new TextSqlNode(data);
if (textSqlNode.isDynamic()) { //如果是动态的TextSqlNode对象,也就是使用了'${}'
//添加到contents中
contents.add(textSqlNode);
//标记为动态SQL
isDynamic = true;
} else { //如果是非动态的TextSqlNode对象,没有使用'${}'
//创建StaticTextSqlNode添加到contents中
contents.add(new StaticTextSqlNode(data));
}
} else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628如果类型是Node.ELEMENT_NODE
//根据子节点的标签,获得对应的NodeHandler对象
String nodeName = child.getNode().getNodeName();
NodeHandler handler = nodeHandlerMap.get(nodeName);
if (handler == null) { //获得不到,说明是未知的标签,抛出BuilderException异常
throw new BuilderException("Unknown element in SQL statement.");
}
//执行NodeHandler处理
handler.handleNode(child, contents);
//标记为动态SQL
isDynamic = true;
}
}
//创建MixedSqlNode对象
return new MixedSqlNode(contents);
}
创建SqlNode数组contents,用于保存解析SQL后的一些列SqlNode对象
获取定义的SQL节点中所有子节点,返回一个NodeList对象,这个对象中包含了该SQL节点内的所有信息,然后逐个遍历子节点
1.其中文本内容会被解析成`#text>`节点,就算一个换行符也会解析成这个
2. ``会被解析成`#cdata-section>`节点
3.还有其他MyBatis自定义的标签 `
如果子节点是或者类型
获取子节点的文本内容
创建TextSqlNode对象
调用TextSqlNode的isDynamic()方法,点击去该进去看看就知道了,如果文本中使用了${},则标记为动态SQL语句,将其添加至contents数组中
否则就是静态文本内容,创建对应的StaticTextSqlNode对象,将其添加至contents数组中
如果类型是Node.ELEMENT_NODE时,也就是MyBatis的自定义标签
根据子节点的标签名称,获得对应的NodeHandler对象
执行NodeHandler的handleNode方法处理该节点,创建不通类型的SqlNode并添加到contents数组中,如何处理的在下面讲述
标记为动态SQL语句
最后将创建contents封装成MixedSqlNode对象
NodeHandler
XMLScriptBuilder的内部接口,用于处理MyBatis自定义标签,接口实现类如下图所示:
代码如下:
private interface NodeHandler {
/**
*处理Node
*
* @param nodeToHandle要处理的XNode节点
* @param targetContents目标的SqlNode数组。实际上,被处理的XNode节点会创建成对应的SqlNode对象,添加到targetContents中
*/
void handleNode(XNode nodeToHandle, List targetContents);
}
这些NodeHandler实现类都定义在XMLScriptBuilder内部,用于处理不同标签,我们逐个来看
BindHandler
实现了NodeHandler接口,标签的处理器,代码如下:
/**
*
*/
private class BindHandler implements NodeHandler {
public BindHandler() {
// Prevent Synthetic Access
}
@Override
public void handleNode(XNode nodeToHandle, List targetContents) {
//解析name、value属性
final String name = nodeToHandle.getStringAttribute("name");
final String expression = nodeToHandle.getStringAttribute("value");
//创建VarDeclSqlNode对象
final VarDeclSqlNode node = new VarDeclSqlNode(name, expression);
targetContents.add(node);
}
}
获取标签的name和value属性
根据这些属性创建一个VarDeclSqlNode对象
添加到targetContents集合中
例如这样配置:
SELECT * FROM BLOG
WHERE title LIKE #{pattern}
TrimHandler
实现了NodeHandler接口,标签的处理器,代码如下:
private class TrimHandler implements NodeHandler {
public TrimHandler() {
// Prevent Synthetic Access
}
@Override
public void handleNode(XNode nodeToHandle, List targetContents) {
//解析内部的SQL节点,成MixedSqlNode对象
MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
//获得prefix、prefixOverrides、"suffix"、suffixOverrides属性
String prefix = nodeToHandle.getStringAttribute("prefix");
String prefixOverrides = nodeToHandle.getStringAttribute("prefixOverrides");
String suffix = nodeToHandle.getStringAttribute("suffix");
String suffixOverrides = nodeToHandle.getStringAttribute("suffixOverrides");
//创建TrimSqlNode对象
TrimSqlNode trim = new TrimSqlNode(configuration, mixedSqlNode, prefix, prefixOverrides, suffix, suffixOverrides);
targetContents.add(trim);
}
}
继续调用parseDynamicTags方法解析标签内部的子标签节点,嵌套解析,生成MixedSqlNode对象
获得prefix、prefixOverrides、suffix、suffixOverrides属性
根据上面获取到的属性创建TrimSqlNode对象
添加到targetContents集合中
WhereHandler
实现了NodeHandler接口,标签的处理器,代码如下:
private class WhereHandler implements NodeHandler {
public WhereHandler() {
// Prevent Synthetic Access
}
@Override
public void handleNode(XNode nodeToHandle, List targetContents) {
//解析内部的SQL节点,成MixedSqlNode对象
MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
//创建WhereSqlNode对象
WhereSqlNode where = new WhereSqlNode(configuration, mixedSqlNode);
targetContents.add(where);
}
}
继续调用parseDynamicTags方法解析标签内部的子标签节点,嵌套解析,生成MixedSqlNode对象
创建WhereSqlNode对象,该对象继承了TrimSqlNode,自定义前缀(WHERE)和需要删除的前缀(AND、OR等)
添加到targetContents集合中
SetHandler
实现了NodeHandler接口,标签的处理器,代码如下:
private class SetHandler implements NodeHandler {
public SetHandler() {
// Prevent Synthetic Access
}
@Override
public void handleNode(XNode nodeToHandle, List targetContents) {
//解析内部的SQL节点,成MixedSqlNode对象
MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
SetSqlNode set = new SetSqlNode(configuration, mixedSqlNode);
targetContents.add(set);
}
}
继续调用parseDynamicTags方法解析标签内部的子标签节点,嵌套解析,生成MixedSqlNode对象
创建SetSqlNode对象,该对象继承了TrimSqlNode,自定义前缀(SET)和需要删除的前缀和后缀(,)
添加到targetContents集合中
ForEachHandler
实现了NodeHandler接口,标签的处理器,代码如下:
private class ForEachHandler implements NodeHandler {
public ForEachHandler() {
// Prevent Synthetic Access
}
@Override
public void handleNode(XNode nodeToHandle, List targetContents) {
//解析内部的SQL节点,成MixedSqlNode对象
MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
//获得collection、item、index、open、close、separator属性
String collection = nodeToHandle.getStringAttribute("collection");
String item = nodeToHandle.getStringAttribute("item");
String index = nodeToHandle.getStringAttribute("index");
String open = nodeToHandle.getStringAttribute("open");
String close = nodeToHandle.getStringAttribute("close");
String separator = nodeToHandle.getStringAttribute("separator");
//创建ForEachSqlNode对象
ForEachSqlNode forEachSqlNode = new ForEachSqlNode(configuration, mixedSqlNode, collection, index, item, open, close, separator);
targetContents.add(forEachSqlNode);
}
}
继续调用parseDynamicTags方法解析标签内部的子标签节点,嵌套解析,生成MixedSqlNode对象
获得collection、item、index、open、close、separator属性
根据这些属性创建ForEachSqlNode对象
添加到targetContents集合中
IfHandler
实现了NodeHandler接口,标签的处理器,代码如下:
private class IfHandler implements NodeHandler {
public IfHandler() {
// Prevent Synthetic Access
}
@Override
public void handleNode(XNode nodeToHandle, List targetContents) {
//解析内部的SQL节点,成MixedSqlNode对象
MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
//获得test属性
String test = nodeToHandle.getStringAttribute("test");
//创建IfSqlNode对象
IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
targetContents.add(ifSqlNode);
}
}
继续调用parseDynamicTags方法解析标签内部的子标签节点,嵌套解析,生成MixedSqlNode对象
获得test属性
根据这个属性创建IfSqlNode对象
添加到targetContents集合中
OtherwiseHandler
实现了NodeHandler接口,标签的处理器,代码如下:
private class OtherwiseHandler implements NodeHandler {
public OtherwiseHandler() {
// Prevent Synthetic Access
}
@Override
public void handleNode(XNode nodeToHandle, List targetContents) {
//解析内部的SQL节点,成MixedSqlNode对象
MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
targetContents.add(mixedSqlNode);
}
}
继续调用parseDynamicTags方法解析标签内部的子标签节点,嵌套解析,生成MixedSqlNode对象
添加到targetContents集合中,需要结合ChooseHandler使用
ChooseHandler
实现了NodeHandler接口,标签的处理器,代码如下:
private class ChooseHandler implements NodeHandler {
public ChooseHandler() {
// Prevent Synthetic Access
}
@Override
public void handleNode(XNode nodeToHandle, List targetContents) {
List whenSqlNodes = new ArrayList();
List otherwiseSqlNodes = new ArrayList();
//解析 ` ` ` `
handleWhenOtherwiseNodes(nodeToHandle, whenSqlNodes, otherwiseSqlNodes);
//获得 ` `
SqlNode defaultSqlNode = getDefaultSqlNode(otherwiseSqlNodes);
//创建ChooseSqlNode对象
ChooseSqlNode chooseSqlNode = new ChooseSqlNode(whenSqlNodes, defaultSqlNode);
targetContents.add(chooseSqlNode);
}
private void handleWhenOtherwiseNodes(XNode chooseSqlNode, List ifSqlNodes,
List defaultSqlNodes) {
List children = chooseSqlNode.getChildren();
for (XNode child : children) {
String nodeName = child.getNode().getNodeName();
NodeHandler handler = nodeHandlerMap.get(nodeName);
if (handler instanceof IfHandler) { //处理 ` `
handler.handleNode(child, ifSqlNodes);
} else if (handler instanceof OtherwiseHandler) { //处理 ` `
handler.handleNode(child, defaultSqlNodes);
}
}
}
private SqlNode getDefaultSqlNode(List defaultSqlNodes) {
SqlNode defaultSqlNode = null;
if (defaultSqlNodes.size() == 1) {
defaultSqlNode = defaultSqlNodes.get(0);
} else if (defaultSqlNodes.size() > 1) {
throw new BuilderException("Too many default (otherwise) elements in choose statement.");
}
return defaultSqlNode;
}
}
先逐步处理标签的和子标签们,通过组合IfHandler和OtherwiseHandler两个处理器,实现对子节点们的解析
如果存在子标签,则抛出异常
根据这些属性创建ChooseSqlNode对象
添加到targetContents集合中
DynamicContext
org.apache.ibatis.scripting.xmltags.DynamicContext:解析动态SQL语句时的上下文,用于解析SQL时,记录动态SQL处理后的SQL语句,内部提供ContextMap对象保存上下文的参数
构造方法
public class DynamicContext {
/**
*入参保存在ContextMap中的Key
*
* {@link #bindings}
*/
public static final String PARAMETER_OBJECT_KEY = "_parameter";
/**
*数据库编号保存在ContextMap中的Key
*
* {@link #bindings}
*/
public static final String DATABASE_ID_KEY = "_databaseId";
static {
//设置OGNL的属性访问器
OgnlRuntime.setPropertyAccessor(ContextMap.class, new ContextAccessor());
}
/**
*上下文的参数集合,包含附加参数(通过 ` ` ` `
*/
private final ContextMap bindings;
/**
*生成后的SQL
*/
private final StringJoiner sqlBuilder = new StringJoiner(" ");
/**
*唯一编号。在{@link org.apache.ibatis.scripting.xmltags.XMLScriptBuilder.ForEachHandler}使用
*/
private int uniqueNumber = 0;
public DynamicContext(Configuration configuration, Object parameterObject) {
//初始化bindings参数
//构建入参的MetaObject对象
MetaObject metaObject = configuration.newMetaObject(parameterObject);
//入参类型是否有对应的类型处理器
boolean existsTypeHandler = configuration.getTypeHandlerRegistry().hasTypeHandler(parameterObject.getClass());
bindings = new ContextMap(metaObject, existsTypeHandler);
} else {
bindings = new ContextMap(null, false);
}
//添加bindings的默认值
bindings.put(PARAMETER_OBJECT_KEY, parameterObject);
bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId());
}
}
类型
属性
说明
ContextMap
bindings
上下文的参数集合,包含附加参数(通过标签生成的,或者标签解析参数保存的),以及几个默认值
StringJoiner
sqlBuilder
保存本次解析后的SQL,每次添加字符串以空格作为分隔符
int
uniqueNumber
唯一编号,在ForEachHandler处理节点时需要用到,生成唯一数组作为集合中每个元素的索引(作为后缀)
初始化bindings参数,创建ContextMap对象
根据入参转换成MetaObject对象
在静态代码块中,设置OGNL的属性访问器,OgnlRuntime是OGNL库中的类,设置ContextMap对应的访问器是ContextAccessor类
往bindings中添加几个默认值:_parameter>入参对象,_databaseId->数据库标识符
ContextMap
DynamicContext的内部静态类,继承HashMap,用于保存解析动态SQL语句时的上下文的参数集合,代码如下:
static class ContextMap extends HashMap {
private static final long serialVersionUID = 2977601501966151582L;
/**
* parameter对应的MetaObject对象
*/
private final MetaObject parameterMetaObject;
/**
*是否有对应的类型处理器
*/
private final boolean fallbackParameterObject;
public ContextMap(MetaObject parameterMetaObject, boolean fallbackParameterObject) {
this.parameterMetaObject = parameterMetaObject;
this.fallbackParameterObject = fallbackParameterObject;
}
@Override
public Object get(Object key) {
String strKey = (String) key;
if (super.containsKey(strKey)) {
return super.get(strKey);
}
if (parameterMetaObject == null) {
return null;
}
return parameterMetaObject.getOriginalObject();
} else {
// issue #61 do not modify the context when reading
return parameterMetaObject.getValue(strKey);
}
}
}
重写了HashMap的get(Object key)方法,增加支持对parameterMetaObject属性的访问
ContextAccessor
DynamicContext的内部静态类,实现ognl.PropertyAccessor接口,上下文访问器,代码如下:
static class ContextAccessor implements PropertyAccessor {
@Override
public Object getProperty(Map context, Object target, Object name) {
Map map = (Map) target;
//优先从ContextMap中,获得属性
Object result = map.get(name);
if (map.containsKey(name) || result != null) {
return result;
}
//
Object parameterObject = map.get(PARAMETER_OBJECT_KEY);
if (parameterObject instanceof Map) {
return ((Map) parameterObject).get(name);
}
return null;
}
@Override
public void setProperty(Map context, Object target, Object name, Object value) {
Map map = (Map ) target;
map.put(name, value);
}
@Override
public String getSourceAccessor(OgnlContext arg0, Object arg1, Object arg2) {
return null;
}
@Override
public String getSourceSetter(OgnlContext arg0, Object arg1, Object arg2) {
return null;
}
}
在DynamicContext的静态代码块中,设置OGNL的属性访问器,设置了ContextMap.class的属性访问器为ContextAccessor
这里方法的入参中的target,就是ContextMap对象
在重写的getProperty方法中,先从ContextMap里面获取属性值(可以回过去看下ContextMap的get方法)
没有获取到则获取PARAMETER_OBJECT_KEY属性的值,如果是Map类型,则从这里面获取属性值
4000520066 欢迎批评指正
All Rights Reserved 新浪公司 版权所有