国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務(wù)器之家 - 編程語言 - Java教程 - Mybatis原理及源碼分析

Mybatis原理及源碼分析

2021-08-09 23:47三不猴子sanbuhouzi Java教程

作為Java程序員Mybatis應(yīng)該是一個(gè)必會(huì)框架了,其源碼體量只有Spring 的1/5,也是Hibernate的1/5 ,相比于其他流行框架Mybatis源碼無疑是學(xué)習(xí)成本最低的,當(dāng)做年輕人看的第一個(gè)框架源碼,無疑是非常好的。

Mybatis原理及源碼分析

作為Java程序員Mybatis應(yīng)該是一個(gè)必會(huì)框架了,其源碼體量只有Spring 的1/5,也是Hibernate的1/5 ,相比于其他流行框架Mybatis源碼無疑是學(xué)習(xí)成本最低的,當(dāng)做年輕人看的第一個(gè)框架源碼,無疑是非常好的。

整體架構(gòu)

對(duì)于一個(gè)陌生的事物切勿一頭扎進(jìn)細(xì)節(jié)里,我們先要觀其大概看看架構(gòu)脈絡(luò),MyBatis 分為三層架構(gòu),分別是基礎(chǔ)支撐層、核心處理層和接口層。

Mybatis原理及源碼分析

Mybatis 整體架構(gòu)

基礎(chǔ)支撐層

基礎(chǔ)支撐層是這個(gè)Mybatis框架的基建,為整個(gè)Mybatis框架提供非常基礎(chǔ)的功能。(篇幅有限下面我們只對(duì)部分模塊做簡(jiǎn)單的分析)

1.類型轉(zhuǎn)換模塊,我們?cè)贛ybatis中使用< typeAliase >標(biāo)簽定義一個(gè)別名就是使用類型轉(zhuǎn)換模塊實(shí)現(xiàn)的。類型轉(zhuǎn)換模塊最重要的功能還是實(shí)現(xiàn)了Mybatis中JDBC類型和Java類型之間的轉(zhuǎn)換。主要體現(xiàn)在:

  • 在SQL模板綁定用戶參入實(shí)參的場(chǎng)景中,將Java類型轉(zhuǎn)換成JDBC類型
  • 在結(jié)果集中,將JDBC類型轉(zhuǎn)換成Java類型。

Mybatis原理及源碼分析

Mybatis類型轉(zhuǎn)換

2.日志模塊,產(chǎn)生日志,定位異常。

3.反射工具,對(duì)原生的Java反射作了一些封裝。

4.Binding模塊,我們?cè)趫?zhí)行Mybatis中的方法時(shí)都是通過SqlSession來獲Mapper接口中的代理,這個(gè)代理將Mapper.xml文件中SQL進(jìn)行關(guān)聯(lián)就是通過Binding模塊來實(shí)現(xiàn)的。值得注意點(diǎn)是這個(gè)過程是發(fā)生在編譯期。可以將錯(cuò)誤提前到編譯期。

5.數(shù)據(jù)源模塊,數(shù)據(jù)源對(duì)于一個(gè)ORM來說算是非常核心的組件之一了。Mybatis默認(rèn)的數(shù)據(jù)源是非常出色的,Mybatis同時(shí)也支持集成第三方的數(shù)據(jù)源。

6.緩存模塊,緩存模塊的好壞直接影響這個(gè)ORM的性能。Mybatis提供了兩級(jí)緩存,同時(shí)也支持第三方緩存中間件集成。

Mybatis原理及源碼分析

Mybatis緩存

7.解析器模塊,主要是config.xml配置文件解析,和mapper.xml文件解析。

8.事務(wù)管理模塊,事務(wù)模塊對(duì)數(shù)據(jù)庫的事務(wù)機(jī)制進(jìn)行控制,對(duì)數(shù)據(jù)庫事務(wù)進(jìn)行了抽象,同時(shí)也對(duì)spring框架進(jìn)行了整合。詳細(xì)的處理邏輯下文會(huì)結(jié)合源碼詳細(xì)解析。

核心處理層

核心處理層是我們?cè)趯W(xué)習(xí)Mybatis原理的時(shí)候需要花80%時(shí)間的地方。核心處理層是 MyBatis 核心實(shí)現(xiàn)所在,其中涉及 MyBatis 的初始化以及執(zhí)行一條 SQL 語句的全流程。

配置解析

