1、對源碼先上一個結構圖:java
源代碼主要在org.apache.ibatis目錄下,18個包,其中在應用中主要的包有:builder、session、cache、type、transaction、datasource、jdbc、mapping,提供支撐服務的包有annotation、binding、io、logging、plugin、reflection、scripting、exception、executor、parsingapache
2、從使用入手緩存
MyBatis使用的三板斧是SqlSessionFactoryBuilder和SqlSessionFactory、SqlSessionsession
SqlSessionFactoryBuilderapp
支持9種構造方法,其實最主要的是包含Configuration對象的構造方法,目的是爲了經過加載配置文件創造SqlSessionFactory對象,真實最終返回的是DefaultSqlSessionFactory對象框架
全部的構造方法最終都是調用build(Configuratiron)方法,這就要來研究一下Configuration對象,其實他就是對xml配置文件的對象映射,關於xml文件結構組成可從源碼中看出以下:ide
本文介紹一下Configuration的大框架,後續開個專輯專門研究Configuration的細節fetch
propertiesElement(root.evalNode("properties")); //加載資源文件屬性和當前文件屬性 typeAliasesElement(root.evalNode("typeAliases")); pluginElement(root.evalNode("plugins")); objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); settingsElement(root.evalNode("settings")); environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631 databaseIdProviderElement(root.evalNode("databaseIdProvider")); typeHandlerElement(root.evalNode("typeHandlers")); mapperElement(root.evalNode("mappers"));
環境元素(數據源和事務)、 屬性、類型別名、typeHandler、mapper、setting、插件ui
屬性:url
先來看屬性的加載,屬性的加載最重要的是瞭解三種屬性來源(屬性配置文件、當前文件的屬性、java代碼輸入)和三種屬性的加載順序(先加載配置文件,在加載config文件的屬性,最後加載java代碼輸入)
private void propertiesElement(XNode context) throws Exception { if (context != null) { Properties defaults = context.getChildrenAsProperties(); String resource = context.getStringAttribute("resource"); String url = context.getStringAttribute("url"); if (resource != null && url != null) { throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other."); } if (resource != null) { defaults.putAll(Resources.getResourceAsProperties(resource)); } else if (url != null) { defaults.putAll(Resources.getUrlAsProperties(url)); } Properties vars = configuration.getVariables(); if (vars != null) { defaults.putAll(vars); } parser.setVariables(defaults); configuration.setVariables(defaults); } }
類型別名
主要是類的完整路徑和簡單別名的對應關係加載,保存在容器typeAliasRegistry中,最終映射到configuratiron對象中
private void typeAliasesElement(XNode parent) { if (parent != null) { for (XNode child : parent.getChildren()) { if ("package".equals(child.getName())) { String typeAliasPackage = child.getStringAttribute("name"); configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage); } else { String alias = child.getStringAttribute("alias"); String type = child.getStringAttribute("type"); try { Class<?> clazz = Resources.classForName(type); if (alias == null) { typeAliasRegistry.registerAlias(clazz); } else { typeAliasRegistry.registerAlias(alias, clazz); } } catch (ClassNotFoundException e) { throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e); } } } } }
typeHandler
用戶自定義類型處理器,保存在typeHandlerRegistry容器中,最終保存在configuration對象中
private void typeHandlerElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { if ("package".equals(child.getName())) { String typeHandlerPackage = child.getStringAttribute("name"); typeHandlerRegistry.register(typeHandlerPackage); } else { String javaTypeName = child.getStringAttribute("javaType"); String jdbcTypeName = child.getStringAttribute("jdbcType"); String handlerTypeName = child.getStringAttribute("handler"); Class<?> javaTypeClass = resolveClass(javaTypeName); JdbcType jdbcType = resolveJdbcType(jdbcTypeName); Class<?> typeHandlerClass = resolveClass(handlerTypeName); if (javaTypeClass != null) { if (jdbcType == null) { typeHandlerRegistry.register(javaTypeClass, typeHandlerClass); } else { typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass); } } else { typeHandlerRegistry.register(typeHandlerClass); } } } } }
mapper
主要是加載mapper.xml文件,同構mapperParser對文件進行解析,保存在容器mapperRegistry中,最終保存到configuration對象中
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"); 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(); } 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(); } else if (resource == null && url == null && mapperClass != null) { Class<?> mapperInterface = Resources.classForName(mapperClass); configuration.addMapper(mapperInterface); } else { throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } } } } }
環境元素
主要加載數據源、和事務配置信息,由Environment.Builder對象進行處理,並將信息保存到configuratiron對象
private void environmentsElement(XNode context) throws Exception { if (context != null) { if (environment == null) { environment = context.getStringAttribute("default"); } for (XNode child : context.getChildren()) { String id = child.getStringAttribute("id"); if (isSpecifiedEnvironment(id)) { TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); DataSource dataSource = dsFactory.getDataSource(); Environment.Builder environmentBuilder = new Environment.Builder(id) .transactionFactory(txFactory) .dataSource(dataSource); configuration.setEnvironment(environmentBuilder.build()); } } } }
setting
加載ibatis自身工做須要配置的全部設置信息,爲configuration對象進行賦值
private void settingsElement(XNode context) throws Exception { if (context != null) { Properties props = context.getChildrenAsProperties(); // Check that all settings are known to the configuration class MetaClass metaConfig = MetaClass.forClass(Configuration.class); for (Object key : props.keySet()) { if (!metaConfig.hasSetter(String.valueOf(key))) { throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive)."); } } configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL"))); configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true)); configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory"))); configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false)); configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), true)); configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true)); configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true)); configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false)); configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE"))); configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null)); configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false)); configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false)); configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION"))); configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER"))); configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString")); configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true)); configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage"))); configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false)); configuration.setLogPrefix(props.getProperty("logPrefix")); configuration.setLogImpl(resolveClass(props.getProperty("logImpl"))); configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory"))); configuration.setInjectionFilterEnabled(booleanValueOf(props.getProperty("injectionFilterEnabled"), false)); configuration.setInjectionFilter(parseExpression(props.getProperty("injectionFilter"), "^[a-zA-Z0-9._]*$")); } }
二、SqlSessionFactory,真實幹活的DefaultSqlSessionFactory
SqlSessionFactory的做用是承上啓下,做爲SqlSession的工廠,主要是工做必須是提供獲取SqlSession的方法,同時還提供了一個獲取Configuration的方法
其中SqlSessionFactory提供了8種獲取SqlSession的方法,主要涉及4個參數:是否自動提交、事務級別、ExecutorType(Statement類型【普通、預處理、批處理】)、自定義Connection
其中須要注意的是openSession()方法默認不是自動commit的。
SqlSession openSession(); SqlSession openSession(boolean autoCommit); SqlSession openSession(Connection connection); SqlSession openSession(TransactionIsolationLevel level); SqlSession openSession(ExecutorType execType); SqlSession openSession(ExecutorType execType, boolean autoCommit); SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level); SqlSession openSession(ExecutorType execType, Connection connection); Configuration getConfiguration();
揭開如何提供SqlSession的祕密(在這裏發現了Configuration還能夠提供創造Executor的工做,Connection被封裝在Transaction 中,Configuration作了兩件事情,一個是配置信息存儲,還有內部對象的組裝工做,須要深刻研究這種模式是否能夠經過另外一個Action對象進行管理呢?)
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); final Executor executor = configuration.newExecutor(tx, execType); return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) { try { boolean autoCommit; try { autoCommit = connection.getAutoCommit(); } catch (SQLException e) { // Failover to true, as most poor drivers // or databases won't support transactions autoCommit = true; } final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); final Transaction tx = transactionFactory.newTransaction(connection); final Executor executor = configuration.newExecutor(tx, execType); return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
這個須要重點關注。建立事務,將Connection傳遞給tx的過程。
三、SqlSession,真實幹活的是DefaultSqlSession
SqlSession類主要封裝了Configuration對象、Executor對象、是否自動提交
SqlSession是在程序中真實幹活的人,咱們在使用Mybatis中打交道最頻繁的就是SqlSessinon對象,上個結構圖,看看他都在幹啥
看到圖了,就沒有什麼祕密了,他就是在幹數據的平常操做的活,只不過是用他本身封裝的一套東西,好比說Configuration(封裝Connection【來源可使Pool】)、Executor(封裝Statement)、ResultHandler(封裝處理ResultSet對象)、RowBounds(封裝分頁對象),提供了CRUD,提供了緩存機制,提供了根據配置文件獲取Sql語句的方法,提供了事務的提交和回滾等。