Mybatis作为一个经久不衰的ORM框架,其源码被众多人膜拜过,今天简单走一遍。
原生的经典使用示例:
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sessionFacotry = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sessionFacotry.openSession();
OrderMapper mapper = session.getMapper(OrderMapper.class);
mapper.selectById(1L);
使用层面的API不赘述,进入源码看看这几句代码都发生的什么。
SqlSessionFactoryBuilder#build
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
SqlSessionFactoryBuilder是经典的Builder模式,实现比较简单:通过XMLConfigBuilder解释配置文件得到Configuration对象,最后通过Configuration创建DefaultSqlSessionFactory。
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
//按顺序处理配置文件中的各个元素
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
这里主要看对mappers的解释:
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);//A:解释Mapper接口
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();//B:解释Mapper.xml
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();//B:解释Mapper.xml
} else if (resource == null && url == null && mapperClass != null) {
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);//A:解释Mapper接口
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
//mappers元素支持两种配置,一种按package,一种按mapper,mapper可以指向具体的xml或Mapper接口。如果通过Mapper接口走configuration.addMapper()解释,如果是xml走XMLMapperBuilder.parse()。这两种实现是等价,他们的实现内部会相互调用。
configuration.addMapper()会委托给MapperRegistry.addMapper():
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
//执行Mapper注册
knownMappers.put(type, new MapperProxyFactory<T>(type));
//执行Mapper解释
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
先是建立Mapper接口与MapperProxyFactory的映射,便于后继创建接口代理,然后通过Builder模式MapperAnnotationBuilder的解释,将每个接口方法解释成MappedStatement,并注册进Configuration中。MapperAnnotationBuilder#parse():
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
loadXmlResource();
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
parseCache();
parseCacheRef();
Method[] methods = type.getMethods();
for (Method method : methods) {
try {
// issue #237
if (!method.isBridge()) {
//将每个接口方法解释成MappedStatement,并注册进Configuration中。
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods();
}
parseStatement()主要解释Mapper接口方法上的各种注释,构造MappedStatement对象,并进行注册。 这里源码不贴出。
通过上面的一系列解释,生成Configuration对象,该类可以说是Mybatis最核心最重要的对象。
Configuration对象的结构包含了mybatis-config.xml配置文件的所有信息,也包含了各个Mapper.xml的所有信息。以及维持和加速构架运行的其它信息。Mybatis中的各种配置项都会对应到一个对象。内部核心数据对象:
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
protected final InterceptorChain interceptorChain = new InterceptorChain();
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
//维护每个MappedStatement
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");
protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection");
protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection");
protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection");
我们主要看Mapper相关信息的封装。
MapperProxy:与Mapper接口一一对应,Mapper接口的动态代理的回调处理器,内部维护着一个映射:Map<Method, MapperMethod>
MappedStatement:与XML中的一个CRUD元素(或者一个@Insert/@Update/@Select/@Delete/@SqlProvider对应),封装着一次数据库操作的SQL,参数,结果集映射等信息。
MapperMethod:与Mapper接口中的一个方法对应,提供execute方法,内部会调用SqlSession的crud方法。
SqlSource:封装着一次数据库操作的SQL,参数等。提供5种实现,无论哪种实现最终在执行阶段会被转换成StaticSqlSource,并最终生成BoundSql对象,该对象可被Executor直接执行。
再以Configuration的视角看怎么追溯到配置的内容:
一句话,Configuration可以直接或间接获取到配置相关和Mapper相关的所有信息。
解释完配置,分析SqlSession.getMapper()
代码比较简单,直接贴出流程图:
代码已经足够清晰,不用多做解释
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;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
//创建代理
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
//通过接口创建MapperProxy实例
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
根据上面的分析,Mybatis会为Mapper创建动态代理,并通过MapperProxy来处理回调,按照动态代理的套路,重点看MaperProxy.invoke()方法:
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);
}
//从缓存中查找MapperMethod,找不到则创建
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
MapperProxy主要有两个属性:
private final SqlCommand command;
private final MethodSignature method;
MapperProxy.execute()根据SqlCommand的不同类型,执行不同的操作
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
以select为例,分析如何拿到结果集:所有的查询最终会调用SqlSession.selectList()方法
SqlSession是面向开发者的最重要最熟悉的接口,该接口提供了封装了各种基础的SQL操作,同时可以通过该接口获取到各种Mapper接口。其默认实现是DefaultSqlSession,注:该实现不是线程安全,Spring提供了线程安全版本:SqlSessionTemplate。
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//通过statementId查找到MappedStatement
MappedStatement ms = configuration.getMappedStatement(statement);
//委托给executor执行查询
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
Executor主要有三个实现:BatchExecutor,ResueExecutor,和SimpleExecutor,通过默认是SimpleExecutor,这三者的公用逻辑被封装成BaseExecutor,而CachingExecutor当开起二级缓存时使用,
具体见Configuration.newExecutor():
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
//如果开启二级缓存
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
//插件增强,后面会细讲
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
BaseExecutor负责一级缓存的维护,BaseExecutor.query():
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
//SQL上设置了flushCache=true,则清空一级缓存
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
//如果设置了loaclCacheScope=STATEMENT,则清空一级缓存
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
//维护一级缓存
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
doQuery是抽象方法,这里选择SimpleExecutor继续走:
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
//调用Connection.prepareStatement,设置timeout,fetchSize
stmt = handler.prepare(connection, transaction.getTimeout());
//设置SQL的参数
handler.parameterize(stmt);
return stmt;
}
先看回Configuration.newStatementHandler(),这里返回的是经过插件增加了的StatementHandler。
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
//statementHandler经过插件增加
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
StatementHandler有四个实现,其中
回来看看StatementHandler.query():
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
StatementHandler将参数设置和结果集处理分离出来,分别交到了ParameterHandler和ResultSetHandler,这两者都只提供一个实现。代码不详
上面有些地方,有提到mybatis的插件机制。这里先上一张不严格的类图:
Mybatis的插件机制主要看Plugin和Interceptor,Plugin实现了InvocationHandler,同时聚合了Interceptor和4大组件(之一),会被包进动态代理。在invoke中调用Interceptor去实现最终的拦截操作。
Mybatis支持对参与SQL执行的4大组件进行增加,分别是:
1.Executor
2.StatementHandler
3.ResultSetHandler
4.ParameterHandler
上面代码中有涉及到两块,Executor和StatementHandler,在Mybatis的实现中,4大组件的创建都是由Configuration提供创建入口,其原因主要是方便对组件进行增强,因为Configuration维护着各种配置,这其中自然也包括了plugin,在Mybatis中,plugin的本质就是实现对4大组件的增加。插件常用来实现诸如自动分页,数据脱敏,数据加解密等工作。
在实现层面,Mybatis通过InterceptorChain将4大对象和Interceptor包装进Plugin,并创建了动态代理,该类其实现了InvocationHandler ,最终由该类完成动态的回调逻辑。
InterceptorChain#pluginAll()
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
pluginAll()很简单,就是以链的形式,将Interceptor 进行增强。通常情况下,interceptor.plugin()会调整Plugin.wrap(target, this),将目标对象和Interceptor自身包装进Plugin,并创建动态代理。看下Interceptor 的接口声明。
public interface Interceptor {
//执行拦截
Object intercept(Invocation invocation) throws Throwable;
//该方法通常用于对target进行增加
Object plugin(Object target);
//属性注入
void setProperties(Properties properties);
}
public class Plugin implements InvocationHandler {
private final Object target;
private final Interceptor interceptor;
private final Map<Class<?>, Set<Method>> signatureMap;
private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
this.target = target;
this.interceptor = interceptor;
this.signatureMap = signatureMap;
}
public static Object wrap(Object target, Interceptor interceptor) {
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
//创建代理
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
//如果目标方法有命中拦截器,则执行拦截
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}
//没命中则直接调用
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
}
整个插件动态代理的过程就是一个套娃的过程,前面的Interceptor会被套到最里层,会最晚被执行。
总结一下:
1.Mybatis的先执行配置的加载和解释,生成Configuration,该对象是构建SqlSessionFactory的基础。
2.SqlSession提供开发API的门面,其数据库操作委托给Executor,Executor不断细分合工,衍生出了StatementHandler,ResultSetHandler,ParameterHandler。
3.Mybatis提供了灵活的plugin机制,业务开发可以扩展Interceptor,拦截4大组件干点事情 。
4.Mybatis对各种组件都做了很好的封装,除了上面提到了SqlSession,Executor,StatementHandler等,还包括各种SqlSource/Buider/TypeHandler等,代码实现优雅,值得好好学习。
by simple
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/vipshop_fin_dev/article/details/121777104
内容来源于网络,如有侵权,请联系作者删除!