<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Programming on LOCRIAN_V Blog</title>
    <link>https://lv-blog.pages.dev/en/categories/programming/</link>
    <description>Recent content in Programming on LOCRIAN_V Blog</description>
    <generator>Hugo</generator>
    <language>en</language>
    <lastBuildDate>Sat, 06 Jun 2026 21:40:20 +0800</lastBuildDate>
    <atom:link href="https://lv-blog.pages.dev/en/categories/programming/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Understanding the CAS Spin Pattern in Java Through Random</title>
      <link>https://lv-blog.pages.dev/en/posts/programming/backend/understanding-cas-spin-from-random/</link>
      <pubDate>Sat, 06 Jun 2026 21:40:20 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/en/posts/programming/backend/understanding-cas-spin-from-random/</guid>
      <description>&lt;p&gt;Do you understand this snippet?&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    oldseed &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; seed.&lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    newseed &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (oldseed &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; ...) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; mask;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;seed.&lt;span style=&#34;color:#a6e22e&#34;&gt;compareAndSet&lt;/span&gt;(oldseed, newseed));   &lt;span style=&#34;color:#75715e&#34;&gt;// ← CAS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;level-0-first-understand-why-a-variable-is-dangerous-in-multithreading&#34;&gt;Level 0: First understand why a &amp;ldquo;variable&amp;rdquo; is dangerous in multithreading&lt;/h2&gt;
&lt;p&gt;Suppose there&amp;rsquo;s a variable &lt;code&gt;seed = 100&lt;/code&gt;, and two threads want to change it &lt;strong&gt;at the same time&lt;/strong&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Thread A: reads seed=100 → computes new value 200 → writes back seed=200
Thread B: reads seed=100 → computes new value 300 → writes back seed=300
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here&amp;rsquo;s the problem — both read 100, each computes on its own, and whoever writes later wins. A&amp;rsquo;s result is overwritten by B, A&amp;rsquo;s work is wasted, and &lt;strong&gt;the program has no idea anything went wrong&lt;/strong&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>A Close Look at Common Caching Strategies</title>
      <link>https://lv-blog.pages.dev/en/posts/programming/backend/redis/common-cache-strategies/</link>
      <pubDate>Tue, 05 May 2026 22:46:44 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/en/posts/programming/backend/redis/common-cache-strategies/</guid>
      <description>&lt;h1 id=&#34;the-three-cache-brothers&#34;&gt;The Three Cache Brothers&lt;/h1&gt;
&lt;h2 id=&#34;cache-penetration&#34;&gt;Cache Penetration&lt;/h2&gt;
&lt;p&gt;What it is: a flood of requests querying data that doesn&amp;rsquo;t exist in the database. The cache has nothing, so it keeps hammering the database.
Prevention:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Cache null values
&lt;ol&gt;
&lt;li&gt;Even when the database finds nothing, put an empty marker in the cache (with a short TTL, e.g. 2 minutes; only after that TTL expires can the database be queried again)&lt;/li&gt;
&lt;li&gt;The next time the same id comes in, it&amp;rsquo;s blocked directly&lt;/li&gt;
&lt;li&gt;Pros: effectively intercepts large numbers of penetration requests&lt;/li&gt;
&lt;li&gt;Cons:
&lt;ol&gt;
&lt;li&gt;A malicious attack with many different non-existent keys fills the cache with useless data and wastes memory&lt;/li&gt;
&lt;li&gt;It delays data consistency; new data has to wait for the cache to expire&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bloom filter&lt;/strong&gt; (recommended)
&lt;ol&gt;
&lt;li&gt;At startup, put all legitimate ids into the Bloom filter&lt;/li&gt;
&lt;li&gt;If the Bloom filter says no, it really doesn&amp;rsquo;t exist&lt;/li&gt;
&lt;li&gt;Pros:
&lt;ol&gt;
&lt;li&gt;No high-memory-usage risk&lt;/li&gt;
&lt;li&gt;No data-consistency risk&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Cons:
&lt;ol&gt;
&lt;li&gt;There&amp;rsquo;s a tiny false-positive rate&lt;/li&gt;
&lt;li&gt;Insertions and deletions need maintenance; when data updates, the Bloom filter must be updated in sync&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Rate limiting and validation at the interface layer
&lt;ol&gt;
&lt;li&gt;Intercept at the API gateway or Controller layer&lt;/li&gt;
&lt;li&gt;Parameter validation, rate limiting, and degradation&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Mutex-based rebuild
&lt;ol&gt;
&lt;li&gt;A mutex mainly solves cache breakdown (a hot key expiring), but in the cache-empty-object scenario, if there are many concurrent requests for a non-existent key, a lock can be used&lt;/li&gt;
&lt;li&gt;Each access lets one thread query the DB while the others wait&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Big companies&amp;rsquo; practice&lt;/p&gt;</description>
    </item>
    <item>
      <title>The Fundamentals You Need to Build a Chat Room</title>
      <link>https://lv-blog.pages.dev/en/posts/programming/backend/fundamentals-for-building-a-chatroom/</link>
      <pubDate>Fri, 03 Apr 2026 11:45:26 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/en/posts/programming/backend/fundamentals-for-building-a-chatroom/</guid>
      <description>&lt;h2 id=&#34;the-tcp-three-way-handshake&#34;&gt;The TCP Three-Way Handshake&lt;/h2&gt;
&lt;p&gt;Client — Server&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Purpose of the three-way handshake&lt;/strong&gt;: the client&amp;rsquo;s send and receive are both fine, and the server&amp;rsquo;s send and receive are both fine.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&#34;first-handshake-the-client-sends-syn-synchronize-request&#34;&gt;First handshake: the client sends SYN (synchronize request)&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Client&lt;/strong&gt;: I know nothing, so I&amp;rsquo;ll send out a &lt;strong&gt;SYN&lt;/strong&gt; message and see if anyone receives it.&lt;/li&gt;
&lt;li&gt;If no one replies, I send it a few more times (timeout retransmission).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;At this moment, what the client knows is&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;I sent it, but I don&amp;rsquo;t know whether anyone received it ❌&lt;/li&gt;
&lt;li&gt;Can I receive messages? Unknown ❌&lt;/li&gt;
&lt;li&gt;Does the other side exist? Unknown ❌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h4 id=&#34;second-handshake-the-server-replies-synack-synchronize--acknowledge&#34;&gt;Second handshake: the server replies SYN+ACK (synchronize + acknowledge)&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The &lt;strong&gt;server&lt;/strong&gt; received the client&amp;rsquo;s SYN.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Why the Backend Sometimes Needs to Maintain a Refresh_Tokens Table</title>
      <link>https://lv-blog.pages.dev/en/posts/programming/backend/why-backend-maintains-a-refresh-tokens-table/</link>
      <pubDate>Mon, 02 Mar 2026 20:07:07 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/en/posts/programming/backend/why-backend-maintains-a-refresh-tokens-table/</guid>
      <description>&lt;h1 id=&#34;the-normal-jwt-request-flow&#34;&gt;The Normal JWT Request Flow&lt;/h1&gt;
&lt;pre class=&#34;mermaid&#34;&gt;sequenceDiagram
    participant User as User
    participant Frontend as Frontend (browser/app)
    participant Auth as Auth server
    participant Backend as Backend API server

    Note over User,Backend: 1. Login phase
    User-&gt;&gt;Frontend: enter username/password
    Frontend-&gt;&gt;Auth: POST /login (credentials)
    Auth-&gt;&gt;Auth: verify credentials
    Auth--&gt;&gt;Frontend: return access_token + refresh_token
    Frontend-&gt;&gt;Frontend: store tokens (memory/localStorage)
    Frontend--&gt;&gt;User: login successful

    Note over User,Backend: 2. Normal request phase
    User-&gt;&gt;Frontend: request a protected resource
    Frontend-&gt;&gt;Backend: GET /api/resource&lt;br/&gt;Authorization: Bearer access_token
    Backend-&gt;&gt;Backend: verify access_token signature and expiry
    Backend--&gt;&gt;Frontend: return the requested resource
    Frontend--&gt;&gt;User: display data

    Note over User,Backend: 3. Access token expires
    User-&gt;&gt;Frontend: keep requesting
    Frontend-&gt;&gt;Backend: GET /api/resource&lt;br/&gt;Authorization: Bearer access_token (expired)
    Backend--&gt;&gt;Frontend: 401 Unauthorized (token expired)
    Frontend-&gt;&gt;Frontend: detects 401, triggers refresh logic

    Note over Frontend,Auth: 4. Token refresh phase
    Frontend-&gt;&gt;Auth: POST /refresh&lt;br/&gt;refresh_token
    Auth-&gt;&gt;Auth: verify refresh_token
    Auth--&gt;&gt;Frontend: return a new access_token (optionally a new refresh_token)
    Frontend-&gt;&gt;Frontend: update the stored access_token
    Frontend-&gt;&gt;Backend: retry the original request (new access_token)
    Backend--&gt;&gt;Frontend: return the requested resource
    Frontend--&gt;&gt;User: display data