MyBatis 的初始化以及執(zhí)行一條 SQL 語句的全流程中也包含了配置解析,我們?cè)诂F(xiàn)實(shí)開發(fā)中一般都是使用spring boot starter的自動(dòng)配置。我們一項(xiàng)目啟動(dòng)為起點(diǎn)一層一層剝開Mybatis的流程。先打開org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration首先明確一點(diǎn)就是MybatisAutoConfiguration的目的就是要得到一個(gè)SqlSessionFactory。

  1. @Bean 
  2. @ConditionalOnMissingBean 
  3. public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { 
  4.   SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); 
  5.   factory.setDataSource(dataSource); 
  6.   factory.setVfs(SpringBootVFS.class); 
  7.   if (StringUtils.hasText(this.properties.getConfigLocation())) { 
  8.     factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation())); 
  9.   } 
  10.   Configuration configuration = this.properties.getConfiguration(); 
  11.   if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) { 
  12.     configuration = new Configuration(); 
  13.   } 
  14.   if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) { 
  15.     for (ConfigurationCustomizer customizer : this.configurationCustomizers) { 
  16.       customizer.customize(configuration); 
  17.     } 
  18.   } 
  19.   factory.setConfiguration(configuration); 
  20.   if (this.properties.getConfigurationProperties() != null) { 
  21.     factory.setConfigurationProperties(this.properties.getConfigurationProperties()); 
  22.   } 
  23.   if (!ObjectUtils.isEmpty(this.interceptors)) { 
  24.     factory.setPlugins(this.interceptors); 
  25.   } 
  26.   if (this.databaseIdProvider != null) { 
  27.     factory.setDatabaseIdProvider(this.databaseIdProvider); 
  28.   } 
  29.   if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) { 
  30.     factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage()); 
  31.   } 
  32.   if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) { 
  33.     factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage()); 
  34.   } 
  35.   if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) { 
  36.     factory.setMapperLocations(this.properties.resolveMapperLocations()); 
  37.   } 
  38.  
  39.   return factory.getObject(); 

這里是通過MybatisProperties里面的配置并放入到SqlSessionFactoryBean中,再由SqlSessionFactoryBean得到SqlSessionFactory。看到最后一行return factory.getObject();我們進(jìn)去看看這個(gè)factory.getObject()的邏輯是如何得到一個(gè)SqlSessionFactory。

  1. @Override 
  2. public SqlSessionFactory getObject() throws Exception { 
  3.   if (this.sqlSessionFactory == null) { 
  4.     afterPropertiesSet(); 
  5.   } 
  6.  
  7.   return this.sqlSessionFactory; 

這一步?jīng)]什么好說的,看看afterPropertiesSet()方法

  1. @Override 
  2. public void afterPropertiesSet() throws Exception { 
  3.   notNull(dataSource, "Property 'dataSource' is required"); 
  4.   notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required"); 
  5.   state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null), 
  6.             "Property 'configuration' and 'configLocation' can not specified with together"); 
  7.  
  8.   this.sqlSessionFactory = buildSqlSessionFactory(); 

重點(diǎn)來了,看看這個(gè)buildSqlSessionFactory()方法這里的核心目的就是將configurationProperties解析到Configuration對(duì)象中。代碼太長(zhǎng)了我就不貼出來了, buildSqlSessionFactory()的邏輯我畫了個(gè)圖,有興趣的小伙伴自行看一下。

Mybatis原理及源碼分析

Mybatis配置解析1

我們不要陷入細(xì)節(jié)之中,我們看看中點(diǎn)看看buildSqlSessionFactory() 方法的最后一行this.sqlSessionFactoryBuilder.build(configuration)點(diǎn)進(jìn)去

  1. public SqlSessionFactory build(Configuration config) { 
  2.     return new DefaultSqlSessionFactory(config); 
  3.   } 

通過buildSqlSessionFactory()解析得到的Configuration對(duì)象創(chuàng)建一個(gè)DefaultSqlSessionFactory(config),到此我們就得到了SqlSessionFactory同時(shí)被配置成一個(gè)bean了。

我們最終操作都是SqlSession,什么時(shí)候會(huì)通過SqlSessionFactory得到一個(gè)SqlSession呢?

