MyBatis 实现原理
-
读取 mybatis 配置信息,并创建
SqlSessionFactory
对象。通过
SqlSessionFactoryBuilder
对象构建SqlSessionFactory
:public SqlSessionFactory build(InputStream inputStream) { return build(inputStream, null, null); } public SqlSessionFactory build(InputStream inputStream, String environment) { return build(inputStream, environment, null); } public SqlSessionFactory build(InputStream inputStream, Properties properties) { return build(inputStream, null, properties); } public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { // 拿到 XML 解析对象 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); // 将解析对象解析为 Configuration 对象,并构建 SqlSessionFactory 对象 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) { // 通过默认实现类构建 SqlSessionFactory return new DefaultSqlSessionFactory(config); }
-
通过
SqlSessionFactory
对象开启会话,获取SqlSession
对象,默认开启事务(因为自动提交缺省值为false
)。@Override public SqlSession openSession() { return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); } @Override public SqlSession openSession(boolean autoCommit) { return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit); } @Override public SqlSession openSession(ExecutorType execType) { return openSessionFromDataSource(execType, null, false); } @Override public SqlSession openSession(TransactionIsolationLevel level) { return openSessionFromDataSource(configuration.getDefaultExecutorType(), level, false); } @Override public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) { return openSessionFromDataSource(execType, level, false); } @Override public SqlSession openSession(ExecutorType execType, boolean autoCommit) { return openSessionFromDataSource(execType, null, autoCommit); } @Override public SqlSession openSession(Connection connection) { return openSessionFromConnection(configuration.getDefaultExecutorType(), connection); } @Override public SqlSession openSession(ExecutorType execType, Connection connection) { return openSessionFromConnection(execType, connection); }
上面这些重载方法中,默认都是开启事务的,除了允许设置
autoCommit
属性值的方法我们可以通过调用可以修改为自动提交。 -
通过
SqlSession
API 来与数据库交互(交互方式有两种)。-
通过
SqlSession
对象调用selectList, selectOne, insert, update, delete
等等的方法来与数据库交互,其中在调用方法中有一个很重要的参数就是statement
,指的是在mapper.xml
中你定义的标签的id
转换为Mapper
接口中的方法。这种方式通过statement
参数获取到指定的MappedStatement
对象,然后再使用MappedStatement
对象去取执行底层的select, update
(insert, update, delete
均为update
)。这种方式没有使用动态代理。 具体可以查看一下
XMLStatementBuilder
这个类中的parseStatementNode()
方法。 -
通过
SqlSession
对象调用getMapper
方法来获取我们在程序中定义的Mapper
接口,再使用Mapper
接口中的方法与数据库交互。这种方式使用了动态代理。在使用
SqlSession
对象调用getMapper
方法的时候,会调用默认实现类DefaultSqlSession
中的getMapper
方法,其内部使用的是Configuration
对象的getMapper
方法:// 该方法为 DefaultSqlSession 的方法 @Override public <T> T getMapper(Class<T> type) { return configuration.getMapper(type, this); }
跟进看一下
Configuration
对象的getMapper
方法:// 该方法为 Configuration 的方法 public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); }
跟进看一下
MapperRegistry
对象的getMapper
方法:// 用于映射接口与代理工厂的关系 private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>(); // 该方法为 MapperRegistry 的方法 @SuppressWarnings("unchecked") public <T> T getMapper(Class<T> type, SqlSession sqlSession) { // 在这里可以看到要获取一个 MapperProxyFactory 对象,也就是 Mapper 代理工厂对象 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); } }
看到这里应该也很好奇,什么时候改变的
knownMappers
的堆内存(准确来说这里的关注点是堆数据是何时改变的,即何时做的put
操作):// 先来看一下 MapperRegistry 对象的其他方法 public <T> boolean hasMapper(Class<T> type) { return knownMappers.containsKey(type); } 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 { knownMappers.put(type, new MapperProxyFactory<>(type)); // It's important that the type is added before the parser is run // otherwise the binding may automatically be attempted by the // mapper parser. If the type is already known, it won't try. MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } } /** * @since 3.2.2 */ public void addMappers(String packageName, Class<?> superType) { ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>(); resolverUtil.find(new ResolverUtil.IsA(superType), packageName); Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses(); for (Class<?> mapperClass : mapperSet) { addMapper(mapperClass); } } /** * @since 3.2.2 */ public void addMappers(String packageName) { addMappers(packageName, Object.class); } /** * @since 3.2.2 */ public Collection<Class<?>> getMappers() { return Collections.unmodifiableCollection(knownMappers.keySet()); }
在
XMLConfigBuilder
类中:// XMLConfigBuilder 中的方法 // 看到这里,回溯一下整体步骤1 中,构建 SqlSessionFactory 对象的时候调用了该方法 public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; parseConfiguration(parser.evalNode("/configuration")); return configuration; } // 在 parse 方法中调用了该方法,用于解析 Configuration 对象 private void parseConfiguration(XNode root) { try { //issue #117 read properties first propertiesElement(root.evalNode("properties")); Properties settings = settingsAsProperties(root.evalNode("settings")); loadCustomVfs(settings); loadCustomLogImpl(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")); // 注意这里,这里是配置文件中解析 mappers 的配置 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); } else { String resource = child.getStringAttribute("resource"); String url = child.getStringAttribute("url"); String mapperClass = child.getStringAttribute("class"); // 下面要根据不同的配置形式来调用之前所说的 addMapper 的方法 if (resource != null && url == null && mapperClass == null) { ErrorContext.instance().resource(resource); InputStream inputStream = Resources.getResourceAsStream(resource); // Configuration 中的 addMapper 内部调用的是 MapperRegistry 的 addMapper // 而 Configuration 中的 addMapper 方法在两个类中使用,分别是 XMLConfigBuilder 与 XMLMapperBuilder // XMLMapperBuilder 的具体调用就在 parse 方法中 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); // 在 parse 方法中调用 addMapper mapperParser.parse(); } 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()); // 在 parse 方法中调用 addMapper mapperParser.parse(); } else if (resource == null && url == null && mapperClass != null) { Class<?> mapperInterface = Resources.classForName(mapperClass); // 在 mapper 标签的 mapperClass 属性指定时,调用 addMapper configuration.addMapper(mapperInterface); } else { throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } } } } }
下面来具体看一下
XMLMapperBuilder
中的parse
方法:// XMLMapperBuilder 中的方法 public void parse() { if (!configuration.isResourceLoaded(resource)) { configurationElement(parser.evalNode("/mapper")); configuration.addLoadedResource(resource); // 在未加载 mapper 标签的 resource 属性时,需要绑定 XML 与 Mapper 接口的关系 bindMapperForNamespace(); } parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); } // 绑定 XML 与 Mapper 的方法实现 private void bindMapperForNamespace() { String namespace = builderAssistant.getCurrentNamespace(); if (namespace != null) { Class<?> boundType = null; try { boundType = Resources.classForName(namespace); } catch (ClassNotFoundException e) { //ignore, bound type is not required } if (boundType != null) { if (!configuration.hasMapper(boundType)) { // Spring may not know the real resource name so we set a flag // to prevent loading again this resource from the mapper interface // look at MapperAnnotationBuilder#loadXmlResource configuration.addLoadedResource("namespace:" + namespace); // 如果 Configuration 对象中不包含反射的 Mapper 接口,则调用 addMapper configuration.addMapper(boundType); } } } }
那么接下来的任务就是看一下
MapperProxyFactory
是如何创建MapperProxy
对象的就 OK 了:public class MapperProxyFactory<T> { private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>(); 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) { // 这里用到了 JDK 的动态代理 return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } // 通过 SqlSession 对象创建 MapperProxy 对象 public T newInstance(SqlSession sqlSession) { // methodCache 交由代理类维护 final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } }
下面来看看
MapperProxy
这个代理类:// 使用 JDK 动态代理 public class MapperProxy<T> implements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L; private final SqlSession sqlSession; private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache; public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; this.methodCache = methodCache; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { // 判断调用的是不是 Object 中定义的方法,toString,hashCode 这类。是的话直接放行。 if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } else if (method.isDefault()) { return invokeDefaultMethod(proxy, method, args); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args); } private MapperMethod cachedMapperMethod(Method method) { // 如果 method 不存在就构建一个新的 MapperMethod 对象返回 return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())); } private Object invokeDefaultMethod(Object proxy, Method method, Object[] args) throws Throwable { final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class .getDeclaredConstructor(Class.class, int.class); if (!constructor.isAccessible()) { constructor.setAccessible(true); } final Class<?> declaringClass = method.getDeclaringClass(); return constructor .newInstance(declaringClass, MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC) .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args); } }
下面看一下
MapperMethod
对象的execute
方法:private final SqlCommand command; private final MethodSignature method; public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) { this.command = new SqlCommand(config, mapperInterface, method); this.method = new MethodSignature(config, mapperInterface, method); } public Object execute(SqlSession sqlSession, Object[] args) { Object result; // 具体执行 sql 返回结果 switch (command.getType()) { case INSERT: { Object param = method.convertArgsToSqlCommandParam(args); // 这里可以看到最终调用的还是 SqlSession 的方法 result = rowCountResult(sqlSession.insert(command.getName(), param)); break; } case UPDATE: { Object param = method.convertArgsToSqlCommandParam(args); // 这里可以看到最终调用的还是 SqlSession 的方法 result = rowCountResult(sqlSession.update(command.getName(), param)); break; } case DELETE: { Object param = method.convertArgsToSqlCommandParam(args); // 这里可以看到最终调用的还是 SqlSession 的方法 result = rowCountResult(sqlSession.delete(command.getName(), param)); break; } case SELECT: if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { // 方法内部调用的也是 SqlSession 中的方法 result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { // 方法内部调用的也是 SqlSession 中的方法 result = executeForMap(sqlSession, args); } else if (method.returnsCursor()) { // 方法内部调用的也是 SqlSession 中的方法 result = executeForCursor(sqlSession, args); } else { Object param = method.convertArgsToSqlCommandParam(args); // 这里可以看到最终调用的还是 SqlSession 的方法 result = sqlSession.selectOne(command.getName(), param); if (method.returnsOptional() && (result == null || !method.getReturnType().equals(result.getClass()))) { result = Optional.ofNullable(result); } } break; case FLUSH: // 这里可以看到最终调用的还是 SqlSession 的方法 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; }
-