&lt;/pre&gt;
&lt;h1 id=&#34;why-you-cant-rely-on-jwt-alone&#34;&gt;Why You Can&amp;rsquo;t Rely on JWT Alone&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;Once a refresh token is issued and the backend doesn&amp;rsquo;t store it, it becomes an &lt;strong&gt;uncontrollable long-term pass&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;You can&amp;rsquo;t forcibly log a user out&lt;/li&gt;
&lt;li&gt;You can&amp;rsquo;t log out a single device&lt;/li&gt;
&lt;li&gt;You can&amp;rsquo;t tell whether a token has been stolen (an attacker who gets the refresh token can keep refreshing the access token forever)&lt;/li&gt;
&lt;li&gt;You can&amp;rsquo;t implement session management&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&#34;the-flow-after-using-a-refresh_tokens-table&#34;&gt;The Flow After Using a refresh_tokens Table&lt;/h1&gt;
&lt;pre class=&#34;mermaid&#34;&gt;sequenceDiagram
    participant User as User
    participant Frontend as Frontend
    participant Auth as Auth server
    participant DB as Database (refresh_tokens table)
    participant Backend as Backend API server

    Note over User,DB: 1. Login phase (issue token + persist)
    User-&gt;&gt;Frontend: enter username/password
    Frontend-&gt;&gt;Auth: POST /login
    Auth-&gt;&gt;Auth: verify credentials
    Auth-&gt;&gt;DB: generate a unique refresh_token_id&lt;br/&gt;INSERT INTO refresh_tokens&lt;br/&gt;(user_id, token_hash, expires_at,&lt;br/&gt; device_info, ip_address, revoked)
    DB--&gt;&gt;Auth: insert successful
    Auth-&gt;&gt;Auth: generate access_token + refresh_token&lt;br/&gt;(refresh_token contains the id reference)
    Auth--&gt;&gt;Frontend: return access_token + refresh_token
    Frontend-&gt;&gt;Frontend: store tokens
    Frontend--&gt;&gt;User: login successful

    Note over User,Backend: 2. Normal request (same as before)
    User-&gt;&gt;Frontend: request a resource
    Frontend-&gt;&gt;Backend: GET /api/resource&lt;br/&gt;Authorization: Bearer access_token
    Backend-&gt;&gt;Backend: verify access_token
    Backend--&gt;&gt;Frontend: return the resource

    Note over Frontend,DB: 3. Access token expires → refresh
    Frontend-&gt;&gt;Backend: GET /api/resource (access_token expired)
    Backend--&gt;&gt;Frontend: 401 Unauthorized
    Frontend-&gt;&gt;Auth: POST /refresh&lt;br/&gt;refresh_token

    Note over Auth,DB: 4. Refresh verification (multi-step checks)
    Auth-&gt;&gt;Auth: parse refresh_token, extract token_id
    Auth-&gt;&gt;DB: SELECT * FROM refresh_tokens&lt;br/&gt;WHERE id = token_id
    DB--&gt;&gt;Auth: return the record
    
    Auth-&gt;&gt;Auth: verification checklist:
    Note over Auth: ✅ does token_hash match&lt;br/&gt;✅ is it expired (expires_at &gt; now())&lt;br/&gt;✅ is it revoked (revoked = false)&lt;br/&gt;✅ is the user still valid&lt;br/&gt;✅ does device info match (optional)
    
    alt all checks pass
        Auth-&gt;&gt;DB: UPDATE refresh_tokens&lt;br/&gt;SET last_used_at = now(),&lt;br/&gt;    last_used_ip = current_ip&lt;br/&gt;WHERE id = token_id
        Auth-&gt;&gt;Auth: generate a new access_token&lt;br/&gt;(optional: extend refresh_token validity)
        Auth--&gt;&gt;Frontend: return the new access_token
        Frontend-&gt;&gt;Frontend: update access_token
        Frontend-&gt;&gt;Backend: retry the original request (new token)
        Backend--&gt;&gt;Frontend: return the resource
    else checks fail
        Auth--&gt;&gt;Frontend: 401/403 (refresh failed)
        Frontend-&gt;&gt;Frontend: clear all tokens
        Frontend-&gt;&gt;User: redirect to the login page
    end

    Note over User,DB: 5. Explicit logout
    User-&gt;&gt;Frontend: click logout
    Frontend-&gt;&gt;Auth: POST /logout&lt;br/&gt;refresh_token
    Auth-&gt;&gt;DB: UPDATE refresh_tokens&lt;br/&gt;SET revoked = true&lt;br/&gt;WHERE id = token_id
    Auth--&gt;&gt;Frontend: logout successful
    Frontend-&gt;&gt;Frontend: clear local tokens
    Frontend--&gt;&gt;User: logged out

    Note over User,DB: 6. Security scenario: password change
    User-&gt;&gt;Frontend: change password
    Frontend-&gt;&gt;Auth: POST /change-password
    Auth-&gt;&gt;DB: UPDATE refresh_tokens&lt;br/&gt;SET revoked = true&lt;br/&gt;WHERE user_id = current_user_id
    Note over DB: revoke all of the user&#39;s refresh_tokens&lt;br/&gt;(force all devices to log in again)
    Auth--&gt;&gt;Frontend: password change successful
    Frontend-&gt;&gt;Frontend: clear local tokens
    Frontend--&gt;&gt;User: please log in again
&lt;/pre&gt;</description>
    </item>
    <item>
      <title>Microservice Auth System: Architecture Walkthrough and Interview Prep</title>
      <link>https://lv-blog.pages.dev/en/posts/programming/backend/microservice-auth-architecture-and-interview/</link>
      <pubDate>Mon, 26 Jan 2026 18:05:16 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/en/posts/programming/backend/microservice-auth-architecture-and-interview/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tech stack&lt;/strong&gt;: Spring Boot 3 · Spring Cloud Alibaba · OpenFeign · JWT · Gateway · Nacos&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;how-to-use-this-document&#34;&gt;How to use this document&lt;/h2&gt;
&lt;p&gt;When you talk about a project in an interview, the worst thing you can do is start from code details. The right order is:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;First explain what problem it solves → then the overall architecture → then walk through it by request flow → finally handle the deep-dive questions.&lt;/strong&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>On What a Hassle Login Is</title>
      <link>https://lv-blog.pages.dev/en/posts/programming/backend/the-hassle-of-login/</link>
      <pubDate>Fri, 26 Dec 2025 15:56:40 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/en/posts/programming/backend/the-hassle-of-login/</guid>
      <description>From SMS codes to Passkeys — a look at the hassles behind login that nobody wants to think through, and a set of practices you can actually ship.</description>
    </item>
    <item>
      <title>The Incompatibility Between PostgreSQL and Java&#39;s LocalDateTime</title>
      <link>https://lv-blog.pages.dev/en/posts/programming/backend/bugs-solutions/postgresql-java-localdatetime-incompatibility/</link>
      <pubDate>Sat, 25 Oct 2025 12:55:49 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/en/posts/programming/backend/bugs-solutions/postgresql-java-localdatetime-incompatibility/</guid>
      <description>&lt;p&gt;&lt;strong&gt;Java&amp;rsquo;s &lt;code&gt;LocalDateTime&lt;/code&gt; and PostgreSQL&amp;rsquo;s time types &amp;ldquo;aren&amp;rsquo;t talking about the same kind of time.&amp;rdquo;&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;first-get-clear-on-postgresqls-two-time-types&#34;&gt;First, get clear on PostgreSQL&amp;rsquo;s two time types&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;TIMESTAMP           → without time zone, just a bare time &amp;#34;2026-06-25 10:00:00&amp;#34;
TIMESTAMPTZ         → with time zone, stored internally as UTC, converted to the session time zone on query
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2 id=&#34;on-the-java-side&#34;&gt;On the Java side&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;LocalDateTime       → no concept of a time zone, just a bare time
ZonedDateTime       → with time zone
OffsetDateTime      → with an offset (e.g. +08:00)
Instant             → a UTC timestamp
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2 id=&#34;why-the-error-happens&#34;&gt;Why the error happens&lt;/h2&gt;
&lt;p&gt;The PostgreSQL JDBC driver (especially newer versions 42.x+) is &lt;strong&gt;very strict&lt;/strong&gt; about type matching:&lt;/p&gt;</description>
    </item>
    <item>
      <title>Swagger 2 vs. Swagger 3 Annotation Comparison Table</title>
      <link>https://lv-blog.pages.dev/en/posts/programming/backend/bugs-solutions/swagger-2-3-annotation-comparison/</link>
      <pubDate>Thu, 25 Sep 2025 09:06:35 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/en/posts/programming/backend/bugs-solutions/swagger-2-3-annotation-comparison/</guid>
      <description>&lt;p&gt;In the Spring Boot ecosystem, the annotations changed a great deal between Swagger 2.0 (typically using the &lt;code&gt;Springfox&lt;/code&gt; dependency) and Swagger 3.0 (typically using the &lt;code&gt;Springdoc-openapi&lt;/code&gt; dependency, based on the OpenAPI 3 spec).&lt;/p&gt;
