Understanding MyBatis's Core Execution Flow and Internal Architecture

Resource Loading via ClassLoader

The process begins with loading the configuration file, typically mybatis.xml, using Resources.getResourceAsStream(). This method leverages Java’s class loading mechanism to locate resources within the classpath. It does not resolve absolute file paths directly but instead relies on the ClassLoader hierarchy to find the resource.

Internally, Resources.getResourceAsStream() delegates to ClassLoader.getResourceAsStream(). The class loader chain includes:

  • The current thread’s context class loader
  • The class loader associated with the calling class
  • The system class loader (typically URLClassLoader)
  • A fallback to the parent class loader

The actual lookup occurs through findResource() in URLClassLoader, which uses a URLClassPath instance to search for the resource among JARs and directories in the classpath. If found, it returns an InputStream for reading the configuration.

Configuration Parsing with XMLConfigBuilder

After obtaining the input stream, SqlSessionFactoryBuilder.build(inputStream) is invoked. This triggers the creation of an XMLConfigBuilder instance, responsible for parsing the XML configuration into a Configuration object.

The XMLConfigBuilder constructor initializes a XPathParser to parse the document using XPath expressions. It then calls parse() to begin processing the root <configuration> element. Inside parseConfiguration(), various sections are processed sequentially:

  • Properties
  • Settings
  • Type aliases
  • Plugins
  • Object factories
  • Transaction managers and data sources
  • Mappers

Each section is parsed using evalNode() and corresponding handler methods, populating the Configuration object with metadata needed for runtime execution.

SqlSessionFactory Initialization

Once the Configuration is fully constructed, build(Configuration) returns a DefaultSqlSessionFactory. This factory is immutable and serves as the entry point for creating SqlSession instances.

SqlSession Creation and Executor Instantiation

When sqlSessionFactory.openSession() is called, the system:

  1. Retrieves the active environment from Configuration
  2. Creates a TransactionFactory based on the environment settings
  3. Initializes a Transaction object using the data source and isolation level
  4. Constructs an Executor based on the configured type (SIMPLE, REUSE, or BATCH)
  5. Wraps the executor with caching and plugin interceptors
  6. Returns a new DefaultSqlSession instance

The Executor is central to SQL execution. Depending on the type:

  • SimpleExecutor: Creates a new Statement per execution
  • ReuseExecutor: Reuses prepared statements
  • BatchExecutor: Enables batch operations

Caching is added if enabled via CachingExecutor, and all components are enhanced via the interceptor chain (pluginAll).

StatementHandler and SQL Preparation

For each database operation, a StatementHandler is created through RoutingStatementHandler, which routes to the appropriate implementation (PreparedStatementHandler, CallableStatementHandler, etc.).

The prepare() method in PreparedStatementHandler:

  • Precompiles the SQL using instantiateStatement()
  • Sets timeout and fetch size options
  • Returns the prepared statement

Then, parameterize() is called to bind parameters using ParameterHandler, which:

  • Extracts values from the parameter object
  • Uses TypeHandler to convert types safely
  • Applies JDBC type mappings

Result Handling and Interceptor Chain

After execution, results are handled by ResultSetHandler, which maps rows to objects based on result maps defined in XML.

Throughout this flow, the InterceptorChain applies user-defined plugins via pluginAll(), wrapping each component in proxy layers for interception before final use.

Mapper Interface Proxying

When sqlSession.getMapper(StudentDao.class) is called, MyBatis creates a dynamic proxy via MapperProxyFactory. The proxy implements InvocationHandler and maintains a cache (methodCache) mapping interface methods to MapperMethod instances.

On method invocation:

  • MapperProxy.invoke() checks if it's a default method or Object method
  • Otherwise, it retrieves the cached MapperMethod
  • Calls execute() on that method, which dispatches to the correct SqlSession operation (selectOne, insert, etc.)

The MapperMethod interprets method signatures, converts arguments, and selects the right execution path based on return type (e.g., List, Map, Optional, Cursor). For queries, it invokes sqlSession.selectList() or similar.

This entire flow enables a clean separation: developers write plain Java interfaces, and MyBatis dynamically binds them to SQL through reflection, proxies, and metadata-driven execution.

Tags: MyBatis SQL Execution Configuration Parsing Dynamic Proxy Executor Pattern

Posted on Sun, 17 May 2026 00:51:05 +0000 by XeroXer