一文掌握MyBatis的動態SQL使用與原理

来源:https://www.cnblogs.com/huaweiyun/archive/2022/12/23/17000620.html
-Advertisement-
Play Games

摘要:使用動態 SQL 並非一件易事,但藉助可用於任何 SQL 映射語句中的強大的動態 SQL 語言,MyBatis 顯著地提升了這一特性的易用性。 本文分享自華為雲社區《MyBatis詳解 - 動態SQL使用與原理》,作者:龍哥手記 。 動態 SQL 是 MyBatis 的強大特性之一。如果你使用 ...


摘要:使用動態 SQL 並非一件易事,但藉助可用於任何 SQL 映射語句中的強大的動態 SQL 語言,MyBatis 顯著地提升了這一特性的易用性。

本文分享自華為雲社區《MyBatis詳解 - 動態SQL使用與原理》,作者:龍哥手記 。

動態 SQL 是 MyBatis 的強大特性之一。如果你使用過 JDBC 或其它類似的框架,你應該能理解根據不同條件拼接 SQL 語句有多痛苦,例如拼接時要確保不能忘記添加必要的空格,還要註意去掉列表最後一個列名的逗號。利用動態 SQL,可以徹底擺脫這種痛苦。

使用動態 SQL 並非一件易事,但藉助可用於任何 SQL 映射語句中的強大的動態 SQL 語言,MyBatis 顯著地提升了這一特性的易用性。

如果你之前用過 JSTL 或任何基於類 XML 語言的文本處理器,你對動態 SQL 元素可能會感覺似曾相識。在 MyBatis 之前的版本中,需要花時間瞭解大量的元素。藉助功能強大的基於 OGNL 的表達式,MyBatis 3 替換了之前的大部分元素,大大精簡了元素種類,現在要學習的元素種類比原來的一半還要少。

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

if

使用動態 SQL 最常見情景是根據條件包含 where 子句的一部分。比如:

<select id="findActiveBlogWithTitleLike"
 resultType="Blog">
  SELECT * FROM BLOG
  WHERE state = ‘ACTIVE’
 <if test="title != null">
    AND title like #{title}
 </if>
</select>

這條語句提供了可選的查找文本功能。如果不傳入 “title”,那麼所有處於 “ACTIVE” 狀態的 BLOG 都會返回;如果傳入了 “title” 參數,那麼就會對 “title” 一列進行模糊查找並返回對應的 BLOG 結果(細心的讀者可能會發現,“title” 的參數值需要包含查找掩碼或通配符字元)。

如果希望通過 “title” 和 “author” 兩個參數進行可選搜索該怎麼辦呢?首先,我想先將語句名稱修改成更名副其實的名稱;接下來,只需要加入另一個條件即可。

<select id="findActiveBlogLike"
 resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
 <if test="title != null">
    AND title like #{title}
 </if>
 <if test="author != null and author.name != null">
    AND author_name like #{author.name}
 </if>
</select>

choose、when、otherwise

有時候,我們不想使用所有的條件,而只是想從多個條件中選擇一個使用。針對這種情況,MyBatis 提供了 choose 元素,它有點像 Java 中的 switch 語句。

還是上面的例子,但是策略變為:傳入了 “title” 就按 “title” 查找,傳入了 “author” 就按 “author” 查找的情形。若兩者都沒有傳入,就返回標記為 featured 的 BLOG(這可能是管理員認為,與其返回大量的無意義隨機 Blog,還不如返回一些由管理員挑選的 Blog)。

<select id="findActiveBlogLike"
 resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
 <choose>
 <when test="title != null">
      AND title like #{title}
 </when>
 <when test="author != null and author.name != null">
      AND author_name like #{author.name}
 </when>
 <otherwise>
      AND featured = 1
 </otherwise>
 </choose>
</select>

trim、where、set

前面幾個例子已經合宜地解決了一個臭名昭著的動態 SQL 問題。現在回到之前的 “if” 示例,這次我們將 “state = ‘ACTIVE’” 設置成動態條件,看看會發生什麼。

<select id="findActiveBlogLike"
 resultType="Blog">
  SELECT * FROM BLOG
  WHERE
 <if test="state != null">
    state = #{state}
 </if>
 <if test="title != null">
    AND title like #{title}
 </if>
 <if test="author != null and author.name != null">
    AND author_name like #{author.name}
 </if>
</select>

如果沒有匹配的條件會怎麼樣?最終這條 SQL 會變成這樣:

SELECT * FROM BLOG
WHERE

這會導致查詢失敗。如果匹配的只是第二個條件又會怎樣?這條 SQL 會是這樣:

SELECT * FROM BLOG
WHERE
AND title like ‘someTitle’

這個查詢也會失敗。這個問題不能簡單地用條件元素來解決。這個問題是如此的難以解決,以至於解決過的人不會再想碰到這種問題。

MyBatis 有一個簡單且適合大多數場景的解決辦法。而在其他場景中,可以對其進行自定義以符合需求。而這,只需要一處簡單的改動:

<select id="findActiveBlogLike"
 resultType="Blog">
  SELECT * FROM BLOG
 <where>
 <if test="state != null">
         state = #{state}
 </if>
 <if test="title != null">
        AND title like #{title}
 </if>
 <if test="author != null and author.name != null">
        AND author_name like #{author.name}
 </if>
 </where>
</select>

where 元素只會在子元素返回任何內容的情況下才插入 “WHERE” 子句。而且,若子句的開頭為 “AND” 或 “OR”,where 元素也會將它們去除。

如果 where 元素與你期望的不太一樣,你也可以通過自定義 trim 元素來定製 where 元素的功能。比如,和 where 元素等價的自定義 trim 元素為:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

prefixOverrides 屬性會忽略通過管道符分隔的文本序列(註意此例中的空格是必要的)。上述例子會移除所有 prefixOverrides 屬性中指定的內容,並且插入 prefix 屬性中指定的內容。

用於動態更新語句的類似解決方案叫做 set。set 元素可以用於動態包含需要更新的列,忽略其它不更新的列。比如:

<update id="updateAuthorIfNecessary">
  update Author
 <set>
 <if test="username != null">username=#{username},</if>
 <if test="password != null">password=#{password},</if>
 <if test="email != null">email=#{email},</if>
 <if test="bio != null">bio=#{bio}</if>
 </set>
  where id=#{id}
</update>

這個例子中,set 元素會動態地在行首插入 SET 關鍵字,並會刪掉額外的逗號(這些逗號是在使用條件語句給列賦值時引入的)。

來看看與 set 元素等價的自定義 trim 元素吧:

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>

註意,我們覆蓋了尾碼值設置,並且自定義了首碼值。

foreach

動態 SQL 的另一個常見使用場景是對集合進行遍歷(尤其是在構建 IN 條件語句的時候)。比如:

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
 <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
 </foreach>
</select>

foreach 元素的功能非常強大,它允許你指定一個集合,聲明可以在元素體內使用的集合項(item)和索引(index)變數。它也允許你指定開頭與結尾的字元串以及集合項迭代之間的分隔符。這個元素也不會錯誤地添加多餘的分隔符,看它多智能!

提示:你可以將任何可迭代對象(如 List、Set 等)、Map 對象或者數組對象作為集合參數傳遞給 foreach。當使用可迭代對象或者數組時,index 是當前迭代的序號,item 的值是本次迭代獲取到的元素。當使用 Map 對象(或者 Map.Entry 對象的集合)時,index 是鍵,item 是值。

至此,我們已經完成了與 XML 配置及映射文件相關的討論。下一章將詳細探討 Java API,以便你能充分利用已經創建的映射配置。

script

要在帶註解的映射器介面類中使用動態 SQL,可以使用 script 元素。比如:

 @Update({"<script>",
 "update Author",
 "  <set>",
 "    <if test='username != null'>username=#{username},</if>",
 "    <if test='password != null'>password=#{password},</if>",
 "    <if test='email != null'>email=#{email},</if>",
 "    <if test='bio != null'>bio=#{bio}</if>",
 "  </set>",
 "where id=#{id}",
 "</script>"})
 void updateAuthorValues(Author author);

bind

bind 元素允許你在 OGNL 表達式以外創建一個變數,並將其綁定到當前的上下文。比如:

<select id="selectBlogsLike" resultType="Blog">
 <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
  SELECT * FROM BLOG
  WHERE title LIKE #{pattern}
</select>

多資料庫支持

如果配置了 databaseIdProvider,你就可以在動態代碼中使用名為 “_databaseId” 的變數來為不同的資料庫構建特定的語句。比如下麵的例子:

<insert id="insert">
 <selectKey keyProperty="id" resultType="int" order="BEFORE">
 <if test="_databaseId == 'oracle'">
      select seq_users.nextval from dual
 </if>
 <if test="_databaseId == 'db2'">
      select nextval for seq_users from sysibm.sysdummy1"
 </if>
 </selectKey>
  insert into users values (#{id}, #{name})
</insert>

動態 SQL 中的插入腳本語言

MyBatis 從 3.2 版本開始支持插入腳本語言,這允許你插入一種語言驅動,並基於這種語言來編寫動態 SQL 查詢語句。

可以通過實現以下介面來插入一種語言:

public interface LanguageDriver {
 ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql);
 SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType);
 SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType);
}