&lt;p&gt;Below is a complete correspondence table of the common annotations in Swagger 2.0 vs. Swagger 3.0 (OpenAPI 3):&lt;/p&gt;
&lt;h3 id=&#34;1-core-annotation-correspondence-table&#34;&gt;1. Core annotation correspondence table&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Description&lt;/th&gt;
					&lt;th&gt;Swagger 2.0 annotation (io.swagger.annotations)&lt;/th&gt;
					&lt;th&gt;Swagger 3.0 annotation (io.swagger.v3.oas.annotations)&lt;/th&gt;
					&lt;th&gt;Notes&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Mark a controller class&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;@Api(tags = &amp;quot;User API&amp;quot;)&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;@Tag(name = &amp;quot;User API&amp;quot;)&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;3.0 removed the &lt;code&gt;description&lt;/code&gt; attribute; use &lt;code&gt;name&lt;/code&gt; uniformly&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Mark an API method&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;@ApiOperation(value = &amp;quot;Get user&amp;quot;)&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;@Operation(summary = &amp;quot;Get user&amp;quot;)&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;In 3.0 &lt;code&gt;value&lt;/code&gt; became &lt;code&gt;summary&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Request/entity class&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;@ApiModel(value = &amp;quot;User object&amp;quot;)&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;@Schema(description = &amp;quot;User object&amp;quot;)&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;3.0 greatly simplified this; use &lt;code&gt;@Schema&lt;/code&gt; uniformly&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Entity class property&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;@ApiModelProperty(value = &amp;quot;Name&amp;quot;)&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;@Schema(description = &amp;quot;Name&amp;quot;)&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;As above, merged into &lt;code&gt;@Schema&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Ignore a property&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;@ApiModelProperty(hidden = true)&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;@Schema(hidden = true)&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Ignore a whole class/method&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;@ApiIgnore&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;@Hidden&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;For endpoints or parameters you don&amp;rsquo;t want exposed in docs&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id=&#34;2-request-parameter-annotation-correspondence-table&#34;&gt;2. Request-parameter annotation correspondence table&lt;/h3&gt;
&lt;p&gt;For method parameters (URL path parameters, query parameters, etc.), 3.0 introduced a more structured configuration:&lt;/p&gt;</description>
    </item>
    <item>
      <title>SCA 2023.X — Troubleshooting and Fixing the Nacos Bootstrap Config Failure</title>
      <link>https://lv-blog.pages.dev/en/posts/programming/backend/bugs-solutions/sca-nacos-bootstrap-bug/</link>
      <pubDate>Thu, 24 Jul 2025 22:40:26 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/en/posts/programming/backend/bugs-solutions/sca-nacos-bootstrap-bug/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Issue source&lt;/strong&gt;: &lt;a href=&#34;https://github.com/alibaba/spring-cloud-alibaba/issues/3931&#34;&gt;spring-cloud-alibaba#3931&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Affected versions&lt;/strong&gt;: &lt;code&gt;spring-cloud-alibaba 2023.0.1.3+&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;1-problem-description&#34;&gt;1. Problem Description&lt;/h2&gt;
&lt;p&gt;In Spring Cloud Alibaba 2023.X, placing Nacos config (including &lt;code&gt;extension-configs&lt;/code&gt;, &lt;code&gt;shared-configs&lt;/code&gt;, etc.) in &lt;code&gt;bootstrap.yml&lt;/code&gt; / &lt;code&gt;bootstrap.properties&lt;/code&gt; causes &lt;strong&gt;the config center&amp;rsquo;s content to fail to load properly&lt;/strong&gt;, even though the logs show the &lt;code&gt;bootstrap&lt;/code&gt; file itself was read.&lt;/p&gt;
&lt;p&gt;Moving the same config to &lt;code&gt;application.yml&lt;/code&gt; makes everything work again.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;2-root-cause&#34;&gt;2. Root Cause&lt;/h2&gt;
&lt;pre class=&#34;mermaid&#34;&gt;flowchart TD
    A[bootstrap.yml is read] --&gt; B{SCA version check}
    B -- 2023.0.1.2 and earlier --&gt; C[✅ Nacos config center loads normally]
    B -- 2023.0.1.3 and later --&gt; D[❌ extension-configs / shared-configs fail]
    D --&gt; E[Root cause: SCA 2023.0.1.3 changed the config-load priority mechanism]
    E --&gt; F[Nacos PropertySource registered in the bootstrap phase is overwritten or discarded by later steps]
&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;In short:&lt;/strong&gt;&lt;br&gt;
SCA &lt;code&gt;2023.0.1.3&lt;/code&gt; made a &lt;strong&gt;backward-incompatible internal change&lt;/strong&gt;, causing the Nacos extension config in &lt;code&gt;bootstrap.yml&lt;/code&gt; to be &amp;ldquo;dropped&amp;rdquo; in the loading chain, while config in &lt;code&gt;application.yml&lt;/code&gt; takes the new path and is unaffected.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Some Common Nacos Configurations</title>
      <link>https://lv-blog.pages.dev/en/posts/programming/backend/code-snippets/nacos-config-tips/</link>
      <pubDate>Tue, 24 Jun 2025 19:01:02 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/en/posts/programming/backend/code-snippets/nacos-config-tips/</guid>
      <description>&lt;h1 id=&#34;bootstrap-config&#34;&gt;bootstrap config&lt;/h1&gt;
&lt;h2 id=&#34;bootstrapyaml&#34;&gt;bootstrap.yaml&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;server&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;port&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;10888&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;tomcat&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;uri-encoding&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;UTF-8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;spring&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;profiles&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;active&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;dev&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;application&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;archive-service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;cloud&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;nacos&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;config&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;file-extension&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;yaml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;shared-configs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#f92672&#34;&gt;data-id&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;shared-spring.yaml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;refresh&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#f92672&#34;&gt;data-id&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;shared-redis.yaml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;refresh&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#f92672&#34;&gt;data-id&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;shared-mybatis.yaml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;refresh&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#f92672&#34;&gt;data-id&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;shared-logs.yaml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;refresh&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#f92672&#34;&gt;data-id&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;shared-feign.yaml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;refresh&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#f92672&#34;&gt;data-id&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;shared-logs.yaml&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# shared logging config&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;refresh&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#          - data-id: shared-feign.yaml # shared feign config&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#            refresh: false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;lia&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;jdbc&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;database&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;tb_archive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;bootstrap-devyaml&#34;&gt;bootstrap-dev.yaml&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;spring&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;cloud&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;nacos&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;server-addr&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;192.168.2.115&lt;/span&gt;:&lt;span style=&#34;color:#ae81ff&#34;&gt;8848&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# nacos registry&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;discovery&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;namespace&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;f923fb34-cb0a-4c06-8fca-ad61ea61a3f0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;group&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;DEFAULT_GROUP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;ip&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;192.168.2.115&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;logging&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;level&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;asia.liminality&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;debug&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;nacos-config&#34;&gt;nacos config&lt;/h1&gt;
&lt;h2 id=&#34;shared-springyaml&#34;&gt;shared-spring.yaml&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;spring&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;jackson&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;default-property-inclusion&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;non_null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;main&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;allow-bean-definition-overriding&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;mvc&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;pathmatch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#75715e&#34;&gt;# Fixes the exception: swagger Failed to start bean &amp;#39;documentationPluginsBootstrapper&amp;#39;; nested exception is java.lang.NullPointerException&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#75715e&#34;&gt;# Because Springfox&amp;#39;s path matching is based on AntPathMatcher, while Spring Boot 2.6.X uses PathPatternMatcher&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;matching-strategy&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ant_path_matcher&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;shared-redisyaml&#34;&gt;shared-redis.yaml&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;spring&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;redis&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;host&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${lia.redis.host:192.168.2.115}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;password&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${lia.redis.password:github}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;lettuce&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;pool&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;max-active&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${lia.redis.pool.max-active:8}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;max-idle&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${lia.redis.pool.max-idle:8}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;min-idle&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${lia.redis.pool.min-idle:1}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;max-wait&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${lia.redis.pool.max-wait:300}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;shared-mybatisyaml&#34;&gt;shared-mybatis.yaml&lt;/h2&gt;
&lt;h3 id=&#34;mysql&#34;&gt;MySQL&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;spring&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;datasource&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;driver-class-name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;com.mysql.cj.jdbc.Driver&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;url&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;jdbc:mysql://${sh.jdbc.host:127.0.0.1}/${sh.jdbc.database}?useUnicode=true&amp;amp;characterEncoding=utf8&amp;amp;serverTimezone=Asia/Shanghai&amp;amp;useSSL=false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;username&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${lia.jdbc.username:root}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;password&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${lia.jdbc.password:794211}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;mybatis-plus&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;default-enum-type-handler&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;global-config&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;db-config&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;logic-delete-field&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;deletedAt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;logic-not-delete-value&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;null&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;logic-delete-value&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;now()&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;id-type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;assign_id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;insert-strategy&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;not_null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;update-strategy&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;not_null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;postgresql&#34;&gt;PostgreSQL&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;spring&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;datasource&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;driver-class-name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;org.postgresql.Driver&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;url&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;jdbc:postgresql://${lia.jdbc.host:192.168.2.115}:${lia.jdbc.port:5432}/${lia.jdbc.database}?useUnicode=true&amp;amp;characterEncoding=UTF-8&amp;amp;autoReconnect=true&amp;amp;serverTimezone=Asia/Shanghai&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;username&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${lia.jdbc.username:postgres}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;password&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${lia.jdbc.password:github}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;mybatis-plus&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;default-enum-type-handler&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;global-config&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;db-config&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;logic-delete-field&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;deletedAt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;logic-not-delete-value&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;null&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;logic-delete-value&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;now()&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;id-type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;assign_id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;insert-strategy&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ignored&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;update-strategy&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ignored&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;select-strategy&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;not_null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;shared-logsyaml&#34;&gt;shared-logs.yaml&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;logging&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;pattern&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;dateformat&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;HH:mm:ss.SSS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;console&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%clr(%d{${LOG_DATEFORMAT_PATTERN}}){faint}-[${hostname}][%X{requestId:-sys}] %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;file&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%d{${LOG_DATEFORMAT_PATTERN}}-[${hostname}][%X{requestId:-sys}]-${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%15.15t] %-40.40logger{39} : %m%n&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;file&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;path&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;logs/${spring.application.name}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;shared-feignyaml&#34;&gt;shared-feign.yaml&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;feign&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;client&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;config&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;default&lt;/span&gt;: &lt;span style=&#34;color:#75715e&#34;&gt;# the default global config&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;loggerLevel&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;BASIC&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# log level; BASIC is the basic request and response info&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;httpclient&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;enabled&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# enable Feign&amp;#39;s HttpClient support&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;max-connections&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;200&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# max connections&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;max-connections-per-route&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;50&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# max connections per route&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;sentinel&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;enabled&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
    </item>
    <item>
      <title>A Guide to Suffering Through WeChat Mini Program Development</title>
      <link>https://lv-blog.pages.dev/en/posts/programming/wechatapp-development-notes/</link>
      <pubDate>Wed, 23 Apr 2025 14:02:30 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/en/posts/programming/wechatapp-development-notes/</guid>
      <description>&lt;h2 id=&#34;the-auth-interaction-flow-between-a-wechat-mini-program-and-the-backend&#34;&gt;The auth interaction flow between a WeChat Mini Program and the backend&lt;/h2&gt;
