[Mybatis source code analysis] summary of design patterns involved in Mybatis source code

Although we all know that there are 26 design patterns, most of them stay at the conceptual level and are rarely encountered in real development. A large number of design patterns are used in Mybatis source code. Reading the source code and observing the application of design patterns in it can have a deeper understanding of design patterns.
Mybatis has encountered at least the following design patterns:

Builder mode, such as SqlSessionFactoryBuilder, XMLConfigBuilder, XMLMapperBuilder, XMLStatementBuilder and CacheBuilder;
Factory mode, such as SqlSessionFactory, ObjectFactory, MapperProxyFactory;
Singleton mode, such as ErrorContext and LogFactory;
Proxy mode, the core of Mybatis implementation, such as MapperProxy and ConnectionLogger, uses jdk dynamic proxy; And the executor The loader package uses cglib or javassist to achieve the effect of delayed loading;
Combination mode, such as SqlNode and each subclass ChooseSqlNode;
Template method patterns, such as BaseExecutor and simpleexector, BaseTypeHandler and all subclasses, such as IntegerTypeHandler;
Adapter mode, such as the Mybatis interface of Log and its adaptation to various Log frameworks such as jdbc and log4j;
Decorator mode, such as Cache in Cache package The implementation of each decorator in the decorators sub package;
Iterator mode, such as iterator mode PropertyTokenizer;

Next, interpret the patterns one by one, first introduce the knowledge of the pattern itself, and then explain how to apply the pattern in Mybatis.

1. Builder mode

The definition of builder pattern is to "separate the construction of a complex object from its representation, so that the same construction process can create different representations.", It belongs to the creation class mode. Generally speaking, if the construction of an object is more complex and beyond the scope of the constructor, you can use the factory mode and builder mode. Compared with the factory mode, it will produce a complete product. Builder is applied to the construction of more complex objects, or even only a part of the product.

During the initialization of Mybatis environment, SqlSessionFactoryBuilder will call XMLConfigBuilder to read all mybatismap config XML and all mapper XML file, build the core object Configuration object of Mybatis, and then use the Configuration object as a parameter to build a SqlSessionFactory object.
When building the Configuration object, XMLConfigBuilder will also call XMLMapperBuilder to read Mapper files, and XMLMapperBuilder will use XMLStatementBuilder to read and build all SQL statements.

In this process, there is a similar feature, that is, these builders will read files or configurations, and then do a lot of steps such as XPath parser parsing, configuration or syntax parsing, reflecting generated objects, storing results in cache, etc. so much work can not be included in a constructor, so they use a lot of Builder mode to solve it.

For the specific classes of builder, most methods start with build *, such as SqlSessionFactoryBuilder, which includes the following methods:

That is, the factory object SqlSessionFactory is built according to different input parameters.

2. Factory mode

In Mybatis, for example, SqlSessionFactory uses the factory mode. The factory has less complex logic and is a simple factory mode.

Simple factory pattern: also known as static factory method pattern, it belongs to class creation pattern. In the simple factory mode, different instances can be returned according to different parameters. The simple factory pattern specifically defines a class to be responsible for creating instances of other classes. The created instances usually have a common parent class.

SqlSession can be regarded as the core interface of Mybatis. Through this interface, you can execute SQL statements, obtain Mappers and manage transactions. Similar to the Connection object connecting to MySQL.

It can be seen that the openSession method of the Factory is overloaded with many, and supports the input of parameters such as autoCommit, Executor and Transaction to build the core SqlSession object.