要解決這個(gè)問題我們回到最開始的MybatisAutoConfiguration的sqlSessionTemplate(SqlSessionFactory sqlSessionFactory)這個(gè)方法,點(diǎn)開SqlSessionTemplate發(fā)現(xiàn)它是一個(gè)實(shí)現(xiàn)了SqlSession到這里我們猜測(cè)就是在這里SqlSessionFactory會(huì)構(gòu)建一個(gè)SqlSession出來。我們進(jìn)入new SqlSessionTemplate(sqlSessionFactory)看看源碼。

  1. public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { 
  2.    this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType()); 
  3.  } 

再往下看,我們就看到了

  1. public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, 
  2.     PersistenceExceptionTranslator exceptionTranslator) { 
  3.  
  4.   notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required"); 
  5.   notNull(executorType, "Property 'executorType' is required"); 
  6.  
  7.   this.sqlSessionFactory = sqlSessionFactory; 
  8.   this.executorType = executorType; 
  9.   this.exceptionTranslator = exceptionTranslator; 
  10.   this.sqlSessionProxy = (SqlSession) newProxyInstance( 
  11.       SqlSessionFactory.class.getClassLoader(), 
  12.       new Class[] { SqlSession.class }, 
  13.       new SqlSessionInterceptor()); 

這里通過動(dòng)態(tài)代理創(chuàng)建了一個(gè)SqlSession。

參數(shù)映射、SQL解析

我們先看一下MapperFactoryBean類,這個(gè)類實(shí)現(xiàn)了FactoryBean在bean初始化的時(shí)候會(huì)調(diào)用getObject()方法我們看看這個(gè)類下重寫的getObject()方法里的內(nèi)容。

  1. @Override 
  2.  public T getObject() throws Exception { 
  3.    return getSqlSession().getMapper(this.mapperInterface); 
  4.  } 

這里調(diào)用了sqlSession的getMapper()方法。一層一層點(diǎn)進(jìn)去里面返回的是一個(gè)代理對(duì)象。最后的執(zhí)行是由MapperProxy執(zhí)行。

  1. public <T> T getMapper(Class<T> type, SqlSession sqlSession) { 
  2.     final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); 
  3.     if (mapperProxyFactory == null) { 
  4.       throw new BindingException("Type " + type + " is not known to the MapperRegistry."); 
  5.     } 
  6.     try { 
  7.       return mapperProxyFactory.newInstance(sqlSession); 
  8.     } catch (Exception e) { 
  9.       throw new BindingException("Error getting mapper instance. Cause: " + e, e); 
  10.     } 
  11.   } 

接下來的流程我還是畫個(gè)流程圖,防止小伙伴們走丟。我這里的內(nèi)容可能未必完全和小標(biāo)題一樣,我主要按照sql執(zhí)行的流程講解的。

Mybatis原理及源碼分析

Mybatis參數(shù)綁定

先看一下MapperProxy中的invoke方法,cachedMapperMethod()方法將MapperMethod緩存起來了。

  1. @Override 
  2. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
  3.   try { 
  4.     if (Object.class.equals(method.getDeclaringClass())) { 
  5.       return method.invoke(this, args); 
  6.     } else if (isDefaultMethod(method)) { 
  7.       return invokeDefaultMethod(proxy, method, args); 
  8.     } 
  9.   } catch (Throwable t) { 
  10.     throw ExceptionUtil.unwrapThrowable(t); 
  11.   } 
  12.   final MapperMethod mapperMethod = cachedMapperMethod(method); 
  13.   return mapperMethod.execute(sqlSession, args); 
  14.  
  15.  private MapperMethod cachedMapperMethod(Method method) { 
  16.     MapperMethod mapperMethod = methodCache.get(method); 
  17.     if (mapperMethod == null) { 
  18.       mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()); 
  19.       methodCache.put(method, mapperMethod); 
  20.     } 
  21.     return mapperMethod; 
  22.   } 