&lt;p&gt;The core flow of WeChat Mini Program login: the mini program gets a temporary credential &lt;code&gt;code&lt;/code&gt; → the backend exchanges the &lt;code&gt;code&lt;/code&gt; for an &lt;code&gt;openid&lt;/code&gt; → the backend issues its own JWT to the mini program. Below is the overall flow diagram.&lt;/p&gt;
&lt;pre class=&#34;mermaid&#34;&gt;sequenceDiagram
    participant MP as WeChat Mini Program
    participant WX as WeChat server
    participant GW as gateway
    participant Auth as auth module
    participant User as user-service
    participant DB as lia_user database

    MP-&gt;&gt;WX: wx.login() to get code
    WX--&gt;&gt;MP: returns temporary code
    MP-&gt;&gt;GW: POST /auth/wx-login {code}
    GW-&gt;&gt;Auth: forward the request
    Auth-&gt;&gt;WX: code2session(code)
    WX--&gt;&gt;Auth: returns openid + session_key
    Auth-&gt;&gt;User: OpenFeign to query/create the user
    User-&gt;&gt;DB: query by openid
    DB--&gt;&gt;User: user record (or empty)
    User--&gt;&gt;Auth: return userId
    Auth-&gt;&gt;Auth: generate JWT
    Auth--&gt;&gt;MP: return token + user info
    MP-&gt;&gt;MP: store into Storage
&lt;/pre&gt;
&lt;h3 id=&#34;breaking-down-the-key-points&#34;&gt;Breaking down the key points&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1. The mini program gets the code (frontend)&lt;/strong&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Message Middleware: Interview Questions</title>
      <link>https://lv-blog.pages.dev/en/posts/programming/for-interview/mq-interview-questions/</link>
      <pubDate>Sun, 23 Mar 2025 13:21:54 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/en/posts/programming/for-interview/mq-interview-questions/</guid>
      <description>&lt;h1 id=&#34;rabbitmq&#34;&gt;RabbitMQ&lt;/h1&gt;
&lt;h2 id=&#34;how-does-rabbitmq-guarantee-messages-are-not-lost&#34;&gt;How does RabbitMQ guarantee messages are not lost?&lt;/h2&gt;
&lt;!-- TODO review --&gt;
&lt;ul&gt;
&lt;li&gt;Where can loss happen
&lt;ul&gt;
&lt;li&gt;publisher -&amp;gt; exchange
&lt;ul&gt;
&lt;li&gt;Producer confirmation mechanism
&lt;ul&gt;
&lt;li&gt;ack publish-confirm&lt;/li&gt;
&lt;li&gt;nack publish-confirm&lt;/li&gt;
&lt;li&gt;ack publish-return&lt;/li&gt;
&lt;li&gt;How to handle a failed message
&lt;ul&gt;
&lt;li&gt;Callback method&lt;/li&gt;
&lt;li&gt;Log it&lt;/li&gt;
&lt;li&gt;Save to the database and resend on a schedule&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;exchange -&amp;gt; queue
&lt;ul&gt;
&lt;li&gt;Message persistence
&lt;ul&gt;
&lt;li&gt;Exchange persistence&lt;/li&gt;
&lt;li&gt;Queue persistence&lt;/li&gt;
&lt;li&gt;Message persistence&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;queue&lt;/li&gt;
&lt;li&gt;queue -&amp;gt; consumer
&lt;ul&gt;
&lt;li&gt;Consumer acknowledgment mechanism&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;how-is-the-duplicate-consumption-problem-in-rabbitmq-solved&#34;&gt;How is the duplicate-consumption problem in RabbitMQ solved?&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Causes of duplicate consumption&lt;/strong&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Spring Cloud: Interview Questions</title>
      <link>https://lv-blog.pages.dev/en/posts/programming/for-interview/spring-cloud-interview-questions/</link>
      <pubDate>Sun, 23 Feb 2025 13:20:18 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/en/posts/programming/for-interview/spring-cloud-interview-questions/</guid>
      <description>&lt;h2 id=&#34;the-openfeign-service-call-process&#34;&gt;The OpenFeign service-call process&lt;/h2&gt;
&lt;pre class=&#34;mermaid&#34;&gt;flowchart TD

