Mybatis 程式碼流程及實現原理解析(三)
接上篇, 這篇繼續分析XMLMapperBuilder.parse()裡的configurationElement() 這個方法。
此方法逐步解析<mapper>的子節點。private void configurationElement(XNode context) { try { //mapper可以加namespace來避免重複情況 String namespace = context.getStringAttribute("namespace"); builderAssistant.setCurrentNamespace(namespace); //該節點表示從其他名目空間引用快取配置 cacheRefElement(context.evalNode("cache-ref")); cacheElement(context.evalNode("cache"));//解析cache子節點. //解析parameterMap 子節點,因為有多個並列的parameterMap節點,所以要加上路徑,解析返回的列表 parameterMapElement(context.evalNodes("/mapper/parameterMap")); //解析resultMap子節點,因為有多個並列的resultMap節點,所以要加上路徑,解析返回的列表 resultMapElements(context.evalNodes("/mapper/resultMap")); //解析sql子節點,因為有多個並列的sql節點,所以要加上路徑,解析返回的列表 sqlElement(context.evalNodes("/mapper/sql")); //解析select,insert,update,delete子節點 buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } catch (Exception e) { throw new RuntimeException("Error parsing Mapper XML. Cause: " + e, e); } }
子節點<cache-ref>
xml配置片段:
<cache-ref namespace="com.Book"/>
java程式碼解析:
CacheRefResolver.java的resolve方法private void cacheRefElement(XNode context) { if (context != null) { //首先用該節點的namespace替換cacherefmap中mapper檔案的。 configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace")); CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant, context.getStringAttribute("namespace")); try { //建立resolver,判斷該namespace是否可用。 cacheRefResolver.resolveCacheRef(); } catch (IncompleteElementException e) { //如果不可用,加入到為完成列表,在所有節點解析完成後,再統一做處理 configuration.addIncompleteCacheRef(cacheRefResolver); } } }
public Cache resolveCacheRef() {
return assistant.useCacheRef(cacheRefNamespace);
}
MapperBuilderAssistant.java的useCacheRef方法:
public Cache useCacheRef(String namespace) { if (namespace == null) { throw new BuilderException("cache-ref element requires a namespace attribute."); } try { unresolvedCacheRef = true; Cache cache = configuration.getCache(namespace); //如果當前configuration物件沒有持有該namespace的Cache, 則丟擲異常。 if (cache == null) { throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found."); } currentCache = cache; unresolvedCacheRef = false; return cache; } catch (IllegalArgumentException e) { throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.", e); } }
子節點<cache>
xml配置如下:
<mapper namespace="com.skoyou.domain.UserDAO">
<cache type="com.domain.something.MyCustomCache"
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="tr">
<property name="cacheFile" value="/tmp/my-custom-cache.tmp"/>
</cache>
type:配置自定義快取或為其他第三方快取方案 建立介面卡來完全覆蓋快取行為。如果未指定的話, 使用“PERPETFUL”型別(PerpetualCache.java)。eviction:快取回收策略,預設值是“LRU”, 有如下幾種策略可以指定:
LRU:最近最少使用的:移除最長時間不被使用的物件。
FIFO:先進先出:按物件進入快取的順序來移除它們。
SOFT:軟引用:移除基於垃圾回收器狀態和軟引用規則的物件。
WEAK:弱引用:更積極地移除基於垃圾收集器狀態和弱引用規則的物件.
flushInterval:重新整理間隔)可以被設定為任意的正整數,而且它們代表一個合理的毫秒 形式的時間段。預設情況是不設定,也就是沒有重新整理間隔,快取僅僅呼叫語句時重新整理。
size:(引用數目)可以被設定為任意正整數,要記住你快取的物件數目和你執行環境的 可用記憶體資源數目。預設值是 1024。
readyOnly:(只讀)屬性可以被設定為 true 或 false。只讀的快取會給所有呼叫者返回緩 存物件的相同例項。因此這些物件不能被修改。這提供了很重要的效能優勢。可讀寫的快取 會返回快取物件的拷貝(通過序列化) 。這會慢一些,但是安全,因此預設是 false。
cache可以有個子節點<property> 來載入.
java方法解析:
private void cacheElement(XNode context) throws Exception {
if (context != null) {
//獲取cache type的值,沒有的話,使用預設PERPETUAL
String type = context.getStringAttribute("type", "PERPETUAL");
// 得到type類, 預設是PERPETUAL 對應的PerpetualCache
Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
//獲取eviction的值,沒有的話,使用預設LRU得到eviction類, 即LruCache.class
String eviction = context.getStringAttribute("eviction", "LRU");
Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
Long flushInterval = context.getLongAttribute("flushInterval");
Integer size = context.getIntAttribute("size");
boolean readWrite = !context.getBooleanAttribute("readOnly", false);
Properties props = context.getChildrenAsProperties();
//根據提供的引數, 建一個cache物件, 並將這個物件放入到Configuration 物件中.
builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, props);
}
}
子節點<parameterMap>
在Mybatis中,這個節點已經被廢棄了!老式風格的引數對映。內聯引數是首選,這個元素可能在將來被移除。其實解析還是比較簡單的。取這個節點的所有屬性,build出一個ParameterMapping物件,方法parametermappings列表裡, 然後將這個列表更新到configuration物件中的ParameterMap中去。
for (XNode parameterMapNode : list) {
String id = parameterMapNode.getStringAttribute("id");
String type = parameterMapNode.getStringAttribute("type");
Class<?> parameterClass = resolveClass(type);
List<XNode> parameterNodes = parameterMapNode.evalNodes("parameter");
List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
for (XNode parameterNode : parameterNodes) {
String property = parameterNode.getStringAttribute("property");
String javaType = parameterNode.getStringAttribute("javaType");
String jdbcType = parameterNode.getStringAttribute("jdbcType");
String resultMap = parameterNode.getStringAttribute("resultMap");
String mode = parameterNode.getStringAttribute("mode");
String typeHandler = parameterNode.getStringAttribute("typeHandler");
Integer numericScale = parameterNode.getIntAttribute("numericScale");
ParameterMode modeEnum = resolveParameterMode(mode);
Class<?> javaTypeClass = resolveClass(javaType);
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
@SuppressWarnings("unchecked")
Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
ParameterMapping parameterMapping = builderAssistant.buildParameterMapping(parameterClass, property, javaTypeClass, jdbcTypeEnum, resultMap, modeEnum, typeHandlerClass, numericScale);
parameterMappings.add(parameterMapping);
}
builderAssistant.addParameterMap(id, parameterClass, parameterMappings);
}
}
子節點<resultMap>
這個節點的解析較複雜,會在下一遍中單獨講解。
子節點<sql>
<sql id="userColumns"> id,username,password </sql>
這個元素可以被用來定義可重用的 SQL 程式碼段,可以包含在其他語句中。Java 程式碼
private void sqlElement(List<XNode> list) throws Exception {
if (configuration.getDatabaseId() != null) { //傳人configuration現有的database id。
sqlElement(list, configuration.getDatabaseId());
}
sqlElement(list, null);
}
sqlElement方法:
private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception {
for (XNode context : list) { //由於有多個並列的sql節點,需求逐個解析。
//sql 也可以有自己的database id,但實際上這個id要與全域性配置的一致
String databaseId = context.getStringAttribute("databaseId");
String id = context.getStringAttribute("id");
//判斷id是否包含有當前的namespace,包含的話直接返回當前id。 沒有的話,則以當前mapper的namespace.id值返回
id = builderAssistant.applyCurrentNamespace(id, false);
if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId))
sqlFragments.put(id, context);
}
}
databaseIdMatchesCurrent 方法:
sql節點自己指定的database id要與configuration中的一致。
private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {
if (requiredDatabaseId != null) {
if (!requiredDatabaseId.equals(databaseId)) {
return false;
}
} else {
if (databaseId != null) {
return false;
}
// skip this fragment if there is a previous one with a not null databaseId
if (this.sqlFragments.containsKey(id)) {
XNode context = this.sqlFragments.get(id);
if (context.getStringAttribute("databaseId") != null) {
return false;
}
}
}
return true;
}
實際上這個sqlFragments map是在XMLMappBuilder初始化的時候,有configuration傳入的, 所以configuration能得到最新的對這個map的修改。這個map會在build select等節點的時候被用到。
子節點<select>,<insert>,<update>,<delete>
節點配置,以select為例:
<select
id="selectPerson"
databaseId="test"
fetchSize="256"
timeout="10000"
parameterMap="deprecated"
parameterType="int"
resultMap="personResultMap"
resultType="hashmap"
lang="XML"
resultSetType="FORWARD_ONLY"
statementType="PREPARED"
flushCache="false"
useCache="true"
resultOrdered="false"
>
節點屬性描述:
屬性 | 描述 |
---|---|
id | 在名稱空間中唯一的識別符號,可以被用來引用這條語句。 |
databaseId | 如果配置了databaseProvider,databaseId要與配置的一致。如果不匹配的話,後續程式碼不會繼續解析 |
fetchSize | 這是暗示驅動程式每次批量返回的結果行數。預設不設定(驅動 自行處理)。 |
timeout | 這個設定驅動程式等待資料庫返回請求結果,並丟擲異常時間的 最大等待值。預設不設定(驅動自行處理) |
parameterMap | 這是引用外部 parameterMap 的已經被廢棄的方法。使用內聯引數 對映和 parameterType 屬性。 |
parameterType | 將會傳入這條語句的引數類的完全限定名或別名。 |
resultMap | 命名引用外部的 resultMap。 返回 map 是 MyBatis 最具力量的特性, 對其有一個很好的理解的話, 許多複雜對映的情形就 能被解決了。 使用 resultMap 或 resultType,但不能同時使用。 |
resultType | 從這條語句中返回的期望型別的類的完全限定名或別名。注意集 合情形,那應該是集合可以包含的型別,而不能是集合本身。使 用 resultType 或 resultMap,但不能同時使用 |
lang | 提供”XML“或”RAW“,不同的lang,可能會影響最終的sql語句的生成,預設是”XML“ |
resultSetType | FORWARD_ONLY|SCROLL_SENSITIVE|SCROLL_INSENSITIVE 中的一種。預設是不設定(驅動自行處理)。 |
statementType | STA TEMENT,PREPARED 或 CALLABLE 的一種。 這會讓 MyBatis 使用選擇使用 Statement,PreparedStatement 或 CallableStatement。 預設值:PREPARED |
fhushCache | 將其設定為 true,不論語句什麼時候被呼叫,都會導致快取被 清空。預設值:false |
userCache | 將其設定為 true, 將會導致本條語句的結果被快取。 預設值: true |
resultOrdered | 這個屬性值適用於有巢狀結果的select 語句。如果是true, 那麼就認為包含了巢狀的結果, 或者巢狀的結果被分在一組。這樣的話, 當一個新的主結果返回時, 就不會出現上一個結果行。 這能夠更加記憶體友好地填充巢狀結果。預設是false。 |
另外還有3個是隻對insert有效的屬性:
useGeneratedKeys | 這會告訴 MyBatis使用JDBC的getGeneratedKeys 方法來取出由資料(比如:像 MySQL 和 SQL Server 這樣的資料庫管理系統的自動遞增欄位)內部生成的主鍵。 預設值:false |
keyProperty | 標記一個屬性, MyBatis 會通過 getGeneratedKeys 或者通過 insert 語句的 selectKey 子元素設定它的值。 預設: 不設定 |
keyColumn | 標記一個屬性, MyBatis 會通過 getGeneratedKeys 或者通過 insert 語句的 selectKey 子元素設定它的值。 預設: 不設定 |
sql語句的解析實際上是在 XMLStatementBuider的parseStatementNode方法中解析的。
private void buildStatementFromContext(List<XNode> list) {
if (configuration.getDatabaseId() != null) {
buildStatementFromContext(list, configuration.getDatabaseId());
}
buildStatementFromContext(list, null);
}
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
for (XNode context : list) {
//構造statementBuilder。這幾個sql語句相關的節點在這個類裡解析
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
try {
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
configuration.addIncompleteStatement(statementParser);
}
}
}
XMLStatementBuilder.java的 parseStatementNode方法,如下。這個方法比較長,但主要是讀取屬性值,然後包裝生成一個MappedStatement,並放入configuration物件中。
public void parseStatementNode() {
//讀取id 屬性
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
//讀取databaseId 屬性,但是一定要與現有存在的匹配, 否則不繼續解析剩餘節點
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) return;
Integer fetchSize = context.getIntAttribute("fetchSize");
Integer timeout = context.getIntAttribute("timeout");
String parameterMap = context.getStringAttribute("parameterMap");
String parameterType = context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = resolveClass(parameterType);
String resultMap = context.getStringAttribute("resultMap");
String resultType = context.getStringAttribute("resultType");
String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang);
Class<?> resultTypeClass = resolveClass(resultType);
String resultSetType = context.getStringAttribute("resultSetType");
//statementType 預設是PreparedStatement
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
String nodeName = context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
//如果是select語句,則預設值是false。否則預設值是true。
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
//如果是select語句,則預設值是true。否則預設值是false。
boolean useCache = context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
// 解析<include>子節點,其實是用引用替換<sql>節點的實際內容。
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());
// 解析<selectKey>子節點,僅對<insert>有用。後面會講解parseSelectKeyNodes方法
List<XNode> selectKeyNodes = context.evalNodes("selectKey");
if (configuration.getDatabaseId() != null) {
parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, configuration.getDatabaseId());
}
parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, null);
//預設是XMLLanguageDriver,parameterTypeClass用不上
//生成持有當前sql語句的sqlSource物件,後面會具體看這個sqlSource是怎麼生成的
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
//keyProperty和keyColumn使用於<insert>語句
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
KeyGenerator keyGenerator;
String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
if (configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
? new Jdbc3KeyGenerator() : new NoKeyGenerator();
}
//通過解析出來的屬性值,構造MappedStatement物件。並放入configuration物件中
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver);
}
<insert>節點的子節點<selectKey>的解析
<selectKey>配置例子:
<selectKey
databaseId="mysql“
keyProperty="id"
resultType="int"
order="BEFORE"
statementType="PREPARED">
select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
</selectKey>
keyProperty:selectKey 語句結果應該被設定的目標屬性。就是給哪個欄位生成主鍵值。
resultType:結果的型別。MyBatis 通常可以算出來,但是寫上也沒有問題。 MyBatis 允許任何簡單型別用作主鍵的型別,包括字串
statementType:MyBatis 支援 STA TEMENT ,PREPARED 和 CALLABLE 語句的對映型別,分別代表 PreparedStatement 和 CallableStatement 型別。
order:這可以被設定為 BEFORE 或 AFTER。如果設定為 BEFORE,那 麼它會首先選擇主鍵, 設定 keyProperty 然後執行插入語句。 如果 設定為 AFTER,那麼先執行插入語句,然後是 selectKey 元素- 這和如 Oracle 資料庫相似,可以在插入語句中嵌入序列呼叫.
java程式碼:
public void parseSelectKeyNodes(String parentId, List<XNode> list, Class<?> parameterTypeClass, LanguageDriver langDriver, String skRequiredDatabaseId) {
//可以有多個<selectKey>節點。
for (XNode nodeToHandle : list) {
String id = parentId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
String databaseId = nodeToHandle.getStringAttribute("databaseId");
if (databaseIdMatchesCurrent(id, databaseId, skRequiredDatabaseId)) {
parseSelectKeyNode(id, nodeToHandle, parameterTypeClass, langDriver, databaseId);
}
}
}
//
public void parseSelectKeyNode(String id, XNode nodeToHandle, Class<?> parameterTypeClass, LanguageDriver langDriver, String databaseId) {
String resultType = nodeToHandle.getStringAttribute("resultType");
Class<?> resultTypeClass = resolveClass(resultType);
StatementType statementType = StatementType.valueOf(nodeToHandle.getStringAttribute("statementType", StatementType.PREPARED.toString()));
String keyProperty = nodeToHandle.getStringAttribute("keyProperty");
boolean executeBefore = "BEFORE".equals(nodeToHandle.getStringAttribute("order", "AFTER"));
//defaults
boolean useCache = false;
boolean resultOrdered = false;
KeyGenerator keyGenerator = new NoKeyGenerator();
Integer fetchSize = null;
Integer timeout = null;
boolean flushCache = false;
String parameterMap = null;
String resultMap = null;
ResultSetType resultSetTypeEnum = null;
//解析生成主鍵的sql語句
SqlSource sqlSource = langDriver.createSqlSource(configuration, nodeToHandle, parameterTypeClass);
SqlCommandType sqlCommandType = SqlCommandType.SELECT;
//生成MappedStatement 物件,放入configuration物件的mappedStatements map中
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, null, databaseId, langDriver);
id = builderAssistant.applyCurrentNamespace(id, false);
MappedStatement keyStatement = configuration.getMappedStatement(id, false);
//同時將這個MappedStatement 物件放入到configuration的 keyGenerator map中
configuration.addKeyGenerator(id, new SelectKeyGenerator(keyStatement, executeBefore));
nodeToHandle.getParent().getNode().removeChild(nodeToHandle.getNode());
}
下篇會主要看看<resultMap>的解析和sqlSource的生成。
相關推薦
Mybatis 程式碼流程及實現原理解析(三)
接上篇, 這篇繼續分析XMLMapperBuilder.parse()裡的configurationElement() 這個方法。 private void configurationElement(XNode context) { try { //m
log4j2 自動刪除過期日誌檔案配置及實現原理解析
日誌檔案自動刪除功能必不可少,當然你可以讓運維去做這事,只是這不地道。而日誌元件是一個必備元件,讓其多做一件刪除的工作,無可厚非。本文就來探討下 log4j 的日誌檔案自動刪除實現吧。 0. 自動刪除配置參考樣例: (log4j2.xml) <?xml version="1.0" enc
TCP/IP協議的三次握手及實現原理
簡單 查找 32位 端口 包括 弱點 建立 成功 有效 TCP/IP是很多的不同的協議組成,實際上是一個協議組,TCP用戶數據報表協議(也稱作TCP傳輸控制協議,Transport Control Protocol。可靠的主機到主機層協議。這裏要先強調一下,傳輸控制協議是O
mybatis介面程式設計原理解析三
一、引言 前面兩篇文章比較詳細的介紹了mybatis介面程式設計實現的原理,連結地址: https://blog.csdn.net/Wenlong_L/article/details/82942831 https://blog.csdn.net/Wenlong_L/article/det
Struts2框架及實現原理和工作流程
Struts2為提供了一個為使用者快速構建應用程式的平臺。 Struts2是基於OpenSymphony的網路工程框架。 Struts2實現模型檢視控制器(MVC)設計模式。 在Struts2的模型、檢視和控制器中分別實現了Action,result和FilterDispa
Shiro許可權管理框架(三):Shiro中許可權過濾器的初始化流程和實現原理
本篇是Shiro系列第三篇,Shiro中的過濾器初始化流程和實現原理。Shiro基於URL的許可權控制是通過Filter實現的,本篇從我們注入的ShiroFilterFactoryBean開始入手,翻看原始碼追尋Shiro中的過濾器的實現原理。 初始化流程 ShiroFilterFactoryBean實現
數據庫水平切分(拆庫拆表)的實現原理解析(轉)
數字 一個數據庫 java ins 結果 都對 不同 com 嚴重 第1章 引言 隨著互聯網應用的廣泛普及,海量數據的存儲和訪問成為了系統設計的瓶頸問題。對於一個大型的互聯網應用,每天幾十億的PV無疑對數據庫造成了相當 高的負載。對於系統的穩定性和擴展性造成了極大的問題。
【dubbo基礎】dubbo學習過程、使用經驗分享及實現原理簡單介紹
multi spring配置 不同 影響 為什麽 exception 同事 sock services 一、前言 部門去年年中開始各種改造,第一步是模塊服務化,這邊初選dubbo試用在一些非重要模塊上,慢慢引入到一些稍微重要的功能上,半年時間,學習過程及線上使用遇到的些問
Spring技術內幕:Spring AOP的實現原理(三)
dede ide configure ida mini == src min dem 生成SingleTon代理對象在getSingleTonInstance方法中完畢,這種方法時ProxyFactoryBean生成AopProxy對象的入口。代理對象會
數據庫水平切分的實現原理解析——分庫,分表,主從,集群,負載均衡器(轉)
支付 讀取 dba 我們 課題研究 穩定性 存在 use 根據 第1章 引言 隨著互聯網應用的廣泛普及,海量數據的存儲和訪問成為了系統設計的瓶頸問題。對於一個大型的互聯網應用,每天幾十億的PV無疑對數據庫造成了相當高的負載。對於系統的穩定性和擴展性造成了極大的問題。通過數
EJB2.0教程 詳解EJB技術及實現原理
tee nsa 普通 事情 println 配置 ransac 教程 聲明 EJB是什麽呢?EJB是一個J2EE體系中的組件.再簡單的說它是一個能夠遠程調用的javaBean.它同普通的javaBean有兩點不同.第一點,就是遠程調用.第二點,就是事務的功能,我們在EJB中
dubbo學習過程、使用經驗分享及實現原理簡單介紹
sum 使用 相同 應該 lib blog 組合 功能模塊 返回 一、前言 部門去年年中開始各種改造,第一步是模塊服務化,這邊初選dubbo試用在一些非重要模塊上,慢慢引入到一些稍微重要的功能上,半年時間,學習過程及線上使用遇到的些問題在此總結下。 整理這篇文章差不多花
ThreadLocal的使用場景及實現原理
局部變量 運行 內部 然而 cal private 中間 pub new t 1. 什麽是ThreadLocal? 線程局部變量(通常,ThreadLocal變量是private static修飾的,此時ThreadLocal變量相當於成為了線程內部的全局變量) 2. 使用
C++函式模板及實現原理
C++為我們提供了函式模板機制。所謂函式模板,實際上是建立一個通用函式,其函式型別和形參型別不具體指定,用一個虛擬的型別來代表。這個通用函式就稱為函式模板。 凡是函式體相同的函式都可以用這個模板來代替,不必定義多個函式,只需在模板中定義
python學習之列表物件實現原理解析
l=[1,2,3] id(l[0]) 1652911120 id(l[1]) 1652911152 id(l[2]) 1652911184
.NetCore 中介軟體之AddAuthentication服務說明及實現原理簡述
如果你使用過.NetCore開發過程序,你會很清楚,在其中我們經常會用到一些如下的程式碼 services.AddAuthentication(options => { options.DefaultAuthentic
Mysql MVCC實現原理解析
MVCC(Multi-Version Concurrency Control | 多版本併發控制) InnoDB通過為每一行記錄新增兩個額外的隱藏的值來實現MVCC,這兩個值一個記錄這行資料何時被建立,另外一個記錄這行資料何時過期(或者被刪除)。但是InnoDB
JSP工作流程及執行原理
JSP起源 在很多動態網頁中,絕大部分內容都是固定不變的,只有區域性內容需要動態產生和改變。 如果使用Servlet程式來輸出只有區域性內容需要動態改變的網頁,其中所有的靜態內容也需要程式設計師用Java程式程式碼產生,整個Servlet程式的程式碼將非常臃腫,編寫和維護都將非常困難。 對大
Linux inotify功能及實現原理
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
Oracle Redo log 狀態及工作原理解析
Oracle重做日誌(redo log)是用來記錄操作條目,用於資料庫資料恢復。為了提高效率,oracle通常建議設定三組redo log。本文將對重做日誌組的狀態以及多種狀態之間切換做解析,力求掌握該知識點。 概述 oracle重做日誌組通常有四種狀態,即unused,inactive,ac