Bootstrap

Mybatis3源码全解析

Mybatis架构:

图自:MyBatis整体架构_tiankong_12345的博客-CSDN博客_mybatis架构

Mybatis架构分三层:

第一层:SqlSession

这是使用mybatis的主要接口,通过这个接口我们可以执行命令,获取mapper或者管理事务。

Through this interface you can execute commands, get mappers and manage transactions.

第二层:核心处理层

配置解析模块:mybatis初始化时加载mybatis-config.xml文件、映射配置文件以及Mapper接口中的注解信息,解析方式是DOM配合XPATH,解析后的配置信息会形成相应的对象保存到Configuration中(后续的操作频繁使用到Configuration),通过Configuration可以创建SqlSessionFactory,有了SqlSessionFactory可以创建SqlSession,而SqlSession可以直接操作数据库。

参数映射:由ParameterHandler完成

SQL解析

SQL执行Executor主要负责维护一二级缓存,提供了操作数据库的接口,真正的操作数据库是由StatementHandler完成。

结果集映射:由ResultSetHandler完成。

插件:允许在映射参数或者执行SQL语句时拦截调用。参照mybatis – MyBatis 3 | 配置

第三层:基础支持层

基础支持层是为了支持核心处理层的功能,共有9个模块,如图

下面走马观花,来看Mybatis架构各模块的详细原理。

一。资源加载模块:

在org.apache.ibatis.io包下面,主要类有Resources和ClassLoaderWrapper

ClassLoaderWrapper提供了classForName()和getResourceAsStream()方法和getResourceAsURL()三种方式加载资源。

二。解析器模块:

加载后是解析。XML常见的解析方式有三种:DOM,SAX,StAX。

Mybatis默认的是DOM方式,DOM能把XML文件解析成树状数据结构,然后配合XPath能解析XML中的各节点数据,形成相应的对象保存到Configuration。主org.apache.ibatis.parsing包下。

三。反射模块:

Mybatis在进行参数处理和结果映射时,使用的是反射操作,反射模块在org.apache.ibatis.reflection包下面。主要类有Reflector和2个接口ObjectWrapper和Invoker等。

Reflector是反射的基础,缓存了属性名和 getter/setter方法。每个反射对象对应一个类。官方解释如下:

This class represents a cached set of class definition information that
allows for easy mapping between property names and getter/setter methods.
public class Reflector {

// 这里存储了反射操作需要使用的类的元信息 
  private final Class<?> type;
  private final String[] readablePropertyNames;
  private final String[] writablePropertyNames;
  private final Map<String, Invoker> setMethods = new HashMap<>();
  private final Map<String, Invoker> getMethods = new HashMap<>();
  private final Map<String, Class<?>> setTypes = new HashMap<>();
  private final Map<String, Class<?>> getTypes = new HashMap<>();
  private Constructor<?> defaultConstructor;

  //记录了所有属性名称集合
  private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();

  //这里是构造方法,会解析类,并填充上述成员变量
  public Reflector(Class<?> clazz) {
    type = clazz;
    //填充defaultConstructor变量
    addDefaultConstructor(clazz);

    Method[] classMethods = getClassMethods(clazz);
    //填充 getMethods变量
    addGetMethods(classMethods);
    //填充 setMethods 变量
    addSetMethods(classMethods);
    
    //填充setTypes和getTypes。如果是有效的字段,也会继续填充getMethods和setMethods
    addFields(clazz);
    //填充 readablePropertyNames 和writablePropertyNames 
    readablePropertyNames = getMethods.keySet().toArray(new String[0]);
    writablePropertyNames = setMethods.keySet().toArray(new String[0]);
    for (String propName : readablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
    for (String propName : writablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
  }
}
我们看到这里就是把Class类的元信息解析到Reflector 对象中,主要就是addFields和add***Methods。那么是怎么addFields和add***Methods的呢?Reflector 有这个方法
private void addGetMethod(String name, Method method, boolean isAmbiguous) {
    MethodInvoker invoker = isAmbiguous
        ? new AmbiguousMethodInvoker(method, MessageFormat.format(
            "Illegal overloaded getter method with ambiguous type for property ''{0}'' in class ''{1}''. This breaks the JavaBeans specification and can cause unpredictable results.",
            name, method.getDeclaringClass().getName()))
        : new MethodInvoker(method);
    //这里把方法包装成了MethodInvoker
    getMethods.put(name, invoker);
    Type returnType = TypeParameterResolver.resolveReturnType(method, type);
    getTypes.put(name, typeToClass(returnType));
 }

同理可以看到add***Methods添加的value是SetFieldInvoker和GetFieldInvoker。

 上面我们讲到解析XML后形成相应的Configuration对象,Configuration对象中有个org.apache.ibatis.session.Configuration#getReflectorFactory,通过指定Class和ReflectorFactory对象能创建MetaClass信息

MetaClass metaResultType = MetaClass.forClass(classType, configuration.getReflectorFactory());

MetaClass的成员变量有

  private final Object originalObject;
  private final ObjectWrapper objectWrapper;
  private final ObjectFactory objectFactory;
  private final ObjectWrapperFactory objectWrapperFactory;
  private final ReflectorFactory reflectorFactory;

通过MetaClass又能创建MetaObject对象

MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);

MetaObject的成员变量和MetaClass一样有:

