Al invocar cualquier método definido en una interfaz Mapper, la ejecución real recae sobre un objeto proxy. Dado que este proxy se genera mediante el mecanismo de proxy dinámico de JDK, todas las invocaciones son interceptadas y redirigidas al método invoke de su InvocationHandler correspondiente.
Aálisis del Interceptor de Proxy (MapperProxy.invoke)
La clase MapperProxy actúa como el manejador de invocación. A continuación, se presenta una versión reestructurada de su lógica central para demostrar cómo se despachan las llamadas:
@Override
public Object invoke(Object proxyObj, Method targetMethod, Object[] params) throws Throwable {
// Derivar directamente las operaciones estándar de la clase Object
if (targetMethod.getDeclaringClass().equals(Object.class)) {
return targetMethod.invoke(this, params);
}
// Resolver los metadatos del método y ejecutar la operación de base de datos
MapperMethod resolvedOperation = retrieveMethodMetadata(targetMethod);
return resolvedOperation.executeOperation(sqlSession, params);
}
retrieveMethodMetadata(targetMethod): Gestiona el almacenamiento en caché de la instancia deMapperMethod, optimizando el rendimiento al evitar el análisis repetitivo de la misma firma de método.executeOperation(sqlSession, params): Delega la ejecución real de la sentencia SQL a la instancia deMapperMethod.
Resolución y Almacenamiento en Caché de MapperMethod
El framework necesita extraer y缓存 la información de mapeo de cada método de la interfaz. El proceso de obtención e inicialización se puede observar en el siguiente fragmento:
private MapperMethod retrieveMethodMetadata(Method targetMethod) {
MapperMethod cachedOperation = methodRegistry.get(targetMethod);
if (cachedOperation == null) {
cachedOperation = new MapperMethod(targetMethod, globalConfig);
methodRegistry.put(targetMethod, cachedOperation);
}
return cachedOperation;
}
public MapperMethod(Method targetMethod, Configuration globalConfig) {
this.globalConfig = globalConfig;
this.targetMethod = targetMethod;
// Extraer detalles de las anotaciones o configuraciones XML
this.sqlOperation = extractSqlCommand();
this.inputParamType = extractParameterType();
this.outputResultType = extractResultType();
this.methodExecutor = constructInvoker();
}
Enrutamiento y Ejecución de Comandos SQL
La clase MapperMethod encapsula la lógica de enrutamiento. En lugar de utilizar una estructura switch tradicional, el flujo de ejecución se puede refactorizar utilizando condicionales y extrayendo la complejidad de las operaciones SELECT a un método auxiliar:
public Object executeOperation(SqlSession dbSession, Object[] params) {
Object queryResult;
SqlCommandType operationType = sqlOperation.getType();
if (operationType == SqlCommandType.INSERT) {
queryResult = dbSession.insert(sqlOperation.getName(), params);
} else if (operationType == SqlCommandType.UPDATE) {
queryResult = dbSession.update(sqlOperation.getName(), params);
} else if (operationType == SqlCommandType.DELETE) {
queryResult = dbSession.delete(sqlOperation.getName(), params);
} else if (operationType == SqlCommandType.SELECT) {
queryResult = handleSelectOperation(dbSession, params);
} else {
throw new BindingException("Tipo de operación SQL no soportado: " + sqlOperation.getName());
}
return queryResult;
}
private Object handleSelectOperation(SqlSession dbSession, Object[] params) {
if (targetMethod.getReturnType() == void.class && hasResultHandler()) {
executeWithHandler(dbSession, params);
return null;
}
if (returnsCollection()) {
return executeForMultipleResults(dbSession, params);
}
if (returnsMapStructure()) {
return executeForMapResults(dbSession, params);
}
return executeForSingleResult(dbSession, params);
}
El método getMapper() de SqlSession devuelve exclusivamente este objeto proxy dinámico generado por JDK, cuya columna vertebral es MapperProxy. Toda la cadena de ejecución de una interfaz Mapper sigue un flujo estricto: la llamada al método es interceptada por el proxy, lo que desencadena la resolución del MapperMethod correspondiente, y este último traduce la invocación en una llamada nativa a los métodos de SqlSession, conectando finalmente la interfaz Java con el motor de ejecución SQL subyacente.