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个基础支持模块已经解读完毕。