java html解析,MyBatis3源碼解析(4)參數解析

 2023-10-12 阅读 30 评论 0

摘要:簡介 上篇文章中探索了查詢語句的執行過程,下面我們接著來看看其中的查詢參數的解析細節,是如何工作的 參數的解析 在日常的開發中,常見的參數有如下幾種: 1.直接傳入: func(Object param1, Object param2, …)2.放入Map中進行傳入:

簡介

上篇文章中探索了查詢語句的執行過程,下面我們接著來看看其中的查詢參數的解析細節,是如何工作的

參數的解析

在日常的開發中,常見的參數有如下幾種:

  • 1.直接傳入: func(Object param1, Object param2, …)
  • 2.放入Map中進行傳入:func(Map<String, Object> param)
  • 3.類傳入: func(Object param)

上面的請求是如何對應的呢,下面讓我們帶著疑問跟著源碼走一走

參數解析前的相關準備工作

在上篇中,在ParamNameResolver有獲取參數列表的代碼,大體上從names中遍歷獲取的,這里就涉及到names的初始化的相關代碼,如下:

public class ParamNameResolver {public static final String GENERIC_NAME_PREFIX = "param";private final boolean useActualParamName;private final SortedMap<Integer, String> names;private boolean hasParamAnnotation;// 初始化話的時候,將相關的name已初始化好public ParamNameResolver(Configuration config, Method method) {this.useActualParamName = config.isUseActualParamName();Class<?>[] paramTypes = method.getParameterTypes();Annotation[][] paramAnnotations = method.getParameterAnnotations();SortedMap<Integer, String> map = new TreeMap();int paramCount = paramAnnotations.length;for(int paramIndex = 0; paramIndex < paramCount; ++paramIndex) {if (!isSpecialParameter(paramTypes[paramIndex])) {String name = null;Annotation[] var9 = paramAnnotations[paramIndex];int var10 = var9.length;for(int var11 = 0; var11 < var10; ++var11) {Annotation annotation = var9[var11];if (annotation instanceof Param) {this.hasParamAnnotation = true;name = ((Param)annotation).value();break;}}if (name == null) {if (this.useActualParamName) {name = this.getActualParamName(method, paramIndex);}if (name == null) {name = String.valueOf(map.size());}}map.put(paramIndex, name);}}this.names = Collections.unmodifiableSortedMap(map);}
}

java html解析?在上面的代碼中,names在類構造函數中已經生成好了,后面獲取值的時候直接用即可

而在ParamNameResolver的構造函數中,通過初步跟蹤代碼,是直接讀取的接口函數參數獲取得到的參數,也就是在情況3中傳入類,是當做一個參數,后面這個類會一直傳遞下去

ParamNameResolver在MapperMethod中就已經初始化好了

public class MapperProxy<T> implements InvocationHandler, Serializable {private MapperProxy.MapperMethodInvoker cachedInvoker(Method method) throws Throwable {try {return (MapperProxy.MapperMethodInvoker)MapUtil.computeIfAbsent(this.methodCache, method, (m) -> {if (m.isDefault()) {......} else {return new MapperProxy.PlainMethodInvoker(new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration()));}});} catch (RuntimeException var4) {Throwable cause = var4.getCause();throw (Throwable)(cause == null ? var4 : cause);}}
}public class MapperMethod {public static class MethodSignature {......public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {......this.paramNameResolver = new ParamNameResolver(configuration, method);}}
}

跟蹤代碼下來,首先進行相關的初始化工作,而后在進行參數的解析獲取

參數值初步獲取

初始化完成后,在代碼語句執行前,會獲取參數值列表,下面是具體的處理邏輯:

public class ParamNameResolver {public Object getNamedParams(Object[] args) {// 獲取參數的數量int paramCount = this.names.size();if (args != null && paramCount != 0) {// 沒有參數聲明并且參數數量為1if (!this.hasParamAnnotation && paramCount == 1) {Object value = args[(Integer)this.names.firstKey()];return wrapToMapIfCollection(value, this.useActualParamName ? (String)this.names.get(0) : null);} else {Map<String, Object> param = new ParamMap();int i = 0;// 遍歷name獲得參數列表for(Iterator var5 = this.names.entrySet().iterator(); var5.hasNext(); ++i) {Entry<Integer, String> entry = (Entry)var5.next();param.put(entry.getValue(), args[(Integer)entry.getKey()]);String genericParamName = "param" + (i + 1);if (!this.names.containsValue(genericParamName)) {param.put(genericParamName, args[(Integer)entry.getKey()]);}}return param;}} else {return null;}}
}

maven命令詳解?在上面處理中,如果參數列表中唯一只有一個類參數,那這個參數也算是一個參數,會直接返回類,比如在示例中會直接返回:Person(id=1,name=1),后面獲取參數值填充時,會使用類get方法獲取值,這個在下面會接著分析

而且注意在param中,會存入兩個東西,一個argx,一個是paramx,感覺是和${}和#{}有關,這個后面再分析,param在示例中會如下:

#如果不加@Param注解
param:
- arg0: 1
- arg1: 1
- param0: 1
- param1: 1#如果加@Param注解
param:
- id: 1
- name: 1
- param0: 1
- param1: 1

在這里就得到后面要用的參數了,這里需要注意了,如果是單個參數,那就是直接返回對應的值;如果是多個參數,那就會放到一個map中,這個map中的key是非常關鍵的,因為構造preStatement是根據名稱從里面取值的,后面會有相關代碼

PreStatement的生成

咋上面得到參數后,并不是直接使用,而在在PreStatement生成的時候用于傳入的,關鍵的代碼如下:

public class SimpleExecutor extends BaseExecutor {private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {Connection connection = this.getConnection(statementLog);Statement stmt = handler.prepare(connection, this.transaction.getTimeout());// 這里會進行參數的傳入handler.parameterize(stmt);return stmt;}
}public class DefaultParameterHandler implements ParameterHandler {public void setParameters(PreparedStatement ps) {ErrorContext.instance().activity("setting parameters").object(this.mappedStatement.getParameterMap().getId());List<ParameterMapping> parameterMappings = this.boundSql.getParameterMappings();if (parameterMappings != null) {for(int i = 0; i < parameterMappings.size(); ++i) {ParameterMapping parameterMapping = (ParameterMapping)parameterMappings.get(i);if (parameterMapping.getMode() != ParameterMode.OUT) {String propertyName = parameterMapping.getProperty();Object value;if (this.boundSql.hasAdditionalParameter(propertyName)) {value = this.boundSql.getAdditionalParameter(propertyName);} else if (this.parameterObject == null) {value = null;} else if (this.typeHandlerRegistry.hasTypeHandler(this.parameterObject.getClass())) {value = this.parameterObject;} else {// 上面的三個先不管,下面就是獲取參數的具體的邏輯// 如果是類,會通過一些處理調用對應的get方法// 如果多個之間傳遞的參數,在上面會放入一個map,之間從map中獲取即可MetaObject metaObject = this.configuration.newMetaObject(this.parameterObject);value = metaObject.getValue(propertyName);}// 和參數攔截器相關的,后面再解析,先放過TypeHandler typeHandler = parameterMapping.getTypeHandler();JdbcType jdbcType = parameterMapping.getJdbcType();if (value == null && jdbcType == null) {jdbcType = this.configuration.getJdbcTypeForNull();}try {// 設置相關的值typeHandler.setParameter(ps, i + 1, value, jdbcType);} catch (SQLException | TypeException var10) {throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + var10, var10);}}}}}
}

從上面大致代碼可以看到,在正常情況下,參數的設置都是通過名稱取獲取的,之間傳入或者單個傳入的情況比較簡單

  • 多個參數的情況下,會將所有的參數放入map中,后面根據名稱去獲取
  • 單個參數類的情況下,會調用類的get方法對應獲取

jdk源碼剖析手冊,那如果混合類型,比如下面的情況:

public interface PersonMapper {@Select("Select id, name from Person where id=#{id} and name=#{person.name}")@Results(value = {@Result(property = "id", column = "id"),@Result(property="name", column = "name"),})Person getPersonByMul(@Param("person") Person person, @Param("id") Integer id);
}

我們一直根據下,從下面的代碼中得到,如果是類,會遞歸去獲取

public class MetaObject {public Object getValue(String name) {PropertyTokenizer prop = new PropertyTokenizer(name);if (prop.hasNext()) {MetaObject metaValue = this.metaObjectForProperty(prop.getIndexedName());// 如果是類,后面會再次調用getVale獲取return metaValue == SystemMetaObject.NULL_META_OBJECT ? null : metaValue.getValue(prop.getChildren());} else {return this.objectWrapper.get(prop);}}
}

總結

通過本篇的探索,我們大致了解了MyBatis3的參數獲取解析原理

  • 1.在初始化階段,獲取接口函數的參數列表,初始化names,用于后面獲取參數值
  • 2.獲取參數值,分單個或者多個參數的情況,而且根據參數的類型,有不同的處理方法
    • 單個參數:直接返回
    • 多個參數:放入Map中返回,后面根據key進行獲取
    • 參數類型是類:后面調用類的get防護獲取對應的值
    • 參數類型是Map:后面直接調用get方法
  • 3.PreStatement生成,生成的時候對應的值從上面得到的參數值獲取

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/3/135534.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息