實現自定義語言驅動後,你就可以在 mybatis-config.xml 文件中將它設置為預設語言:

<typeAliases>
 <typeAlias type="org.sample.MyLanguageDriver" alias="myLanguage"/>
</typeAliases>
<settings>
 <setting name="defaultScriptingLanguage" value="myLanguage"/>
</settings>

或者,你也可以使用 lang 屬性為特定的語句指定語言:

<select id="selectBlog" lang="myLanguage">
  SELECT * FROM BLOG
</select>

或者,在你的 mapper 介面上添加 @Lang 註解:

public interface Mapper {
 @Lang(MyLanguageDriver.class)
 @Select("SELECT * FROM BLOG")
 List<Blog> selectBlog();
}
提示:可以使用 Apache Velocity 作為動態語言,更多細節請參考 MyBatis-Velocity 項目。

你前面看到的所有 xml 標簽都由預設 MyBatis 語言提供,而它由語言驅動 org.apache.ibatis.scripting.xmltags.XmlLanguageDriver(別名為 xml)所提供。

動態SQL解析原理

我們在使用mybatis的時候,會在xml中編寫sql語句。比如這段動態sql代碼:

<update id="update" parameterType="org.format.dynamicproxy.mybatis.bean.User">
    UPDATE users
 <trim prefix="SET" prefixOverrides=",">
 <if test="name != null and name != ''">
            name = #{name}
 </if>
 <if test="age != null and age != ''">
            , age = #{age}
 </if>
 <if test="birthday != null and birthday != ''">
            , birthday = #{birthday}
 </if>
 </trim>
    where id = ${id}
</update>

mybatis底層是如何構造這段sql的?下麵帶著這個疑問,我們一步一步分析。

關於動態SQL的介面和類

SqlNode介面,簡單理解就是xml中的每個標簽,比如上述sql的update,trim,if標簽:

public interface SqlNode {
 boolean apply(DynamicContext context);
}

SqlSource Sql源介面,代表從xml文件或註解映射的sql內容,主要就是用於創建BoundSql,有實現類DynamicSqlSource(動態Sql源),StaticSqlSource(靜態Sql源)等:

public interface SqlSource {
 BoundSql getBoundSql(Object parameterObject);
}

BoundSql類,封裝mybatis最終產生sql的類,包括sql語句,參數,參數源數據等參數:

XNode,一個Dom API中的Node介面的擴展類:

BaseBuilder介面及其實現類(屬性,方法省略了,大家有興趣的自己看),這些Builder的作用就是用於構造sql:

下麵我們簡單分析下其中4個Builder:

  • XMLConfigBuilder:解析mybatis中configLocation屬性中的全局xml文件,內部會使用XMLMapperBuilder解析各個xml文件。
  • XMLMapperBuilder:遍歷mybatis中mapperLocations屬性中的xml文件中每個節點的Builder,比如user.xml,內部會使用XMLStatementBuilder處理xml中的每個節點。
  • XMLStatementBuilder:解析xml文件中各個節點,比如select,insert,update,delete節點,內部會使用XMLScriptBuilder處理節點的sql部分,遍歷產生的數據會丟到Configuration的mappedStatements中。
  • XMLScriptBuilder:解析xml中各個節點sql部分的Builder。

LanguageDriver介面及其實現類(屬性,方法省略了,大家有興趣的自己看),該介面主要的作用就是構造sql:

簡單分析下XMLLanguageDriver(處理xml中的sql,RawLanguageDriver處理靜態sql):XMLLanguageDriver內部會使用XMLScriptBuilder解析xml中的sql部分。

源碼分析走起

Spring與Mybatis整合的時候需要配置SqlSessionFactoryBean,該配置會加入數據源和mybatis xml配置文件路徑等信息:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
 <property name="dataSource" ref="dataSource"/>
 <property name="configLocation" value="classpath:mybatisConfig.xml"/>
 <property name="mapperLocations" value="classpath*:org/format/dao/*.xml"/>
</bean>

我們就分析這一段配置背後的細節:

SqlSessionFactoryBean實現了Spring的InitializingBean介面,InitializingBean介面的afterPropertiesSet方法中會調用buildSqlSessionFactory方法 該方法內部會使用XMLConfigBuilder解析屬性configLocation中配置的路徑,還會使用XMLMapperBuilder屬性解析mapperLocations屬性中的各個xml文件。部分源碼如下:

由於XMLConfigBuilder內部也是使用XMLMapperBuilder,我們就看看XMLMapperBuilder的解析細節:

我們關註一下,增刪改查節點的解析:

XMLStatementBuilder的解析:

預設會使用XMLLanguageDriver創建SqlSource(Configuration構造函數中設置)。

XMLLanguageDriver創建SqlSource:

XMLScriptBuilder解析sql:

得到SqlSource之後,會放到Configuration中,有了SqlSource,就能拿BoundSql了,BoundSql可以得到最終的sql。

實例分析

以下麵的xml解析大概說下parseDynamicTags的解析過程:

<update id="update" parameterType="org.format.dynamicproxy.mybatis.bean.User">
    UPDATE users
 <trim prefix="SET" prefixOverrides=",">
 <if test="name != null and name != ''">
            name = #{name}
 </if>
 <if test="age != null and age != ''">
            , age = #{age}
 </if>
 <if test="birthday != null and birthday != ''">
            , birthday = #{birthday}
 </if>
 </trim>
    where id = ${id}
</update>

parseDynamicTags方法的返回值是一個List,也就是一個Sql節點集合。SqlNode本文一開始已經介紹,分析完解析過程之後會說一下各個SqlNode類型的作用。

首先根據update節點(Node)得到所有的子節點,分別是3個子節點:

  • 文本節點 \n UPDATE users
  • trim子節點 ...
  • 文本節點 \n where id = #

遍歷各個子節點:

  • 如果節點類型是文本或者CDATA,構造一個TextSqlNode或StaticTextSqlNode;
  • 如果節點類型是元素,說明該update節點是個動態sql,然後會使用NodeHandler處理各個類型的子節點。這裡的NodeHandler是XMLScriptBuilder的一個內部介面,其實現類包括TrimHandler、WhereHandler、SetHandler、IfHandler、ChooseHandler等。看類名也就明白了這個Handler的作用,比如我們分析的trim節點,對應的是TrimHandler;if節點,對應的是IfHandler...這裡子節點trim被TrimHandler處理,TrimHandler內部也使用parseDynamicTags方法解析節點。

遇到子節點是元素的話,重覆以上步驟:

trim子節點內部有7個子節點,分別是文本節點、if節點、是文本節點、if節點、是文本節點、if節點、文本節點。文本節點跟之前一樣處理,if節點使用IfHandler處理。遍歷步驟如上所示,下麵我們看下幾個Handler的實現細節。

IfHandler處理方法也是使用parseDynamicTags方法,然後加上if標簽必要的屬性:

private class IfHandler implements NodeHandler {
 public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
 List<SqlNode> contents = parseDynamicTags(nodeToHandle);
 MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
 String test = nodeToHandle.getStringAttribute("test");
 IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
 targetContents.add(ifSqlNode);
 }
}

TrimHandler處理方法也是使用parseDynamicTags方法,然後加上trim標簽必要的屬性:

private class TrimHandler implements NodeHandler {
 public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
 List<SqlNode> contents = parseDynamicTags(nodeToHandle);
 MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
 String prefix = nodeToHandle.getStringAttribute("prefix");
 String prefixOverrides = nodeToHandle.getStringAttribute("prefixOverrides");
 String suffix = nodeToHandle.getStringAttribute("suffix");
 String suffixOverrides = nodeToHandle.getStringAttribute("suffixOverrides");
 TrimSqlNode trim = new TrimSqlNode(configuration, mixedSqlNode, prefix, prefixOverrides, suffix, suffixOverrides);
 targetContents.add(trim);
 }
}

以上update方法最終通過parseDynamicTags方法得到的SqlNode集合如下:

trim節點:

由於這個update方法是個動態節點,因此構造出了DynamicSqlSource。DynamicSqlSource內部就可以構造sql了:

DynamicSqlSource內部的SqlNode屬性是一個MixedSqlNode。然後我們看看各個SqlNode實現類的apply方法。下麵分析一下各個SqlNode實現類的apply方法實現:

MixedSqlNode:MixedSqlNode會遍歷調用內部各個sqlNode的apply方法。

public boolean apply(DynamicContext context) {
 for (SqlNode sqlNode : contents) {
 sqlNode.apply(context);
 }
 return true;
}

StaticTextSqlNode:直接append sql文本。

public boolean apply(DynamicContext context) {
 context.appendSql(text);
 return true;
}

IfSqlNode:這裡的evaluator是一個ExpressionEvaluator類型的實例,內部使用了OGNL處理表達式邏輯。