我們?cè)谕驴磎apperMethod.execute(sqlSession, args)方法。

  1. public Object execute(SqlSession sqlSession, Object[] args) { 
  2.   Object result; 
  3.   switch (command.getType()) { 
  4.     case INSERT: { 
  5.     Object param = method.convertArgsToSqlCommandParam(args); 
  6.       result = rowCountResult(sqlSession.insert(command.getName(), param)); 
  7.       break; 
  8.     } 
  9.     case UPDATE: { 
  10.       Object param = method.convertArgsToSqlCommandParam(args); 
  11.       result = rowCountResult(sqlSession.update(command.getName(), param)); 
  12.       break; 
  13.     } 
  14.     case DELETE: { 
  15.       Object param = method.convertArgsToSqlCommandParam(args); 
  16.       result = rowCountResult(sqlSession.delete(command.getName(), param)); 
  17.       break; 
  18.     } 
  19.     case SELECT
  20.       if (method.returnsVoid() && method.hasResultHandler()) { 
  21.         executeWithResultHandler(sqlSession, args); 
  22.         result = null
  23.       } else if (method.returnsMany()) { 
  24.         result = executeForMany(sqlSession, args); 
  25.       } else if (method.returnsMap()) { 
  26.         result = executeForMap(sqlSession, args); 
  27.       } else if (method.returnsCursor()) { 
  28.         result = executeForCursor(sqlSession, args); 
  29.       } else { 
  30.         Object param = method.convertArgsToSqlCommandParam(args); 
  31.         result = sqlSession.selectOne(command.getName(), param); 
  32.       } 
  33.       break; 
  34.     case FLUSH: 
  35.       result = sqlSession.flushStatements(); 
  36.       break; 
  37.     default
  38.       throw new BindingException("Unknown execution method for: " + command.getName()); 
  39.   } 
  40.   if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) { 
  41.     throw new BindingException("Mapper method '" + command.getName()  
  42.         + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ")."); 
  43.   } 
  44.   return result; 

method.convertArgsToSqlCommandParam(args)這里就是處理參數(shù)轉(zhuǎn)換的邏輯。還有很多細(xì)節(jié)由于篇幅有限以及時(shí)間倉促我們不做過多的贅述,感興趣的小伙伴可以結(jié)合上面的圖自己看看。下面我們看SQL的執(zhí)行流程是怎么樣的。整體流程如下圖。

Mybatis原理及源碼分析

Mybatis執(zhí)行流程

我們就不對(duì)每一個(gè)執(zhí)行器都分析,我只挑一個(gè)SimpleExecutor來具體跟一下源碼。我們還是先看看圖吧,防止自己把自己搞蒙。

Mybatis原理及源碼分析

以simpleExecutor為例的執(zhí)行流程

  1. @Override 
  2. public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { 
  3.   Statement stmt = null
  4.   try { 
  5.     Configuration configuration = ms.getConfiguration(); 
  6.     StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); 
  7.     stmt = prepareStatement(handler, ms.getStatementLog()); 
  8.     return handler.<E>query(stmt, resultHandler); 
  9.   } finally { 
  10.     closeStatement(stmt); 
  11.   } 

這里獲取了Configuration,創(chuàng)建了一個(gè)StatementHandler,預(yù)處理操作,具體執(zhí)行的根據(jù)創(chuàng)建的預(yù)處理方法,最后執(zhí)行query方法

  1. @Override 
  2. public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { 
  3.   String sql = boundSql.getSql(); 
  4.   statement.execute(sql); 
  5.   return resultSetHandler.<E>handleResultSets(statement); 

到此我們整理了整個(gè)Mybatis的執(zhí)行流程,分析了其中的源碼,由于篇幅有限很多地方都沒有細(xì)致的分析,但是也貼出了圖,希望能幫助到你。

原文鏈接:https://mp.weixin.qq.com/s/FWGaPN9PV9gcagHS5VyPQA

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 日韩精品专区 | 福利片在线观看 | 伊人中文字幕 | 欧美日一区 | 日本欧美在线 | 国产999精品久久久久久麻豆 | 国产欧美高清在线观看 | 日本在线观看网址 | 欧美成人免费在线 | 久久h | 亚洲一区国产精品 | 中文字幕一区二区三区四区五区 | 国内自拍视频在线观看 | 深夜在线视频 | 天天干夜夜操 | 亚洲免费影院 | 国产一区在线视频观看 | 成人在线播放网站 | 欧美精品一区二区三区四区五区 | 久久成人高清 | 精品国产一区二区 | 一级特黄 | 日本黄色大片免费 | 国产精品视频在线播放 | 亚洲视频区 | a∨色狠狠一区二区三区 | 国产1页 | 久久久一 | 一区二区三区动漫 | 久久99视频精品 | 欧美日韩久久久久 | 国产精品99精品久久免费 | 亚洲精品久久久久久一区二区 | 欧美日韩精品在线播放 | 一区二区三区四区视频 | 亚洲综合射 | 日本一级淫片免费看 | 99成人在线 | 国产99精品视频 | 免费久久精品 | 免费黄色在线观看 |