  private final Object originalObject;
  private final ObjectWrapper objectWrapper;
  private final ObjectFactory objectFactory;
  private final ObjectWrapperFactory objectWrapperFactory;
  private final ReflectorFactory reflectorFactory;

对属性表达式的解析是通过MetaObject实现的。构造MetaObject时会创建ObjectWrapper接口的实现类。

ObjectWrapper接口是对对象的包装,抽象了对象的属性信息,定义了一系列查询和更新属性的方法。

通过下面以org.apache.ibatis.reflection.MetaObject#getValue为例:

先PropertyTokenizer解析表达式(如果表达式比较复杂hasNext,则递归调用MetaObject的getValue方法),再调用ObjectWrapper接口的get方法,最终获取了Invoker对象,利用Java反射取值。

private Object getBeanProperty(PropertyTokenizer prop, Object object) {
    try {
      Invoker method = metaClass.getGetInvoker(prop.getName());
      try {
        return method.invoke(object, NO_ARGUMENTS);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    } catch (RuntimeException e) {
      throw e;
    } catch (Throwable t) {
      throw new ReflectionException("Could not get property '" + prop.getName() + "' from " + object.getClass() + ".  Cause: " + t.toString(), t);
    }
  }

四:类型转换:

类型转换用在数据库的JdbcType与Java类型之间转换,接口类是TypeHandler,实现抽象类BaseTypeHandler可以自定义类型转换。

以Integer为例

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType)
      throws SQLException {
    ps.setInt(i, parameter);
  }

  @Override
  public Integer getNullableResult(ResultSet rs, String columnName)
      throws SQLException {
    int result = rs.getInt(columnName);
    return result == 0 && rs.wasNull() ? null : result;
  }

 至于查找用哪个Handler来处理是由TypeHandlerRegistry 来管理

public final class TypeHandlerRegistry {

  //存储JDBC类型转Java类型的handler
  private final Map<JdbcType, TypeHandler<?>> jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);
  //存储Java类型转JDBC类型的handler
  private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>();
  private final TypeHandler<Object> unknownTypeHandler;
  //存储所有的handler
  private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>();

  private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap();

  private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;
}

最终是使用org.apache.ibatis.type.TypeHandlerRegistry#getTypeHandler(java.lang.reflect.Type, org.apache.ibatis.type.JdbcType)方法来查找对应的handler。

五。日志模块

日志模块在org.apache.ibatis.logging包中

 LogFactory工厂类负责创建对应的日志组件适配器通过执行静态代码块,将对应的实现类构造器赋值给logConstructor变量,然后执行Runnble.run()方法。

这些日志组件按顺序加载并实例化对应日志组件适配器,logConstructor变量就是存储第三方日志组件适配器。

这里重点关注一下适配器模式的实现。以Jdk14LoggingImpl为例

Log接口定义了Mybatis日志模块的功能,有4个日志级别

public interface Log {

  boolean isDebugEnabled();
  boolean isTraceEnabled();
  void error(String s, Throwable e);
  // 如下是4种日志级别
  void error(String s);
  void debug(String s);
  void trace(String s);
  void warn(String s);

}

Jdk14LoggingImpl 实现了Log接口:也是4个日志级别

public class Jdk14LoggingImpl implements Log {

  private final Logger log;
  public Jdk14LoggingImpl(String clazz) {
    log = Logger.getLogger(clazz);
  }
  @Override
  public boolean isDebugEnabled() {
    return log.isLoggable(Level.FINE);
  }
  @Override
  public boolean isTraceEnabled() {
    return log.isLoggable(Level.FINER);
  }
  @Override
  public void error(String s, Throwable e) {
    log.log(Level.SEVERE, s, e);
  }
  //适配器Jdk14Logging的4种日志级别
  @Override
  public void error(String s) {
    log.log(Level.SEVERE, s);
  }
  @Override
  public void debug(String s) {
    log.log(Level.FINE, s);
  }
  @Override
  public void trace(String s) {
    log.log(Level.FINER, s);
  }
  @Override
  public void warn(String s) {
    log.log(Level.WARNING, s);
  }

}

