上篇文章中探索了查詢語句的執行過程,下面我們接著來看看其中的查詢參數的解析細節,是如何工作的
在日常的開發中,常見的參數有如下幾種:
上面的請求是如何對應的呢,下面讓我們帶著疑問跟著源碼走一走
在上篇中,在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生成的時候用于傳入的,關鍵的代碼如下:
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);}}}}}
}
從上面大致代碼可以看到,在正常情況下,參數的設置都是通過名稱取獲取的,之間傳入或者單個傳入的情況比較簡單
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的參數獲取解析原理
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态