public boolean apply(DynamicContext context) {
 if (evaluator.evaluateBoolean(test, context.getBindings())) {
 contents.apply(context);
 return true;
 }
 return false;
}

TrimSqlNode:

public boolean apply(DynamicContext context) {
 FilteredDynamicContext filteredDynamicContext = new FilteredDynamicContext(context);
 boolean result = contents.apply(filteredDynamicContext);
 filteredDynamicContext.applyAll();
 return result;
}
public void applyAll() {
 sqlBuffer = new StringBuilder(sqlBuffer.toString().trim());
 String trimmedUppercaseSql = sqlBuffer.toString().toUpperCase(Locale.ENGLISH);
 if (trimmedUppercaseSql.length() > 0) {
 applyPrefix(sqlBuffer, trimmedUppercaseSql);
 applySuffix(sqlBuffer, trimmedUppercaseSql);
 }
 delegate.appendSql(sqlBuffer.toString());
}
private void applyPrefix(StringBuilder sql, String trimmedUppercaseSql) {
 if (!prefixApplied) {
 prefixApplied = true;
 if (prefixesToOverride != null) {
 for (String toRemove : prefixesToOverride) {
 if (trimmedUppercaseSql.startsWith(toRemove)) {
 sql.delete(0, toRemove.trim().length());
 break;
 }
 }
 }
 if (prefix != null) {
 sql.insert(0, " ");
 sql.insert(0, prefix);
 }
 }
}

TrimSqlNode的apply方法也是調用屬性contents(一般都是MixedSqlNode)的apply方法,按照實例也就是7個SqlNode,都是StaticTextSqlNode和IfSqlNode。 最後會使用FilteredDynamicContext過濾掉prefix和suffix。

 

點擊關註,第一時間瞭解華為雲新鮮技術~


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 大家好,我是棧長。 今天給大家通報一則框架更新消息,時隔 2021.x 版本發佈一年,Spring Cloud 2022.0.0 最新版發佈了,來看下最新的 Spring Cloud 版本情況: Spring Cloud 無疑是現在 Java 微服務事實上的標準,完全基於 Spring Boot 構 ...
  • 前幾篇博文中介紹了Transformer,由於其優越的性能表現,在工業界使用的越來越廣泛,同時,配合遷移學習理論,越來越多的Transformer預訓練模型和源碼庫逐漸開源,Huggingface就是其中做的最為出色的一家機構。Huggingface是一家在NLP社區做出傑出貢獻的紐約創業公司,其所... ...
  • 歷時9個月的時間,對iNeuOS工業互聯網操作系統進行全面重構,發佈內部測試版本。重構的主要目的:工程化的框架優化,更好的聚焦工業領域業務發展。重構也是保障產品能夠可持續發展的重要手段,同時兼顧產品的安全性和穩定性。 ...
  • 目錄 一、什麼是dapper 二、實現問題與思路 & 源碼參考 三、小結 一、什麼是dapper dapper是個組件,一個dll文件,可以通過NuGet下載。 作用:快速訪問資料庫並自動完成資料庫表對象到C#類對象的欄位映射。 應用:對於要完成簡單的查詢、報表之類的應用是非常快捷、方便的。 比較: ...
  • 摘要:為解決用戶自定義處理設備數據以及自定義協議設備快速接入IOT平臺的訴求,華為IoT邊緣提供ModuleSDK,用戶可通過集成SDK讓設備以及設備數據快速上雲。 本文分享自華為雲社區《【華為雲IoTEdge開發實戰】Java開發如何通過IoT邊緣ModuleSDK進行進程應用的開發》,作者: 華 ...
  • 0.腳本作者 Lucifer([email protected]) yuanzijian([email protected]) 1.達夢一鍵安裝腳本完整版附件內容(附件在本文末尾) 1.1.腳本文件夾soft 1.DMShellInstall:資料庫安裝腳本,詳細使用看README.md2.DMCh ...
  • 如有問題請及時指正 select version(); 數據準備 -- 1.學生表 -- S# 學生編號,Sname 學生姓名,Sage 出生年月,Ssex 學生性別 CREATE TABLE `Student` ( `S#` varchar(10) NOT NULL, `Sname` varcha ...
  • GreatSQL社區原創內容未經授權不得隨意使用,轉載請聯繫小編並註明來源。 GreatSQL是MySQL的國產分支版本,使用上與MySQL一致。 作者:蟹黃瓜子 文章來源:社區投稿 1.基於四層+七層的負載均衡 實現負載均衡的方式有很多,例如基於DNS的負載均衡、基於四層交換技術的負載均衡、基於七 ...
一周排行
    -Advertisement-
    Play Games
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...