而实际上java.util.logging.Level有7种日志级别

The levels in descending order are:
 * <ul>
 * <li>SEVERE (highest value)
 * <li>WARNING
 * <li>INFO
 * <li>CONFIG
 * <li>FINE
 * <li>FINER
 * <li>FINEST  (lowest value)

所以Log接口将所有支持的Log组件都适配成只支持4种日志级别的日志输出。

六。数据源模块:

数据源模块在org.apache.ibatis.datasource包下,主要类是DataSourceFactory及其子类以及实现了Java接口DataSource的子类。DataSourceFactory创建具体DataSource是使用了工厂方法模式

可以看到 PooledDataSourceFactory 继承了UnpooledDataSourceFactory 

 public class PooledDataSourceFactory extends UnpooledDataSourceFactory {

  public PooledDataSourceFactory() {
    this.dataSource = new PooledDataSource();
  }

 }

我们重点关注PooledDataSource的创建,有2个非常重要的方法

org.apache.ibatis.datasource.pooled.PooledDataSource#pushConnection

org.apache.ibatis.datasource.pooled.PooledDataSource#popConnection

这里面是线程池获取线程和释放线程的核心逻辑。每次存取都是执行同步块代码

synchronized (state){.........},这个state就是PoolState对象

这个PoolState类,存储了线程池中的闲置线程和激活线程,上面线程的存取最终都是操作了idleConnections或者activeConnections变量。

protected final List<PooledConnection> idleConnections = new ArrayList<>();

protected final List<PooledConnection> activeConnections = new ArrayList<>();

 七。事务管理模块:

事务模块代码在org.apache.ibatis.transaction包下,核心接口是Transaction和TransactionFactory。

TransactionFactory工厂类创建具体Transaction也是使用了工厂方法模式。TransactionFactory有2个创建事务的方法,分别是 从连接中创建和从数据源中创建。

  /**
   * Creates a {@link Transaction} out of an existing connection.
   * @param conn Existing database connection
   * @return Transaction
   * @since 3.1.0
   */
  Transaction newTransaction(Connection conn);

  /**
   * Creates a {@link Transaction} out of a datasource.
   * @param dataSource DataSource to take the connection from
   * @param level Desired isolation level
   * @param autoCommit Desired autocommit
   * @return Transaction
   * @since 3.1.0
   */
  Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, 
  boolean autoCommit);

实现方法都是new了一个对象,并把参数传入,并无多于逻辑。我们再来看看Transaction。ManagedTransaction与JdbcTransaction的区别是

ManagedTransaction事务并不会处理commit和rollback,Does nothing,交予容器完成

  @Override
  public void commit() throws SQLException {
    // Does nothing
  }

  @Override
  public void rollback() throws SQLException {
    // Does nothing
  }
通常我们会在Spring中使用Mybatis,可以使用SpringManagedTransaction来管理事务,该类在mybatis-spring包中。

八。binding模块:

binding模块是Mapper接口类与xxxMapper.xml文件的映射关系。Mybatis启动时会根据@MapperScan注解或者配置文件路径找到所有的Mapper接口类添加到org.apache.ibatis.binding.MapperRegistry#knownMappers变量中,key是class,value是new MapperProxyFactory<>(type)。我们再来看getMapper()方法。getMapper()方法获取了Mapper接口的代理对象,这是使用了代理模式

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

MapperProxyFactory#newInstance(org.apache.ibatis.session.SqlSession) 方法如下

public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
 } 
 protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  

当注入Mapper对象是,实际拿到的是MapperProxy对象,mapper接口调用方法实际就是调用了

org.apache.ibatis.binding.MapperProxy#invoke方法,method为Mapper类的全路径名+接口方法名。

@Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else {
        // 这里最终执行的是org.apache.ibatis.binding.MapperMethod#execute方法
        return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }

九.缓存模块

其中org.apache.ibatis.cache.Cache是关键接口,有11个实现类,除了PerpetualCache是Cache的基本实现外,其余10个都是Cache的装饰器,分别定义了不同场景的的缓存实现。通过组合模式使用了Cache对象。每个装饰器的构造类都需要传入Cache对象。

 至此,Mybatis的9个基础支持模块已经解读完毕。

;