In the default factory implementation of DefaultSqlSessionFactory, there is a method to see how the factory produces a product:

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level,
                                             boolean autoCommit){
    Transaction tx = null;
    try {
        final Environment environment = configuration.getEnvironment();
        final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        final Executor executor = configuration.newExecutor(tx, execType);
        return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
        closeTransaction(tx); // may have fetched a connection so lets call
        // close()
        throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {

This is an underlying method called by openSession. This method first reads the corresponding environment configuration from configuration, then initializes TransactionFactory to obtain a Transaction object, then obtains an Executor object through Transaction, and finally constructs SqlSession through configuration, Executor and whether autoCommit.
You can also see the clue here that the execution of SqlSession is actually entrusted to the corresponding Executor.
For LogFactory, its implementation code:

public final class LogFactory {
    private static Constructor<? extends Log> logConstructor;

    private LogFactory() {
        // disable construction

    public static Log getLog(Class<?> aClass) {
        return getLog(aClass.getName());

One special thing here is that the type of Log variable is constructor <? Extensions Log >, that is, the factory produces not only a product, but a series of products with Log public interface, such as Log4jImpl, Slf4jImpl and many other specific logs.

3. Singleton mode

Singleton pattern: Singleton pattern ensures that there is only one instance of a class, and it instantiates itself and provides this instance to the whole system. This class is called singleton class, which provides global access methods.

There are three key points of singleton mode: first, a class can only have one instance; Second, it must create this instance by itself; Third, it must provide this example to the whole system by itself. Singleton mode is an object creation mode. Singleton mode is also known as singleton mode or singleton mode.

There are two singleton modes used in Mybatis, ErrorContext and LogFactory. ErrorContext is a singleton used in the scope of each thread to record the error information of the thread's execution environment, while LogFactory is a log factory provided to the whole Mybatis to obtain the log objects configured for the project.

Single instance implementation code of ErrorContext:

public class ErrorContext {

    private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<ErrorContext>();

    private ErrorContext() {

    public static ErrorContext instance() {
        ErrorContext context = LOCAL.get();
        if (context == null) {
            context = new ErrorContext();
        return context;

The constructor is a private modifier. It has a static local instance variable and a method to obtain the instance variable. In the method to obtain the instance, first judge whether it is empty. If so, create it first, and then return the constructed object.

But here's an interesting thing: the static instance variable of LOCAL is decorated with ThreadLocal, that is, it belongs to the data of each thread. In the instance() method, get the instance of this thread first. If not, create the ErrorContext unique to this thread.

4. Agent mode

Proxy mode can be regarded as the mode used by the core of Mybatis. Because of this mode, we only need to write mapper The java interface does not need to be implemented. The Mybatis background helps us complete the specific SQL execution.
Proxy pattern: provide a proxy for an object, and the proxy object controls the reference to the original object. Proxy mode is called proxy or Surrogate in English. It is an object structure mode.

The agent mode includes the following roles:

  • Subject: abstract theme role
  • Proxy: proxy subject role
  • RealSubject: real theme role

    There are two steps here. The first is to create a Proxy in advance. The second is to automatically request the Proxy when using it, and then the Proxy will execute specific transactions;

Mapperregistry is called when we use the getMapper method of Configuration getMapper method, which in turn calls mapperproxyfactory Newinstance (sqlsession) to generate a specific agent:

public class MapperProxyFactory<T> {

    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();

    public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;

    public Class<T> getMapperInterface() {
        return mapperInterface;

    public Map<Method, MapperMethod> getMethodCache() {
        return methodCache;

    protected T newInstance(MapperProxy<T> mapperProxy) {
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface },

    public T newInstance(SqlSession sqlSession) {
        final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
        return newInstance(mapperProxy);

Here, we first get a MapperProxy object through the T newInstance(SqlSession sqlSession) method, then call T newInstance(MapperProxy mapperProxy) to generate the proxy object and then return it.
Looking at the MapperProxy code, you can see the following:

public class MapperProxy<T> implements InvocationHandler, Serializable {

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            } else if (isDefaultMethod(method)) {
                return invokeDefaultMethod(proxy, method, args);
        } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        return mapperMethod.execute(sqlSession, args);

Typically, the MapperProxy class implements the InvocationHandler interface and the invoke method of the interface.
In this way, we only need to write Mapper Java interface class. When a Mapper interface is actually executed, it will be forwarded to mapperproxy Invoke method, which will call the subsequent sqlsession cud>executor. A series of methods such as prepare > and execute > are returned.

5. Combination mode

The combination mode combines multiple objects to form a tree structure to represent the structural hierarchy of "whole part".

The combination mode has consistency between single object (leaf object) and combination object (combination object). It organizes objects into tree structure and can be used to describe the relationship between whole and part. At the same time, it also blurs the concepts of simple elements (leaf objects) and complex elements (container objects), so that customers can deal with complex elements as simple elements, so that the client program can be decoupled from the internal structure of complex elements.

In the use of composite mode, one thing to note is also the key point of composite mode: leaf object and composite object implement the same interface. This is why the combination mode can handle leaf nodes and object nodes consistently.

Mybatis supports the powerful functions of dynamic SQL, such as the following SQL:

<update id="update" parameterType="org.format.dynamicproxy.mybatis.bean.User">   
	UPDATE users   
    <trim prefix="SET" prefixOverrides=",">
        <if test="name != null and name != ''">           name = #{name}       </if>
        <if test="age != null and age != ''">           , age = #{age}       </if>
        <if test="birthday != null and birthday != ''">           , birthday = #{birthday}       </if>
	where id = ${id}

Dynamic elements such as trim and if are used here, which can generate SQL under different conditions according to conditions;
In dynamicsqlsource In the getboundsql method, rootsqlnode is called apply (context) method, which is the interface implemented by all dynamic nodes:

public interface SqlNode { 
boolean apply(DynamicContext context);

All nodes that implement the SqlNode interface are the nodes of the whole composite mode tree:

The simplicity of the combination mode is that all child nodes are nodes of the same type and can be executed recursively downward. For example, for TextSqlNode, because it is the lowest leaf node, it directly append s the corresponding content to the SQL statement:

    public boolean apply(DynamicContext context) {
        GenericTokenParser parser = createParser(new BindingTokenParser(context, injectionFilter));
        return true;

But for IfSqlNode,You need to make a judgment first. If the judgment is passed, the child element will still be called SqlNode,Namely contents.apply Method to realize recursive parsing.@Override
public boolean apply(DynamicContext context) {
        if (evaluator.evaluateBoolean(test, context.getBindings())) {
        return true;
        return false;

6. Template method mode

Template method pattern is one of the most common patterns in all patterns. It is the basic technology of code reuse based on inheritance.
The template method pattern requires collaboration between designers who develop abstract classes and concrete subclasses. One designer is responsible for giving the outline and skeleton of an algorithm, while others are responsible for giving the logical steps of the algorithm. The method representing these specific logical steps is called the primitive method; The method that summarizes these basic methods is called template method, from which the name of this design pattern comes.

The template class defines the skeleton of the algorithm in an operation, and delays some steps to subclasses. The subclass can redefine some specific steps of an algorithm without changing the structure of the algorithm.

In Mybatis, the SQL execution of sqlSession is delegated to the Executor. The Executor includes the following structure:

The BaseExecutor adopts the template method mode, which implements most of the SQL execution logic, and then gives the following methods to subclass Customization:

protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;

protected abstract List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException;

protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds,
                                       ResultHandler resultHandler, BoundSql boundSql) throws SQLException;

The template method class has specific implementations of several subclasses and uses different strategies:

  • Simple executor: each time update or select is executed, a Statement object will be opened, and the Statement object will be closed immediately after use. (can be a Statement or PrepareStatement object)
  • Reuse reuseexecution: execute update or select, use sql as the key to find the Statement object, use it if it exists, and create it if it does not exist. When it is used up, the Statement object is not closed, but placed in map < string, Statement > for next use. (can be a Statement or PrepareStatement object)
  • Batch BatchExecutor: executes update (no select, JDBC batch does not support select), adds all SQL to batch processing (addBatch()), and waits for unified execution (executeBatch()). It caches multiple Statement objects, and each Statement object waits for executeBatch() batch processing one by one after addBatch() is completed; The batch executor is equivalent to maintaining multiple buckets. Each bucket contains a lot of its own SQL, just like Apple Blue contains a lot of apples and tomato blue contains a lot of tomatoes. Finally, it is poured into the warehouse. (can be a Statement or PrepareStatement object)
    For example, the update method is implemented in simpleexecution as follows:
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null,
        stmt = prepareStatement(handler, ms.getStatementLog());
        return handler.update(stmt);
    } finally {

7. Adapter mode

Adapter pattern: convert an interface into another interface that the customer wants. The adapter pattern enables those classes whose interfaces are incompatible to work together. Its alias is adapter. The adapter pattern can be either a class structured pattern or an object structured pattern.

In the logging package of Mybatsi, there is a Log interface:

public interface Log {

    boolean isDebugEnabled();

    boolean isTraceEnabled();

    void error(String s, Throwable e);

    void error(String s);

    void debug(String s);

    void trace(String s);

    void warn(String s);


This interface defines the logging method directly used by mybatis, and who will implement the Log interface? Mybatis provides the implementation of various logging frameworks, which match the interface method defined by the Log interface, and finally realize the adaptation of all external logging frameworks to mybatis Log package:

For example, for the implementation of Log4jImpl, the implementation holds org apache. log4j. An instance of logger, and then all logging methods are delegated to this instance.

public class Log4jImpl implements Log {

    private static final String FQCN = Log4jImpl.class.getName();

    private Logger log;

    public Log4jImpl(String clazz) {
        log = Logger.getLogger(clazz);

    public boolean isDebugEnabled() {
        return log.isDebugEnabled();

    public boolean isTraceEnabled() {
        return log.isTraceEnabled();

    public void error(String s, Throwable e) {
        log.log(FQCN, Level.ERROR, s, e);

    public void error(String s) {
        log.log(FQCN, Level.ERROR, s, null);

    public void debug(String s) {
        log.log(FQCN, Level.DEBUG, s, null);

    public void trace(String s) {
        log.log(FQCN, Level.TRACE, s, null);

    public void warn(String s) {
        log.log(FQCN, Level.WARN, s, null);


8. Decorator mode

Decorator Pattern: dynamically add some additional responsibilities to an object. In terms of adding object functions, decorator pattern is more flexible than generating subclass implementation. Its alias can also be called Wrapper (Wrapper, Decorator). According to different translations, decoration mode is also called "painter mode", which is an object structure mode.

  • 1. Do not change the original documents.
  • 2. Do not use inheritance.
  • 3. Dynamic expansion.

    In mybatis, the function of cache is defined by the root interface cache (org.apache.ibatis.cache.Cache). The whole system adopts decorator design mode. The basic functions of data storage and cache are realized by perpetual cache (org. Apache. Ibatis. Cache. Impl. Perpetual cache), and then a series of decorators are used to control the perpetual cache of perpetual cache, such as cache strategy. As shown below:

There are 8 standard decorators for decorating the perpetual cache (all in the org.apache.ibatis.cache.decorators package):

1.FifoCache: FIFO algorithm, cache recycling strategy
2.LoggingCache: output the log information of cache hits
3.LruCache: least recently used algorithm and cache recycling strategy
4.ScheduledCache: scheduling cache, which is responsible for clearing the cache regularly
5.SerializedCache: cache serialization and deserialization storage
6.SoftCache: cache management strategy based on soft reference
7. Synchronized cache: a synchronous cache decorator, which is used to prevent concurrent access of multiple threads
8.WeakCache: cache management strategy based on weak reference

In addition, there is a special decorator transactional cache: transactional cache
Like most persistence layer frameworks, mybatis cache is also divided into level 1 cache and level 2 cache

  • L1 cache, also known as local cache, is a permanent cache of the type of perpetual cache, which is stored in the executor (BaseExecutor), and the executor is in the SqlSession (DefaultSqlSession), so the lifecycle of L1 cache is the same as that of SqlSession.
  • L2 Cache, also known as user-defined Cache, can be used as L2 Cache for all classes that implement Cache interface, so third-party Cache such as encache can be configured. The L2 Cache takes the namespace namespace as its unique identifier and is saved in the Configuration core Configuration object.

The default type of L2 cache object is perpetual cache. If the configured cache is the default type, mybatis will automatically append a series of decorators according to the configuration.

The reference order between Cache objects is:

9. Iterator mode

Iterator mode, also known as Cursor mode. The definition given by GOF is to provide a method to access each element in a container object without exposing the internal details of the object.

The Iterator of Java is the interface of Iterator mode. As long as the interface is implemented, the Iterator mode is applied:

For example, Mybatis's PropertyTokenizer is a heavyweight class in the property package, which will be frequently referenced by other classes in the reflection package. This class implements the Iterator interface. The function hasNext in the Iterator interface is often used.

public class PropertyTokenizer implements Iterator<PropertyTokenizer> {
    private String name;
    private String indexedName;
    private String index;
    private String children;

    public PropertyTokenizer(String fullname) {
        int delim = fullname.indexOf('.');
        if (delim > -1) {
            name = fullname.substring(0, delim);
            children = fullname.substring(delim + 1);
        } else {
            name = fullname;
            children = null;
        indexedName = name;
        delim = name.indexOf('[');
        if (delim > -1) {
            index = name.substring(delim + 1, name.length() - 1);
            name = name.substring(0, delim);

    public String getName() {
        return name;

    public String getIndex() {
        return index;

    public String getIndexedName() {
        return indexedName;

    public String getChildren() {
        return children;

    public boolean hasNext() {
        return children != null;

    public PropertyTokenizer next() {
        return new PropertyTokenizer(children);

    public void remove() {
        throw new UnsupportedOperationException(
                "Remove is not supported, as it has no meaning in the context of properties.");

You can see that this class passes in a string to the constructor, and then provides the iterator method to traverse the parsed substring. It is a very common method class.

Keywords: Java Mybatis Design Pattern Back-end

Added by AliceG on Mon, 07 Feb 2022 11:39:13 +0200