A[&#34;Business code calls&lt;br/&gt;userClient.getById(1L)&#34;] --&gt; B[&#34;JDK dynamic proxy Proxy.invoke&#34;]

B --&gt; C[&#34;Feign InvocationHandler&lt;br/&gt;MethodHandler&#34;]

C --&gt; D[&#34;Contract parses annotations&lt;br/&gt;@GetMapping / @PathVariable&#34;]

D --&gt; E[&#34;RequestTemplate builds the HTTP request&#34;]

E --&gt; F[&#34;Parameter binding&lt;br/&gt;Path / Query / Body / Header&#34;]

F --&gt; G[&#34;Service name resolution: user-service&#34;]

G --&gt; H[&#34;Service discovery: Nacos / Eureka&#34;]

H --&gt; I[&#34;LoadBalancer load balancing&#34;]

I --&gt; J[&#34;Select instance IP:PORT&#34;]

J --&gt; K[&#34;HTTP Client executor&lt;br/&gt;OkHttp / Apache / JDK&#34;]

K --&gt; L[&#34;Send HTTP request GET /user/1&#34;]

L --&gt; M[&#34;Remote service: user-service&#34;]

M --&gt; N[&#34;Controller handles the request&#34;]

N --&gt; O[&#34;Return JSON&#34;]

O --&gt; P[&#34;Feign Decoder deserializes&#34;]

P --&gt; Q[&#34;JSON → UserDTO&#34;]

Q --&gt; R[&#34;Return to business code&#34;]
&lt;/pre&gt;</description>
    </item>
    <item>
      <title>Why Did China Big Tech Companies Pull All Their Public Docker Mirror Sources?</title>
      <link>https://lv-blog.pages.dev/en/posts/programming/why-why-why/why-rare-docker-images/</link>
      <pubDate>Mon, 23 Dec 2024 08:16:01 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/en/posts/programming/why-why-why/why-rare-docker-images/</guid>
      <description>&lt;p&gt;Over 2024 and 2025, the public Docker mirror sites run by Alibaba Cloud, Tencent Cloud, NetEase, iFlytek, and various universities were shut down or throttled in wave after wave. By 2026, &amp;ldquo;pulling an image feels like surviving a tribulation&amp;rdquo; is no longer a joke.&lt;/p&gt;
&lt;p&gt;Many people&amp;rsquo;s first reaction is to curse the big companies for being stingy. But honestly, it&amp;rsquo;s not that simple.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;the-mountain-of-compliance-cant-be-gone-around&#34;&gt;The mountain of compliance can&amp;rsquo;t be gone around&lt;/h3&gt;
&lt;p&gt;Docker Hub is a globally open community — anyone can push an image to it, and no one does content review at the entrance. That means it contains everything: images with vulnerabilities, images carrying malware, even content that crosses domestic regulatory red lines, all mixed in.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Learning the Cloudflare Business</title>
      <link>https://lv-blog.pages.dev/en/posts/programming/backend/cloudflare/</link>
      <pubDate>Sat, 23 Nov 2024 00:37:30 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/en/posts/programming/backend/cloudflare/</guid>
      <description></description>
    </item>
    <item>
      <title>Spring: Interview Questions</title>
      <link>https://lv-blog.pages.dev/en/posts/programming/for-interview/spring-interview-questions/</link>
      <pubDate>Sat, 19 Oct 2024 23:19:24 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/en/posts/programming/for-interview/spring-interview-questions/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;These interview questions were collected from every corner of the internet&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;talk-about-the-underlying-implementation-of-spring-ioc&#34;&gt;Talk about the underlying implementation of Spring IOC&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Reflection&lt;/li&gt;
&lt;li&gt;The value of the factory&lt;/li&gt;
&lt;li&gt;Design patterns&lt;/li&gt;
&lt;li&gt;A few key methods
&lt;ul&gt;
&lt;li&gt;createBeanFactory&lt;/li&gt;
&lt;li&gt;getBean&lt;/li&gt;
&lt;li&gt;doGetBean&lt;/li&gt;
&lt;li&gt;createBean&lt;/li&gt;
&lt;li&gt;doCreateBean&lt;/li&gt;
&lt;li&gt;createBeanInstance(getDeclaredConstructor, newInstance)&lt;/li&gt;
&lt;li&gt;populateBean&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&#34;mermaid&#34;&gt;flowchart TD
    A[Start] --&gt; B[&#34;createBeanFactory()&lt;br/&gt;create DefaultListableBeanFactory&#34;]
    B --&gt; C[Load BeanDefinitions&lt;br/&gt;parse XML/annotations]
    C --&gt; D[&#34;getBean(beanName)&lt;br/&gt;external call entry&#34;]

    D --&gt; E[&#34;doGetBean(beanName)&#34;]
    E --&gt; F{Get singleton from cache?}
    F --&gt;|Yes| G[Return the cached Bean]
    F --&gt;|No| H[&#34;createBean(beanName, mbd)&#34;]

    H --&gt; I[&#34;doCreateBean(beanName, mbd)&#34;]
    I --&gt; J[&#34;createBeanInstance()&lt;br/&gt;getDeclaredConstructor + newInstance&#34;]
    J --&gt; K[&#34;populateBean()&lt;br/&gt;property filling / dependency injection&#34;]
    K --&gt; L[&#34;initializeBean()&lt;br/&gt;initialization callbacks&#34;]
    L --&gt; M[Register destroy methods]
    M --&gt; N[Return the complete Bean instance]
&lt;/pre&gt;
&lt;h2 id=&#34;talk-about-your-understanding-of-spring-ioc--its-principle-and-implementation&#34;&gt;Talk about your understanding of Spring IOC — its principle and implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The IOC idea&lt;/li&gt;
&lt;li&gt;DI as the means of implementation&lt;/li&gt;
&lt;li&gt;What is a container, what is a Bean&lt;/li&gt;
&lt;li&gt;BeanDefinition
&lt;ul&gt;
&lt;li&gt;Where it&amp;rsquo;s read from
&lt;ul&gt;
&lt;li&gt;XML&lt;/li&gt;
&lt;li&gt;Annotations&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Where it&amp;rsquo;s stored
&lt;ul&gt;
&lt;li&gt;All BeanDefinitions are stored in a Map inside DefaultListableBeanFactory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;the-container-lifecycle&#34;&gt;The container lifecycle&lt;/h2&gt;
&lt;pre class=&#34;mermaid&#34;&gt;flowchart TD
    START([🚀 Program starts]) --&gt; REFRESH[&#34;AbstractApplicationContext.refresh()&#34;]

    REFRESH --&gt; PHASE1

    subgraph PHASE1[&#34;🔵 Phase 1: Preparation&#34;]
        direction TB
        A1[&#34;prepareRefresh()\nrecord start time\nset container state to &#39;active&#39;\ninitialize environment variables&#34;] --&gt; A2
        A2[&#34;obtainFreshBeanFactory()\ncreate DefaultListableBeanFactory\nthe container&#39;s core repository&#34;] --&gt; A3
        A3[&#34;prepareBeanFactory()\nregister basic BeanPostProcessors\nregister Aware-related processors\nregister the default environment Bean&#34;]
    end

    PHASE1 --&gt; PHASE2

    subgraph PHASE2[&#34;🟢 Phase 2: Load BeanDefinitions (read the blueprints)&#34;]
        direction TB
        B1[&#34;scan the packages specified by @ComponentScan\nor read the XML config file&#34;] --&gt; B2
        B2[&#34;parse annotations / XML\nrecognize @Component @Service\n@Repository @Controller @Bean&#34;] --&gt; B3
        B3[&#34;generate a BeanDefinition for each Bean\nrecord: class name, scope, lazy or not\ninit method, destroy method, dependencies&#34;] --&gt; B4
        B4[(&#34;store into beanDefinitionMap\nkey = beanName\nvalue = BeanDefinition&#34;)]
    end

    PHASE2 --&gt; PHASE3

    subgraph PHASE3[&#34;🟡 Phase 3: Modify BeanDefinitions (edit the blueprints)&#34;]
        direction TB
        C1[&#34;invokeBeanFactoryPostProcessors()\nrun all BeanFactoryPostProcessors&#34;] --&gt; C2
        C2[&#34;ConfigurationClassPostProcessor\nhandle @Configuration @Import\n@PropertySource @ComponentScan\n👉 Spring Boot auto-configuration triggers here&#34;] --&gt; C3
        C3[&#34;PropertySourcesPlaceholderConfigurer\nreplace all ${xxx} placeholders in\nBeanDefinitions with real values&#34;] --&gt; C4
        C4[/&#34;all BeanDefinitions finalized\nthe blueprints no longer change&#34;/]
    end

    PHASE3 --&gt; PHASE4

    subgraph PHASE4[&#34;🌸 Phase 4: Register BeanPostProcessors (workers take their posts)&#34;]
        direction TB
        D1[&#34;registerBeanPostProcessors()\nregister by priority\nPriorityOrdered → Ordered → ordinary&#34;] --&gt; D2
        D2[&#34;AutowiredAnnotationBeanPostProcessor\nhandles @Autowired @Value&#34;] --&gt; D3
        D3[&#34;CommonAnnotationBeanPostProcessor\nhandles @PostConstruct @PreDestroy @Resource&#34;] --&gt; D4
        D4[&#34;AnnotationAwareAspectJAutoProxyCreator\ndetects aspects, generates AOP proxies&#34;] --&gt; D5
        D5[/&#34;all workers in place\nwaiting to intervene during Bean creation\nnot working yet&#34;/]
    end

    PHASE4 --&gt; PHASE5

    subgraph PHASE5[&#34;🟣 Phase 5: Initialize container infrastructure&#34;]
        direction TB
        E1[&#34;initMessageSource()\ninitialize i18n resources&#34;] --&gt; E2
        E2[&#34;initApplicationEventMulticaster()\ninitialize the event multicaster&#34;] --&gt; E3
        E3[&#34;onRefresh()\n⭐ Spring Boot starts here\nTomcat / Jetty / Undertow\nthe web container begins listening on a port&#34;] --&gt; E4
        E4[&#34;registerListeners()\nregister all ApplicationListeners\nlisten for container events&#34;]
    end

    PHASE5 --&gt; PHASE6

    subgraph PHASE6[&#34;🟠 Phase 6: Instantiate all singleton Beans&#34;]
        direction TB
        F1[&#34;finishBeanFactoryInitialization()\npreInstantiateSingletons()\niterate all non-lazy singleton BeanDefinitions&#34;] --&gt; F2
        F2[&#34;execute getBean() one by one\ntrigger each Bean&#39;s lifecycle\ninstantiate → fill properties → Aware callbacks\n→ pre-processing → init method → post-processing&#34;] --&gt; F3
        F3[(&#34;all singleton Beans stored in\nthe singletonObjects level-1 cache\n✅ all ready&#34;)]
    end

    PHASE6 --&gt; PHASE7

    subgraph PHASE7[&#34;✅ Phase 7: Container ready&#34;]
        direction TB
        G1[&#34;finishRefresh()\nclean up resources used at startup\ninitialize the lifecycle processor&#34;] --&gt; G2
        G2[&#34;publish ContextRefreshedEvent\nnotify all listeners: container startup complete&#34;] --&gt; G3
        G3[/&#34;container enters running state\ncan handle business requests&#34;/]
    end

    G3 --&gt; RUNNING([🎉 Container running normally])

    RUNNING -.-&gt;|&#34;receive shutdown signal\nCtrl+C / kill / close()&#34;| PHASE8

    subgraph PHASE8[&#34;🔴 Phase 8: Container destruction&#34;]
        direction TB
        H1[&#34;publish ContextClosedEvent\nnotify all listeners: container about to close&#34;] --&gt; H2
        H2[&#34;stop the web container\nTomcat stops accepting new requests&#34;] --&gt; H3
        H3[&#34;run all Beans&#39; destroy logic\n@PreDestroy → destroy() → destroy-method\ndestroy in reverse registration order&#34;] --&gt; H4
        H4[&#34;clear all caches\nsingletonObjects cleared\nbeanDefinitionMap cleared&#34;] --&gt; H5
        H5[&#34;set container state to &#39;closed&#39;\nactive = false / closed = true&#34;]
    end

    H5 --&gt; END([💀 Container destruction complete])

    style START fill:#43A047,color:#fff
    style RUNNING fill:#43A047,color:#fff
    style END fill:#e53935,color:#fff
    style REFRESH fill:#1E88E5,color:#fff
&lt;/pre&gt;
&lt;h2 id=&#34;the-bean-lifecycle&#34;&gt;The Bean lifecycle&lt;/h2&gt;
&lt;pre class=&#34;mermaid&#34;&gt;flowchart TD
    START([🌱 Start creating the Bean]) --&gt; A

    A[&#34;① Instantiation\nInstantiation\nreflectively call the constructor\nnew an empty shell object\nall fields are null at this point&#34;] --&gt; B

    B[&#34;② Put into the level-3 cache\nsingletonFactories\nexpose the half-built object early\nto prepare for circular dependencies&#34;] --&gt; C

    C[&#34;③ Property filling\npopulateBean()\nhandle @Autowired @Value\ninject the dependent Beans&#34;] --&gt; D

    subgraph AWARE[&#34;④ Aware callbacks&#34;]
        direction TB
        D[&#34;BeanNameAware\ntell the Bean its own name&#34;] --&gt; E
        E[&#34;BeanFactoryAware\npass the BeanFactory to the Bean&#34;] --&gt; F
        F[&#34;ApplicationContextAware\npass the ApplicationContext to the Bean&#34;]
    end

    subgraph INIT[&#34;⑤ Initialization initializeBean()&#34;]
        direction TB
        G[&#34;pre-processing\npostProcessBeforeInitialization()\nall BeanPostProcessors run one by one\n📌 @PostConstruct is called here&#34;] --&gt; INITMETHOD

        subgraph INITMETHOD[&#34;init methods (executed in order)&#34;]
            direction TB
            H1[&#34;1st: @PostConstruct\n(already run in pre-processing)&#34;] --&gt; H2
            H2[&#34;2nd: afterPropertiesSet()\nimplement the InitializingBean interface&#34;] --&gt; H3
            H3[&#34;3rd: init-method\n@Bean(initMethod=&#39;xxx&#39;) or XML config&#34;]
        end

        INITMETHOD --&gt; I

        I[&#34;post-processing\npostProcessAfterInitialization()\nall BeanPostProcessors run one by one\n⭐ AOP proxies are generated here&#34;]
    end

    AWARE --&gt; INIT

    I --&gt; J

    subgraph CACHE[&#34;⑥ Store into the cache&#34;]
        direction TB
        J[&#34;remove from the level-3 cache singletonFactories\nremove from the level-2 cache earlySingletonObjects&#34;] --&gt; K
        K[&#34;store into the level-1 cache singletonObjects\n✅ complete Bean ready&#34;]
    end

    K --&gt; READY([🎉 Bean is now usable])

    READY -.-&gt;|&#34;container closes&#34;| DESTROY

    subgraph DESTROY[&#34;⑦ Destruction phase (on container close)&#34;]
        direction TB
        D1[&#34;1st: @PreDestroy\nmethod is called&#34;] --&gt; D2
        D2[&#34;2nd: destroy()\nimplement the DisposableBean interface&#34;] --&gt; D3
        D3[&#34;3rd: destroy-method\n@Bean(destroyMethod=&#39;xxx&#39;) or XML config&#34;]
    end

    D3 --&gt; END([💀 Bean destruction complete])
&lt;/pre&gt;
&lt;h2 id=&#34;springs-three-level-cache-dependency-flow&#34;&gt;Spring&amp;rsquo;s three-level cache dependency flow&lt;/h2&gt;
&lt;pre class=&#34;mermaid&#34;&gt;sequenceDiagram
    participant Spring as Spring container
    participant L3 as Level-3 cache&lt;br/&gt;singletonFactories
    participant L2 as Level-2 cache&lt;br/&gt;earlySingletonObjects
    participant L1 as Level-1 cache&lt;br/&gt;singletonObjects

    Note over Spring: Start creating A

    Spring-&gt;&gt;Spring: ① new A empty-shell object
    Spring-&gt;&gt;L3: ② store A&#39;s ObjectFactory lambda

    Note over Spring: Injecting properties into A, discovers it needs B

    Spring-&gt;&gt;Spring: ③ new B empty-shell object
    Spring-&gt;&gt;L3: ④ store B&#39;s ObjectFactory lambda

    Note over Spring: Injecting properties into B, discovers it needs A

    Spring-&gt;&gt;L1: ⑤ getBean(A), check level-1 cache
    L1--&gt;&gt;Spring: ❌ not there

    Spring-&gt;&gt;L2: check level-2 cache
    L2--&gt;&gt;Spring: ❌ not there

    Spring-&gt;&gt;L3: check level-3 cache
    L3--&gt;&gt;Spring: ✅ found A&#39;s lambda!

    Spring-&gt;&gt;Spring: ⑥ execute the lambda&lt;br/&gt;(generate proxy A if a proxy is needed)
    Spring-&gt;&gt;L2: ⑦ put the early reference into the level-2 cache
    Spring-&gt;&gt;L3: remove A from the level-3 cache

    Note over Spring: B gets A&#39;s early reference, B finishes initialization

    Spring-&gt;&gt;L1: ⑧ put B into the level-1 cache ✅
    Spring-&gt;&gt;L3: remove B from the level-3 cache

    Note over Spring: Back in A&#39;s flow, B is ready, A finishes initialization

    Spring-&gt;&gt;L1: ⑨ put A into the level-1 cache ✅
    Spring-&gt;&gt;L2: remove A from the level-2 cache

    Note over L1: Finally: the level-1 cache holds complete A and B ✅
&lt;/pre&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Case&lt;/th&gt;
					&lt;th&gt;Level-3 cache&lt;/th&gt;
					&lt;th&gt;lambda executed&lt;/th&gt;
					&lt;th&gt;Level-2 cache&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;No circular dep, no AOP&lt;/td&gt;
					&lt;td&gt;stored the lambda&lt;/td&gt;
					&lt;td&gt;❌ not executed&lt;/td&gt;
					&lt;td&gt;not used&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;No circular dep, with AOP&lt;/td&gt;
					&lt;td&gt;stored the lambda&lt;/td&gt;
					&lt;td&gt;❌ not executed&lt;/td&gt;
					&lt;td&gt;not used&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Circular dep, no AOP&lt;/td&gt;
					&lt;td&gt;stored the lambda&lt;/td&gt;
					&lt;td&gt;✅ executed&lt;/td&gt;
					&lt;td&gt;stores the raw object&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Circular dep, with AOP&lt;/td&gt;
					&lt;td&gt;stored the lambda&lt;/td&gt;
					&lt;td&gt;✅ executed&lt;/td&gt;
					&lt;td&gt;stores the proxy object&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;when-spring-bean-caches-are-placed-and-removed&#34;&gt;When Spring Bean caches are placed and removed&lt;/h2&gt;
&lt;h2 id=&#34;when-spring-beans-three-level-caches-are-placed-and-removed&#34;&gt;When Spring Bean&amp;rsquo;s three-level caches are placed and removed&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Cache&lt;/th&gt;
					&lt;th&gt;What it stores&lt;/th&gt;
					&lt;th&gt;When placed&lt;/th&gt;
					&lt;th&gt;When removed&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Level-3 &lt;code&gt;singletonFactories&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The Bean&amp;rsquo;s factory lambda&lt;/td&gt;
					&lt;td&gt;Right after instantiation&lt;/td&gt;
					&lt;td&gt;When the factory is called (promoted to level-2) or when the Bean completes&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Level-2 &lt;code&gt;earlySingletonObjects&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The early-exposed half-built Bean&lt;/td&gt;
					&lt;td&gt;The moment the level-3 factory is called&lt;/td&gt;
					&lt;td&gt;When the Bean is fully initialized and placed in level-1&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Level-1 &lt;code&gt;singletonObjects&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The complete, usable Bean&lt;/td&gt;
					&lt;td&gt;After initialization fully completes&lt;/td&gt;
					&lt;td&gt;When the container closes and destroys&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;the-difference-between-beanfactory-and-factorybean&#34;&gt;The difference between BeanFactory and FactoryBean&lt;/h2&gt;
&lt;h3 id=&#34;what-is-a-factorybean&#34;&gt;What is a FactoryBean&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;FactoryBean&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;T&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// Return the Bean instance (can involve complex creation logic)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    T &lt;span style=&#34;color:#a6e22e&#34;&gt;getObject&lt;/span&gt;() &lt;span style=&#34;color:#66d9ef&#34;&gt;throws&lt;/span&gt; Exception;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// Return the Bean&amp;#39;s type&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Class&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;?&amp;gt;&lt;/span&gt; getObjectType();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// Whether it&amp;#39;s a singleton&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;boolean&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;isSingleton&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Similarities
&lt;ul&gt;
&lt;li&gt;Both are used to create Bean objects&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Differences
&lt;ul&gt;
&lt;li&gt;Creating an object with BeanFactory must follow the strict lifecycle process, which is too complex. If you want to simply customize the creation of some object while still handing it to Spring to manage, you must implement the FactoryBean interface
&lt;ul&gt;
&lt;li&gt;isSingleton — whether it&amp;rsquo;s a singleton&lt;/li&gt;
&lt;li&gt;getObjectType — get the returned object&amp;rsquo;s type&lt;/li&gt;
&lt;li&gt;getObject — customize the object-creation process&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Dimension&lt;/th&gt;
					&lt;th&gt;BeanFactory&lt;/th&gt;
					&lt;th&gt;FactoryBean&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Role&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Container/factory interface&lt;/td&gt;
					&lt;td&gt;A special Bean&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Function&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Manages the lifecycle of all Beans&lt;/td&gt;
					&lt;td&gt;Customizes the creation of a specific Bean&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Positioning&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Infrastructure (the IOC container)&lt;/td&gt;
					&lt;td&gt;Business extension (creating complex objects)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;How it&amp;rsquo;s used&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Implemented and used by the Spring framework&lt;/td&gt;
					&lt;td&gt;Implemented by the developer, registered into the container&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Common implementations&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;DefaultListableBeanFactory&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;SqlSessionFactoryBean&lt;/code&gt;, &lt;code&gt;ProxyFactoryBean&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Who creates whom&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;BeanFactory&lt;/code&gt; creates and manages &lt;code&gt;FactoryBean&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;FactoryBean&lt;/code&gt; creates business objects&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;pre class=&#34;mermaid&#34;&gt;flowchart LR
    subgraph BeanFactory[BeanFactory - IOC container]
        direction LR
        Bean1[ordinary Bean]
        Bean2[ordinary Bean]
        FB[FactoryBean&lt;br/&gt;special Bean]
    end

    FB --&gt;|call getObject| Result[business object]
    
    User[developer] --&gt;|getBean| BF[BeanFactory]
    BF --&gt;|return| Bean1
    BF --&gt;|return| Bean2
    BF --&gt;|return getObject result| Result
    BF --&gt;|add &amp; prefix| FB
&lt;/pre&gt;
&lt;h2 id=&#34;design-patterns-used-in-spring&#34;&gt;Design patterns used in Spring&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Singleton&lt;/li&gt;
&lt;li&gt;Prototype (scope set to prototype)&lt;/li&gt;
&lt;li&gt;Factory
&lt;ul&gt;
&lt;li&gt;BeanFactory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Template Method
&lt;ul&gt;
&lt;li&gt;JdbcTemplate&lt;/li&gt;
&lt;li&gt;TransactionTemplate&lt;/li&gt;
&lt;li&gt;RestTemplate&lt;/li&gt;
&lt;li&gt;RedisTemplate&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Strategy
&lt;ul&gt;
&lt;li&gt;XmlBeanDefinitionReader&lt;/li&gt;
&lt;li&gt;PropertiesBeanDefinitionReader&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Observer
&lt;ul&gt;
&lt;li&gt;listener&lt;/li&gt;
&lt;li&gt;event&lt;/li&gt;
&lt;li&gt;multicast&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Adapter
&lt;ul&gt;
&lt;li&gt;HandlerAdapter&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Decorator
&lt;ul&gt;
&lt;li&gt;BeanWrapper&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Chain of Responsibility
&lt;ul&gt;
&lt;li&gt;When using AOP, an interceptor chain is generated first&lt;/li&gt;
&lt;li&gt;The filter chain of Spring MVC&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Proxy
&lt;ul&gt;
&lt;li&gt;Dynamic proxy&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Delegate
&lt;ul&gt;
&lt;li&gt;delegate&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;the-underlying-implementation-principle-of-spring-aop&#34;&gt;The underlying implementation principle of Spring AOP&lt;/h2&gt;
&lt;h3 id=&#34;-level-1-characterize-it-in-one-sentence-opening&#34;&gt;🥇 Level 1: characterize it in one sentence (opening)&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;The underlying essence of Spring AOP is &lt;strong&gt;dynamic proxy&lt;/strong&gt;. When the container initializes a Bean, Spring intercepts via the &lt;code&gt;BeanPostProcessor&lt;/code&gt; mechanism, decides whether the Bean needs enhancement, and if so, uses a dynamic proxy to generate a proxy object that replaces the original Bean when registered into the container. All subsequent calls to this Bean actually go through the proxy object.&amp;rdquo;&lt;/p&gt;</description>
    </item>
    <item>
      <title>MySQL: Interview Questions</title>
      <link>https://lv-blog.pages.dev/en/posts/programming/for-interview/mysql-interview-questions/</link>
      <pubDate>Thu, 19 Sep 2024 23:19:24 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/en/posts/programming/for-interview/mysql-interview-questions/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;These interview questions were collected from every corner of the internet&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;how-do-you-locate-a-slow-query&#34;&gt;How do you locate a slow query?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Approach 1
&lt;ul&gt;
&lt;li&gt;Open-source tools
&lt;ul&gt;
&lt;li&gt;Arthas&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Ops tools
&lt;ul&gt;
&lt;li&gt;Prometheus&lt;/li&gt;
&lt;li&gt;Skywalking&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Approach 2
&lt;ul&gt;
&lt;li&gt;MySQL&amp;rsquo;s built-in slow log (has a performance cost)
&lt;ul&gt;
&lt;li&gt;How to enable the slow log
&lt;ul&gt;
&lt;li&gt;/etc/my.conf
&lt;ul&gt;
&lt;li&gt;slow_query_log&lt;/li&gt;
&lt;li&gt;long_query_time&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;a-sql-statement-runs-slowly--how-do-you-analyze-it&#34;&gt;A SQL statement runs slowly — how do you analyze it?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Reasons for slowness
&lt;ul&gt;
&lt;li&gt;Aggregate queries&lt;/li&gt;
&lt;li&gt;Multi-table queries&lt;/li&gt;
&lt;li&gt;Queries on tables with too much data&lt;/li&gt;
&lt;li&gt;Deep pagination queries&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;How to analyze the slowness
&lt;ul&gt;
&lt;li&gt;The explain / desc command
&lt;ul&gt;
&lt;li&gt;How to read the result?
&lt;ul&gt;
&lt;li&gt;The extra optimization hints
&lt;ul&gt;
&lt;li&gt;using where; using index — the lookup used an index, and the needed data is all in the index columns, so no back-to-table lookup is needed&lt;/li&gt;
&lt;li&gt;using index condition — the lookup used an index&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;type
&lt;ul&gt;
&lt;li&gt;index, all — need optimization&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;do-you-know-about-indexes-what-is-an-index&#34;&gt;Do you know about indexes? What is an index?&lt;/h2&gt;
&lt;p&gt;An index is a data structure (ordered) that helps MySQL fetch data efficiently. Beyond the data itself, the database system maintains data structures that satisfy specific search algorithms (such as B+ trees); these structures reference (point to) the data in some way, so advanced search algorithms can be implemented on them. Such a data structure is an index.&lt;/p&gt;</description>
    </item>
    <item>
      <title>How Powerful Is JeecgBoot? Here Is My Take</title>
      <link>https://lv-blog.pages.dev/en/posts/programming/jeecg-boot/</link>
      <pubDate>Mon, 19 Aug 2024 21:33:09 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/en/posts/programming/jeecg-boot/</guid>
      <description></description>
    </item>
    <item>
      <title>A Close Study of Redis&#39;s LFU Memory Eviction Policy</title>
      <link>https://lv-blog.pages.dev/en/posts/programming/redis/redis-memory-eviction-policy-lfu/</link>
      <pubDate>Fri, 19 Jul 2024 13:10:54 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/en/posts/programming/redis/redis-memory-eviction-policy-lfu/</guid>
      <description>&lt;p&gt;Redis&amp;rsquo;s LFU (Least Frequently Used) eviction policy is an &amp;ldquo;upgraded&amp;rdquo; approximate algorithm built on top of LRU. It reuses the same 24-bit &lt;code&gt;lru&lt;/code&gt; field in the object header and, through clever encoding and logarithmic counting, achieves an approximate tracking of access frequency at a tiny memory cost.&lt;/p&gt;
&lt;p&gt;Below we examine it point by point: from the storage structure, to counter increment/decrement, to the eviction decision, to parameter configuration.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;1-the-bit-layout-of-the-24-bit-field&#34;&gt;1. The bit layout of the 24-bit field&lt;/h3&gt;
&lt;p&gt;Every Redis object has an &lt;code&gt;lru&lt;/code&gt; attribute (24 bits). In LFU mode it no longer holds a second-level timestamp but is split into two parts:&lt;/p&gt;</description>
    </item>
    <item>
      <title>Studying for the System Architect Software Exam</title>
      <link>https://lv-blog.pages.dev/en/posts/programming/ruan-kao/</link>
      <pubDate>Tue, 18 Jun 2024 12:51:35 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/en/posts/programming/ruan-kao/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;These notes are organized around the core knowledge points of the exam syllabus, with code examples and memory mnemonics — good for a final sprint review. (The exam is China&amp;rsquo;s &amp;ldquo;Soft Exam&amp;rdquo; — Computer Technology and Software Professional Qualification — System Architect level.)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;1-laws-regulations-and-standardization&#34;&gt;1. Laws, Regulations, and Standardization&lt;/h2&gt;
&lt;h3 id=&#34;11-copyright&#34;&gt;1.1 Copyright&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Category&lt;/th&gt;
					&lt;th&gt;Content&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Not protected&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Government documents, laws and regulations, current-affairs news&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Protected&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Speeches, authored books, software code&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;When it vests&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Automatically arises from the day software development is completed&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Protection term&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;50 years (attribution, modification, and integrity rights are protected permanently)&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Copyright ownership rules:&lt;/strong&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Learning ElasticSearch</title>
      <link>https://lv-blog.pages.dev/en/posts/programming/backend/elasticsearch/</link>
      <pubDate>Sat, 18 May 2024 12:51:05 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/en/posts/programming/backend/elasticsearch/</guid>
      <description>&lt;h2 id=&#34;1-fundamentals&#34;&gt;1. Fundamentals&lt;/h2&gt;
&lt;h3 id=&#34;11-origin-lucene&#34;&gt;1.1 Origin: Lucene&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Lucene&lt;/strong&gt;: Apache&amp;rsquo;s open-source Java full-text search engine library.&lt;/li&gt;
&lt;li&gt;Advantages: easy to extend, high performance (pure Java, embeddable).&lt;/li&gt;
&lt;li&gt;Drawbacks: complex to use — you must handle low-level details like index creation and query parsing, with no distributed support.&lt;/li&gt;
&lt;li&gt;Elasticsearch is built on Lucene, providing a distributed, easy-to-use RESTful API.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;12-the-tech-stack-elk&#34;&gt;1.2 The tech stack (ELK)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Elasticsearch&lt;/strong&gt;: the storage, search, and analytics engine.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Logstash&lt;/strong&gt;: a server-side data processing pipeline that collects and transforms data before sending it to ES.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kibana&lt;/strong&gt;: a visualization platform for building charts and dashboards and managing ES.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Beats&lt;/strong&gt;: lightweight data shippers that send to Logstash or ES.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;13-installing-with-docker&#34;&gt;1.3 Installing with Docker&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Elasticsearch&lt;/strong&gt; (single node):&lt;/p&gt;</description>
    </item>
    <item>
      <title>Learning AWS</title>
      <link>https://lv-blog.pages.dev/en/posts/programming/backend/aws/</link>
      <pubDate>Thu, 18 Apr 2024 12:48:03 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/en/posts/programming/backend/aws/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Compiled for the Solutions Architect certification, from a Java backend perspective. Core idea: &lt;strong&gt;what pain point each service solves, when to choose it, and how to distinguish it from similar services.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;1-storage&#34;&gt;1. Storage&lt;/h2&gt;
&lt;h3 id=&#34;11-comparison-of-the-three-storage-types&#34;&gt;1.1 Comparison of the three storage types&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Type&lt;/th&gt;
					&lt;th&gt;Representative service&lt;/th&gt;
					&lt;th&gt;Access unit&lt;/th&gt;
					&lt;th&gt;Typical scenario&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Block storage&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;EBS, EC2 Instance Store&lt;/td&gt;
					&lt;td&gt;Block&lt;/td&gt;
					&lt;td&gt;Databases, OS disks&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;File storage&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;EFS, FSx&lt;/td&gt;
					&lt;td&gt;Files / directory tree&lt;/td&gt;
					&lt;td&gt;Multi-instance sharing, NFS/SMB mounts&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Object storage&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;S3&lt;/td&gt;
					&lt;td&gt;Object (Object + Key)&lt;/td&gt;
					&lt;td&gt;Static assets, backups, data lakes&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Block storage  → like a local hard drive; the OS sees a raw device it formats and mounts itself
File storage   → like a NAS; multiple machines can mount the same directory at once
Object storage → like HTTP PUT/GET key-value; no directory concept, simulated with prefixes
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h3 id=&#34;12-amazon-s3&#34;&gt;1.2 Amazon S3&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Quick reference of core features:&lt;/strong&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Learning Spring Cloud</title>
      <link>https://lv-blog.pages.dev/en/posts/programming/backend/spring-cloud/</link>
      <pubDate>Mon, 18 Mar 2024 12:46:51 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/en/posts/programming/backend/spring-cloud/</guid>
      <description>&lt;h2 id=&#34;1-service-decomposition-and-remote-calls&#34;&gt;1. Service Decomposition and Remote Calls&lt;/h2&gt;
&lt;h3 id=&#34;11-why-decompose-into-services&#34;&gt;1.1 Why decompose into services?&lt;/h3&gt;
&lt;p&gt;As business grows, a monolith faces slow deployment, poor scalability, a frozen tech stack, and more. Microservices split it into small, &lt;strong&gt;independently deployable and scalable&lt;/strong&gt; services, each responsible for a single business domain.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Decomposition principles:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Single responsibility: each service does one thing&lt;/li&gt;
&lt;li&gt;High cohesion, low coupling: tight within a service, loose between services&lt;/li&gt;
&lt;li&gt;Data independence: each service owns an independent database&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;12-the-user-login-flow-from-a-microservice-view&#34;&gt;1.2 The user-login flow (from a microservice view)&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Client → Gateway (auth) → business microservice A → microservice B (OpenFeign)
                ↓
           Nacos (service discovery)
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;13-resttemplate--the-raw-inter-service-call&#34;&gt;1.3 RestTemplate — the raw inter-service call&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;RestTemplate&lt;/code&gt; is the HTTP client Spring provides for inter-service calls, but its &lt;strong&gt;code is verbose and it doesn&amp;rsquo;t support load balancing&lt;/strong&gt; — a transitional solution before OpenFeign.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Learning the SSM Framework</title>
      <link>https://lv-blog.pages.dev/en/posts/programming/backend/ssm/</link>
      <pubDate>Sun, 18 Feb 2024 12:40:44 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/en/posts/programming/backend/ssm/</guid>
      <description>&lt;h2 id=&#34;1-the-spring-ioc-container&#34;&gt;1. The Spring IoC Container&lt;/h2&gt;
&lt;h3 id=&#34;11-container-architecture&#34;&gt;1.1 Container architecture&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;BeanFactory (top-level interface)
    └── ApplicationContext (the commonly used interface, extends BF)
            ├── ClassPathXmlApplicationContext (XML config)
            ├── FileSystemXmlApplicationContext (filesystem-path XML)
            └── AnnotationConfigApplicationContext (annotation config)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Core differences between BeanFactory and ApplicationContext:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Feature&lt;/th&gt;
					&lt;th&gt;BeanFactory&lt;/th&gt;
					&lt;th&gt;ApplicationContext&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Bean init timing&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;Lazy&lt;/strong&gt; (on the first getBean)&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;Eager&lt;/strong&gt; (at container startup)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Features&lt;/td&gt;
					&lt;td&gt;Basic IoC&lt;/td&gt;
					&lt;td&gt;IoC + event publishing + i18n + AOP, etc.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Use case&lt;/td&gt;
					&lt;td&gt;Extremely resource-constrained embedded&lt;/td&gt;
					&lt;td&gt;99% of business scenarios&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why doesn&amp;rsquo;t ApplicationContext have close()?&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;ApplicationContext&lt;/code&gt; interface itself doesn&amp;rsquo;t define &lt;code&gt;close()&lt;/code&gt;, to keep the interface general (not all containers can/need to be closed, e.g. web containers). But its implementation &lt;code&gt;AbstractApplicationContext&lt;/code&gt; implements &lt;code&gt;Closeable&lt;/code&gt;, so you can cast and call it, or receive it via the &lt;code&gt;ConfigurableApplicationContext&lt;/code&gt; interface.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Learning MyBatis-Plus</title>
      <link>https://lv-blog.pages.dev/en/posts/programming/backend/mybatisplus/</link>
      <pubDate>Tue, 16 Jan 2024 15:38:37 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/en/posts/programming/backend/mybatisplus/</guid>
      <description>&lt;h2 id=&#34;1-introduction&#34;&gt;1. Introduction&lt;/h2&gt;
&lt;p&gt;MyBatis-Plus (MP for short) is an enhancement tool for MyBatis. It only enhances MyBatis without changing it, built to simplify development and improve efficiency.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Core features:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Non-intrusive&lt;/strong&gt;: adding MP has no effect on an existing MyBatis project — smooth as silk.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Low overhead&lt;/strong&gt;: basic CRUD is auto-injected at startup, with essentially no performance loss.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Powerful CRUD operations&lt;/strong&gt;: built-in generic Mapper and generic Service; with just a little configuration you get most single-table CRUD.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Supports Lambda-style calls&lt;/strong&gt;: write query conditions safely and efficiently via lambda expressions, preventing mistyped field names.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Built-in code generator&lt;/strong&gt;: generate Mapper, Service, Controller, etc. with minimal configuration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Built-in pagination plugin&lt;/strong&gt;: physical pagination based on MyBatis; developers don&amp;rsquo;t need to worry about the details — just configure and use.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Official site&lt;/strong&gt;: &lt;a href=&#34;https://baomidou.com/&#34;&gt;https://baomidou.com/&lt;/a&gt;&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
