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; }