<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:series="https://publishpress.com/"
	>

<channel>
	<title>Java Archives - Tomoshare</title>
	<atom:link href="https://blog.tomosia.com.vn/danh-muc/java/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.tomosia.com.vn/danh-muc/java/</link>
	<description>Kênh chia sẻ kiến thức Tomosia Việt Nam</description>
	<lastBuildDate>Tue, 28 Apr 2026 09:37:56 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0</generator>

<image>
	<url>https://blog.tomosia.com.vn/wp-content/uploads/2023/09/cropped-icon-32x32.png</url>
	<title>Java Archives - Tomoshare</title>
	<link>https://blog.tomosia.com.vn/danh-muc/java/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>IDOR &#8211; Lỗ hổng &#8220;ngây thơ&#8221; nhưng chí mạng trong kiến trúc phần mềm?</title>
		<link>https://blog.tomosia.com.vn/idor-lo-hong-ngay-tho-nhung-chi-mang-trong-kien-truc-phan-mem/</link>
					<comments>https://blog.tomosia.com.vn/idor-lo-hong-ngay-tho-nhung-chi-mang-trong-kien-truc-phan-mem/#comments</comments>
		
		<dc:creator><![CDATA[Nguyen Luan]]></dc:creator>
		<pubDate>Fri, 30 Jan 2026 09:56:08 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Web Server]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=4177</guid>

					<description><![CDATA[<p>Hãy tưởng tượng bạn check-in vào một khách sạn 5 sao sang trọng. Hệ thống an ninh tối&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/idor-lo-hong-ngay-tho-nhung-chi-mang-trong-kien-truc-phan-mem/">IDOR &#8211; Lỗ hổng &#8220;ngây thơ&#8221; nhưng chí mạng trong kiến trúc phần mềm?</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Hãy tưởng tượng bạn check-in vào một khách sạn 5 sao sang trọng. Hệ thống an ninh tối tân, camera khắp nơi, thẻ từ thang máy hiện đại. Lễ tân đưa cho bạn chìa khóa phòng 101. Bạn lên phòng, mở cửa và nghỉ ngơi. Nhưng sau đó, vì tò mò, bạn thử cạo sửa con số trên chìa khóa thành 102 và cắm vào ổ khóa phòng bên cạnh. <em>Cạch!</em> Cửa mở toang.</p>



<p class="wp-block-paragraph">Trong thế giới thực, điều này thật nực cười. Nhưng trong thế giới lập trình Web và API, tình huống này xảy ra thường xuyên đến mức nó trở thành &#8220;cơn đau đầu&#8221; kinh niên của giới bảo mật. Nó được gọi là <strong>IDOR</strong> (Insecure Direct Object Reference) hay <strong>BOLA</strong> (Broken Object Level Authorization).</p>



<p class="wp-block-paragraph">Nếu bạn là một Backend Developer, và bạn tin rằng chỉ cần có <mark style="background-color:#e9ecef" class="has-inline-color"><code>Authentication</code> </mark>(Đăng nhập) là hệ thống đã an toàn, thì bài viết này sẽ thay đổi tư duy lập trình của bạn. Chúng ta sẽ không chỉ nói về bề nổi, mà sẽ đi sâu vào bản chất kiến trúc của lỗi này.</p>



<h3 id="1-ban-chat-ky-thuat-cua-idor" class="wp-block-heading">1. Bản chất kỹ thuật của IDOR</h3>



<p class="wp-block-paragraph"><strong>IDOR</strong> (Insecure Direct Object Reference) không chỉ là một lỗi code, nó là một <strong>lỗi về thiết kế Logic (Business Logic Flaw)</strong>.</p>



<p class="wp-block-paragraph">Trong danh sách <strong>OWASP API Security Top 10</strong>, IDOR chính là bản chất của lỗ hổng số 1: <strong><a href="https://owasp.org/API-Security/editions/2023/en/0xa1-broken-object-level-authorization/">API1: 2023 Broken Object Level Authorization (BOLA)</a></strong>.</p>



<p class="wp-block-paragraph">Hiểu sâu hơn, IDOR xảy ra khi 3 điều kiện sau hội tụ:</p>



<ol class="wp-block-list">
<li><strong>Direct Reference:</strong> Ứng dụng để lộ mã định danh nội bộ (Internal Identifier) của đối tượng (như Database Primary Key, Filename) trực tiếp ra ngoài URL hoặc Body request.</li>



<li><strong>User Input:</strong> Ứng dụng tin tưởng và sử dụng trực tiếp đầu vào này để truy vấn dữ liệu.</li>



<li><strong>Missing Authorization:</strong> Ứng dụng bỏ qua bước kiểm tra quyền truy cập <em>tại thời điểm</em> truy xuất đối tượng.</li>
</ol>



<p class="wp-block-paragraph"><strong>Sai lầm phổ biến:</strong> Nhiều Dev nhầm lẫn giữa <em>Authentication</em> (Bạn là ai?) và <em>Authorization</em> (Bạn được làm gì?). IDOR xảy ra khi bạn đã qua cửa Authentication, nhưng hệ thống quên kiểm tra Authorization ở mức độ đối tượng (Object-level).</p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="559" src="https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-1024x559.png" alt="" class="wp-image-4187" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-1024x559.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-300x164.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-768x419.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-380x207.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-800x436.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-1160x633.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1.png 1408w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h3 id="2-giai-phau-co-che-tan-cong" class="wp-block-heading">2. Giải phẫu cơ chế tấn công</h3>



<p class="wp-block-paragraph">Hãy mổ xẻ một HTTP Request để thấy IDOR có thể lẩn trốn ở đâu. Không chỉ ở URL Parameter, nó có thể nằm ở Header hoặc Body.</p>



<p class="wp-block-paragraph"><strong>Kịch bản:</strong> Ứng dụng Chat cho phép người dùng tải lại lịch sử chat.</p>



<p class="wp-block-paragraph"><strong>Request bình thường:</strong></p>



<pre class="wp-block-code"><code>GET /api/v1/messages/history HTTP/1.1
Host: example.com
Cookie: session_id=abc123xyz...
X-User-ID: 1005  &lt;-- IDOR tiềm ẩn ở Header</code></pre>



<ol class="wp-block-list">
<li><strong>Hacker quan sát:</strong> Thấy Header <code><mark style="background-color:#e9ecef" class="has-inline-color">X-User-ID: 1005</mark></code> được gửi kèm mỗi request.</li>



<li><strong>Thử nghiệm:</strong> Hacker dùng Postman hoặc Burp Suite để chặn request, sửa <code><mark style="background-color:#e9ecef" class="has-inline-color">X-User-ID</mark></code> thành <code>1006</code>.</li>



<li><strong>Kết quả:</strong> Server, thay vì lấy ID từ session an toàn (Server-side), lại tin tưởng lấy ID từ Header (Client-side) để query database <code><mark style="background-color:#e9ecef" class="has-inline-color">SELECT * FROM messages WHERE user_id = 1006</mark></code>.</li>



<li><strong>Hậu quả:</strong> Hacker đọc được toàn bộ tin nhắn của người dùng 1006.</li>
</ol>



<p class="wp-block-paragraph"><em><strong>IDOR có thể xuất hiện ở:</strong></em></p>



<ul class="wp-block-list">
<li><strong>URL Path:</strong> <code><mark style="background-color:#e9ecef" class="has-inline-color">/users/100/profile</mark></code></li>



<li><strong>Query Params:</strong> <code><mark style="background-color:#e9ecef" class="has-inline-color">/invoices?id=100</mark></code></li>



<li><strong>Body (JSON/XML):</strong> <mark style="background-color:#e9ecef" class="has-inline-color"><code>POST /api/change-password</code> với body <code>{"user_id": 100, "new_pass": "..."}</code></mark></li>
</ul>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="572" src="https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-1024x572.png" alt="" class="wp-image-4190" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-1024x572.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-300x167.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-768x429.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-380x212.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-800x447.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-1160x647.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1.png 1376w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h3 id="3-tai-sao-idor-lai-la-sat-thu-tham-lang" class="wp-block-heading">3. Tại sao IDOR lại là &#8220;Sát thủ thầm lặng&#8221;?</h3>



<p class="wp-block-paragraph">IDOR nguy hiểm vì tính &#8220;hợp lệ&#8221; về mặt cú pháp của nó.</p>



<ul class="wp-block-list">
<li><strong>Vượt mặt WAF (Web Application Firewall):</strong> Các tường lửa thường chặn các ký tự lạ như <code><mark style="background-color:#e9ecef" class="has-inline-color">' OR 1=1 --</mark></code> (SQL Injection) hay <code><mark style="background-color:#e9ecef" class="has-inline-color">&lt;script&gt;</mark></code> (XSS). Nhưng với IDOR, payload chỉ là một con số: <code>id=1006</code>. Đối với WAF, đây là một con số hoàn toàn sạch và hợp lệ.</li>



<li><strong>Khó phát hiện bằng Automation Tool:</strong> Các máy quét (Scanner) không hiểu ngữ cảnh nghiệp vụ. Nếu máy quét gửi <code><mark style="background-color:#e9ecef" class="has-inline-color">id=1006</mark></code> và nhận về <code>200 OK</code>, nó đánh dấu là &#8220;Thành công&#8221;. Nó không biết rằng dữ liệu trả về đó <em>không thuộc về</em> người đang gửi request. Chỉ có con người (Pentesters) hoặc Unit Test được viết kỹ lưỡng mới phát hiện ra.</li>



<li><strong>Hệ quả dây chuyền:</strong> IDOR thường là bước đệm để leo thang đặc quyền (Privilege Escalation), dẫn đến việc chiếm đoạt toàn bộ hệ thống (Account Takeover).</li>
</ul>



<p class="wp-block-paragraph">Sự nguy hiểm của IDOR nằm ở chỗ nó là một lỗ hổng logic, không phải lỗ hổng cú pháp. Các công cụ quét bảo mật tĩnh (SAST) truyền thống thường thất bại trong việc phát hiện IDOR vì chúng không thể hiểu ngữ cảnh &#8220;ai sở hữu cái gì&#8221; trong một ứng dụng cụ thể. Điều này dẫn đến một nghịch lý: trong khi các lỗ hổng kỹ thuật như <strong>SQL Injection</strong> đang giảm dần nhờ các framework hiện đại, IDOR vẫn tồn tại dai dẳng và chiếm tỷ lệ cao trong các chương trình săn lỗi nhận thưởng (Bug Bounty), đặc biệt là trong các lĩnh vực y tế, chính phủ và dịch vụ chuyên nghiệp.</p>



<h3 id="4-phan-loai-idor-chuyen-sau" class="wp-block-heading">4. Phân loại IDOR chuyên sâu.</h3>



<h4 id="4-1-leo-thang-dac-quyen-ngang-horizontal-escalation" class="wp-block-heading">4.1. Leo thang đặc quyền ngang (Horizontal Escalation)</h4>



<p class="wp-block-paragraph">Đây là dạng phổ biến nhất. Người dùng A truy cập dữ liệu của Người dùng B có cùng vai trò.</p>



<ul class="wp-block-list">
<li><strong>Ví dụ:</strong> Xem đơn hàng, tải hóa đơn, xem ảnh riêng tư.</li>



<li><strong>Rủi ro:</strong> Rò rỉ thông tin cá nhân (PII Breach), vi phạm GDPR/CCPA.</li>
</ul>



<h4 id="4-2-leo-thang-dac-quyen-doc-vertical-escalation" class="wp-block-heading">4.2. Leo thang đặc quyền dọc (Vertical Escalation)</h4>



<p class="wp-block-paragraph">Người dùng thường (User) tìm cách gọi các API dành riêng cho Quản trị viên (Admin/Manager).</p>



<ul class="wp-block-list">
<li><strong>Ví dụ:</strong>
<ul class="wp-block-list">
<li>Hacker tìm thấy API: <mark style="background-color:#e9ecef" class="has-inline-color"><code>POST /api/users/1001/roles</code>.</mark></li>



<li>Hacker gửi request với body: <code><mark style="background-color:#e9ecef" class="has-inline-color">{"role": "ADMIN"}</mark></code>.</li>



<li>Nếu Backend không check xem &#8220;người gọi API này có phải là Super Admin không&#8221;, Hacker lập tức trở thành Admin.</li>
</ul>
</li>
</ul>



<h4 id="4-3-idor-tren-file-tinh-static-file-idor" class="wp-block-heading">4.3. IDOR trên File tĩnh (Static File IDOR)</h4>



<p class="wp-block-paragraph">Tên file thường dễ đoán.</p>



<ul class="wp-block-list">
<li>Ví dụ: <code><mark style="background-color:#e9ecef" class="has-inline-color">https://example.com/uploads/receipts/receipt_001.pdf.</mark></code></li>



<li>Hacker viết script lặp từ <code>001</code> đến <code>9999</code> để tải toàn bộ hóa đơn của hệ thống.</li>
</ul>



<h3 id="5-dan-chung-thuc-te-khi-ong-lon-cung-mac-sai-lam" class="wp-block-heading">5. Dẫn chứng thực tế: Khi &#8220;Ông lớn&#8221; cũng mắc sai lầm</h3>



<h4 id="5-1-mcdonalds-su-co-mchire-7-2025" class="wp-block-heading">5.1. McDonald&#8217;s &amp; sự cố McHire (7/2025)</h4>



<p class="wp-block-paragraph">Link: <a href="https://research.cgu.edu/icdc/2025/07/01/mcdonalds-july-2025-breach/">https://research.cgu.edu/icdc/2025/07/01/mcdonalds-july-2025-breach/</a></p>



<p class="wp-block-paragraph"><strong>Bối cảnh</strong>: Hệ thống bị ảnh hưởng là&nbsp;<strong>McHire</strong>, nền tảng tuyển dụng toàn cầu của McDonald&#8217;s, được vận hành bởi đối tác công nghệ bên thứ ba là&nbsp;<strong>Paradox.ai</strong>. Paradox.ai cung cấp giải pháp chatbot AI tên là &#8220;Olivia&#8221; để tự động hóa quy trình phỏng vấn, thu thập thông tin ứng viên và lên lịch làm việc. Sự phụ thuộc vào bên thứ ba này đã mở rộng bề mặt tấn công của McDonald&#8217;s ra ngoài hạ tầng kiểm soát trực tiếp của họ.<sup></sup>&nbsp;&nbsp;&nbsp;</p>



<p class="wp-block-paragraph"><strong>Nguyên nhân chính</strong><br>Sự cố xảy ra do kết hợp 2 lỗi nghiêm trọng:</p>



<ol class="wp-block-list">
<li><strong>Xác thực yếu (Weak Authentication)</strong>
<ul class="wp-block-list">
<li>Một tài khoản admin thử nghiệm của Paradox.ai dùng mật khẩu <strong>“123456”</strong>, không có MFA.</li>



<li>Tài khoản này tồn tại từ <strong>2019</strong>, không bị vô hiệu hóa dù có quyền truy cập production.</li>
</ul>
</li>



<li><strong>Lỗ hổng IDOR (Missing Object Level Authorization)</strong>
<ul class="wp-block-list">
<li>API backend cho phép truy xuất hồ sơ ứng viên bằng <code><mark style="background-color:#e9ecef" class="has-inline-color">applicant_id</mark></code>.</li>



<li>Không kiểm tra quyền truy cập → chỉ cần đổi ID là đọc được dữ liệu của bất kỳ ứng viên nào.</li>



<li>Từ 1 tài khoản → mở rộng ra <strong>64 triệu hồ sơ toàn cầu</strong>.</li>
</ul>
</li>
</ol>



<p class="wp-block-paragraph"><strong>Tác động</strong></p>



<ul class="wp-block-list">
<li>Rò rỉ <strong>PII</strong>: tên, email, số điện thoại, địa chỉ nhà.</li>



<li>Lộ dữ liệu nhạy cảm: log chat với AI (tính cách, sở thích, thông tin cá nhân).</li>



<li>Lộ token phiên → có thể giả danh ứng viên.</li>



<li>Nguy cơ cao cho <strong>social engineering, lừa đảo, chiếm tài khoản</strong>.</li>
</ul>



<p class="wp-block-paragraph">Sự kết hợp giữa <strong>quản lý tài khoản kém</strong> (tài khoản ma, không MFA) và <strong>thiếu kiểm soát quyền API</strong> (IDOR) tạo ra “cơn bão hoàn hảo” cho rò rỉ dữ liệu quy mô toàn cầu – một xu hướng cực kỳ đáng báo động trong năm 2025</p>



<h4 id="5-2-mozilla-firefox-accounts-lo-hong-xoa-tai-khoan-5-2025" class="wp-block-heading">5.2. Mozilla Firefox Accounts – Lỗ hổng xóa tài khoản (5/2025)</h4>



<p class="wp-block-paragraph">Link: <a href="https://hackerone.com/reports/3154983">https://hackerone.com/reports/3154983</a></p>



<p class="wp-block-paragraph"><strong>Bản chất sự cố</strong></p>



<ul class="wp-block-list">
<li>Lỗ hổng IDOR nghiêm trọng trong API xóa tài khoản (<code><mark style="background-color:#e9ecef" class="has-inline-color">/v1/account/destroy</mark></code>).</li>



<li>Cho phép <strong>xóa vĩnh viễn tài khoản người khác</strong> mà không cần tương tác từ nạn nhân (zero-click).</li>



<li>Ảnh hưởng trực tiếp đến Integrity (toàn vẹn) và Availability (sẵn sàng) của dữ liệu.</li>
</ul>



<p class="wp-block-paragraph"><strong>Nguyên nhân kỹ thuật</strong></p>



<ul class="wp-block-list">
<li>Lỗi Session Misbinding: backend không kiểm tra phiên làm việc có khớp với tài khoản bị xóa hay không.</li>



<li>API tin vào payload JSON (email, authPW) thay vì session của người gửi request.</li>



<li>Đây là dạng IDOR ghi/xóa, nguy hiểm hơn IDOR đọc dữ liệu.</li>
</ul>



<p class="wp-block-paragraph"><strong>Kịch bản khai thác</strong></p>



<ol class="wp-block-list">
<li>Kẻ tấn công tạo tài khoản Firefox, đăng nhập bằng SSO (Google).</li>



<li>Chặn request xóa tài khoản của chính mình.</li>



<li>Đổi email trong payload thành email nạn nhân (cũng dùng SSO, chưa set mật khẩu).</li>



<li>Gửi request → hệ thống xóa tài khoản nạn nhân.</li>
</ol>



<p class="wp-block-paragraph"><strong>Tác động</strong></p>



<ul class="wp-block-list">
<li>Mất vĩnh viễn dữ liệu: mật khẩu đã lưu, lịch sử duyệt web, tab, sync data.</li>



<li>Không thể khôi phục, không cần nạn nhân click hay xác nhận.</li>



<li>Được đánh giá <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color"><strong>Critical </strong></mark>trên HackerOne.</li>
</ul>



<p class="wp-block-paragraph"><strong>Bài học cốt lõi</strong>: IDOR không chỉ gây rò rỉ dữ liệu (read) mà còn có thể phá hủy dữ liệu (write/delete) nếu backend không ràng buộc chặt chẽ giữa session và đối tượng tài nguyên.</p>



<h4 id="5-3-gitlab-lo-hong-machine-learning-model-registry-2024-2025" class="wp-block-heading">5.3. GitLab – Lỗ hổng Machine Learning Model Registry (2024–2025)</h4>



<p class="wp-block-paragraph">Link: <a href="https://hackerone.com/reports/2528293">https://hackerone.com/reports/2528293</a></p>



<p class="wp-block-paragraph"><strong>Bối cảnh</strong></p>



<ul class="wp-block-list">
<li>GitLab triển khai Machine Learning Model Registry để quản lý phiên bản mô hình AI.</li>



<li>Lỗ hổng được báo cáo từ 5/2024, vá và công bố kéo dài sang <strong>2025</strong>, cho thấy độ phức tạp của bảo mật trên nền tảng lớn.</li>



<li>Sự cố ảnh hưởng trực tiếp đến tài sản trí tuệ AI (model weights, biases).</li>
</ul>



<p class="wp-block-paragraph"><strong>Nguyên nhân kỹ thuật</strong></p>



<ul class="wp-block-list">
<li>GitLab dùng <strong>ID tịnh tiến (1,2,3,&#8230;)</strong> cho <mark style="background-color:#e9ecef" class="has-inline-color"><code>model_id</code> </mark>thay vì UUID ngẫu nhiên.</li>



<li>API tải/xem mô hình không kiểm tra quyền truy cập dự án (Missing Object Level Authorization).</li>



<li>Đây là IDOR điển hình + thiết kế API kém an toàn.</li>
</ul>



<p class="wp-block-paragraph"><strong>Cách khai thác</strong></p>



<ul class="wp-block-list">
<li>Kẻ tấn công chỉ cần tài khoản GitLab miễn phí.</li>



<li>Lặp ID từ 1 → hàng triệu để liệt kê và tải mô hình của dự án khác.</li>



<li>Không cần brute-force, không cần quyền dự án.</li>
</ul>



<p class="wp-block-paragraph"><strong>Tác động</strong></p>



<ul class="wp-block-list">
<li>Rò rỉ mô hình ML độc quyền của doanh nghiệp.</li>



<li>Nguy cơ mất bí mật thương mại, thiệt hại kinh tế lớn trong bối cảnh AI là tài sản cốt lõi.</li>



<li>Lỗ hổng có thể bị khai thác tự động, quy mô lớn (enumeration).</li>
</ul>



<p class="wp-block-paragraph"><strong>Bài học cốt lõi</strong>: Không dùng ID tịnh tiến cho API công khai. Nó biến IDOR từ lỗ hổng tiềm ẩn thành thảm họa có thể khai thác hàng loạt, đặc biệt nguy hiểm với tài sản AI.</p>



<h3 id="6-ngan-chan-idor-hieu-qua-best-practices" class="wp-block-heading">6. Ngăn chặn IDOR hiệu quả (Best Practices)</h3>



<p class="wp-block-paragraph">Khi một tài khoản test có thể mở khóa 64 triệu hồ sơ, một email có thể xóa tài khoản người khác, và một con số tăng dần có thể làm bay cả mô hình AI, thì rõ ràng vấn đề không nằm ở “hacker quá giỏi” — mà ở chỗ chúng ta cần chặn IDOR cho đúng cách. </p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="572" src="https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-1024x572.png" alt="" class="wp-image-4197" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-1024x572.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-300x167.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-768x429.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-380x212.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-800x447.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-1160x647.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1.png 1376w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph"><em>&#8220;Đừng bao giờ tin tưởng Client&#8221;</em>. Dưới đây là chiến lược phòng thủ đa lớp <strong>(Defense in Depth)</strong>.</p>



<p class="wp-block-paragraph"><strong>Lớp 1: Kiểm soát truy cập tại tầng Data (Data-Layer Authorization)</strong></p>



<p class="wp-block-paragraph">Đây là chốt chặn cuối cùng và quan trọng nhất. Mọi câu lệnh truy vấn liên quan đến dữ liệu người dùng <strong>phải</strong> kèm theo điều kiện về chủ sở hữu.</p>



<p class="wp-block-paragraph"><strong>❌ Code rủi ro (Spring Data JPA):</strong></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:7.703125px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">Java</span><span role="button" tabindex="0" data-code="// Controller
public Order getOrder(@PathVariable Long id) {
    return orderRepo.findById(id); // Chỉ tìm theo ID đơn hàng
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">// Controller</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Order</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">getOrder</span><span style="color: #D4D4D4">(@</span><span style="color: #4EC9B0">PathVariable</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Long</span><span style="color: #D4D4D4"> id) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">orderRepo</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">findById</span><span style="color: #D4D4D4">(id); </span><span style="color: #6A9955">// Chỉ tìm theo ID đơn hàng</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p class="wp-block-paragraph"><strong>✅ Code an toàn (Repository Pattern):</strong></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:15.40625px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">Java</span><span role="button" tabindex="0" data-code="// Repository
// Luôn gắn kèm userId vào câu query
@Query(&quot;SELECT o FROM Order o WHERE o.id = :id AND o.owner.id = :currentUserId&quot;)
Optional&lt;Order&gt; findByIdAndOwner(@Param(&quot;id&quot;) Long id, @Param(&quot;currentUserId&quot;) Long userId);

// Service Layer
public Order getOrder(Long id, User currentUser) {
    return orderRepo.findByIdAndOwner(id, currentUser.getId())
        .orElseThrow(() -&gt; new ResourceNotFoundException(&quot;Order not found or access denied&quot;));
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">// Repository</span></span>
<span class="line"><span style="color: #6A9955">// Luôn gắn kèm userId vào câu query</span></span>
<span class="line"><span style="color: #D4D4D4">@</span><span style="color: #4EC9B0">Query</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;SELECT o FROM Order o WHERE o.id = :id AND o.owner.id = :currentUserId&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #4EC9B0">Optional</span><span style="color: #D4D4D4">&lt;Order&gt; </span><span style="color: #DCDCAA">findByIdAndOwner</span><span style="color: #D4D4D4">(@</span><span style="color: #4EC9B0">Param</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;id&quot;</span><span style="color: #D4D4D4">) </span><span style="color: #4EC9B0">Long</span><span style="color: #D4D4D4"> id, @</span><span style="color: #4EC9B0">Param</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;currentUserId&quot;</span><span style="color: #D4D4D4">) </span><span style="color: #4EC9B0">Long</span><span style="color: #D4D4D4"> userId);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">// Service Layer</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Order</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">getOrder</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">Long</span><span style="color: #D4D4D4"> id, </span><span style="color: #4EC9B0">User</span><span style="color: #D4D4D4"> currentUser) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">orderRepo</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">findByIdAndOwner</span><span style="color: #D4D4D4">(id, </span><span style="color: #9CDCFE">currentUser</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">getId</span><span style="color: #D4D4D4">())</span></span>
<span class="line"><span style="color: #D4D4D4">        .</span><span style="color: #DCDCAA">orElseThrow</span><span style="color: #D4D4D4">(() </span><span style="color: #569CD6">-&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">ResourceNotFoundException</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Order not found or access denied&quot;</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p class="wp-block-paragraph"><strong>Lớp 2: Kiểm soát truy cập tại tầng Controller (Method-Level Security)</strong></p>



<p class="wp-block-paragraph">Sử dụng các Annotation bảo mật của Framework (như Spring Security) để chặn ngay từ cửa vào.</p>



<p class="wp-block-paragraph"><strong>✅ Ví dụ với Spring Security (<code><mark style="background-color:#e9ecef" class="has-inline-color">@PreAuthorize</mark></code>):</strong></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:7.703125px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">Java</span><span role="button" tabindex="0" data-code="@GetMapping(&quot;/invoices/{id}&quot;)
// SpEL: Chỉ cho phép nếu user hiện tại là chủ sở hữu của invoice có id này
@PreAuthorize(&quot;@securityService.isOwner(authentication, #id)&quot;) 
public Invoice getInvoice(@PathVariable Long id) {
    return invoiceRepo.findById(id);
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">@</span><span style="color: #4EC9B0">GetMapping</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;/invoices/{id}&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #6A9955">// SpEL: Chỉ cho phép nếu user hiện tại là chủ sở hữu của invoice có id này</span></span>
<span class="line"><span style="color: #D4D4D4">@</span><span style="color: #4EC9B0">PreAuthorize</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;@securityService.isOwner(authentication, #id)&quot;</span><span style="color: #D4D4D4">) </span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Invoice</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">getInvoice</span><span style="color: #D4D4D4">(@</span><span style="color: #4EC9B0">PathVariable</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Long</span><span style="color: #D4D4D4"> id) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">invoiceRepo</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">findById</span><span style="color: #D4D4D4">(id);</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p class="wp-block-paragraph"><em>Cách này giúp code business logic sạch hơn, tách biệt logic bảo mật ra khỏi logic nghiệp vụ.</em></p>



<p class="wp-block-paragraph"><strong>Lớp 3: Làm khó Hacker bằng UUID (Obfuscation)</strong></p>



<p class="wp-block-paragraph">Thay vì dùng Auto-Increment Integer (<code>1, 2, 3</code>), hãy dùng <strong>UUID</strong> (Random String: <code><mark style="background-color:#e9ecef" class="has-inline-color">a0eebc99-9c0b...</mark></code>) hoặc các thuật toán băm ID (như Hashids).</p>



<ul class="wp-block-list">
<li><strong>Lợi ích:</strong> Chặn đứng tấn công kiểu liệt kê (Enumeration Attack). Hacker không thể viết vòng lặp <code><mark style="background-color:#e9ecef" class="has-inline-color">for (i=0; i&lt;1000; i++)</mark></code> để quét dữ liệu.</li>



<li><strong>Cảnh báo:</strong> UUID <strong>KHÔNG THỂ THAY THẾ</strong> được <em>Lớp 1</em> và <em>Lớp 2</em>. Nếu hacker bằng cách nào đó biết được UUID của nạn nhân, IDOR vẫn xảy ra nếu thiếu Authorization check. UUID chỉ là &#8220;lớp sương mù&#8221;, không phải &#8220;bức tường thép&#8221;.</li>
</ul>



<p class="wp-block-paragraph"><strong>Lớp 4: Kiểm thử tự động (Automated Security Testing)</strong></p>



<p class="wp-block-paragraph">Đừng đợi Pentester tìm lỗi. Hãy viết Test Case cho IDOR.</p>



<ul class="wp-block-list">
<li><strong>Unit Test:</strong> Tạo 2 user (Alice, Bob). Đăng nhập bằng Alice, cố gắng request dữ liệu ID của Bob. Assert rằng kết quả trả về phải là <code><mark style="background-color:#e9ecef" class="has-inline-color">403 Forbidden</mark></code> hoặc <code><mark style="background-color:#e9ecef" class="has-inline-color">404 Not Found</mark></code>.</li>
</ul>



<h3 id="7-ket-luan" class="wp-block-heading">7. Kết luận</h3>



<p class="wp-block-paragraph">IDOR không phải là một lỗi kỹ thuật phức tạp, nhưng nó phản ánh sự thiếu sót trong tư duy thiết kế hệ thống.</p>



<p class="wp-block-paragraph">Hãy nhớ nguyên tắc vàng: <strong>Mọi request truy cập dữ liệu đều phải trả lời được hai câu hỏi:</strong></p>



<ol class="wp-block-list">
<li><em>Người dùng này là ai? (Authentication)</em></li>



<li><em>Dữ liệu này có phải của họ không? (Authorization)</em></li>
</ol>



<p class="wp-block-paragraph">Nếu thiếu câu hỏi số 2, chúng ta đang mở toang cánh cửa &#8220;phòng 102&#8221; cho bất kỳ ai cầm chìa khóa &#8220;phòng 101&#8221;.</p>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://blog.tomosia.com.vn/idor-lo-hong-ngay-tho-nhung-chi-mang-trong-kien-truc-phan-mem/">IDOR &#8211; Lỗ hổng &#8220;ngây thơ&#8221; nhưng chí mạng trong kiến trúc phần mềm?</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/idor-lo-hong-ngay-tho-nhung-chi-mang-trong-kien-truc-phan-mem/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Keycloak: Giải pháp IAM toàn diện cho ứng dụng hiện đại</title>
		<link>https://blog.tomosia.com.vn/keycloak-giai-phap-iam-toan-dien-cho-ung-dung-hien-dai/</link>
					<comments>https://blog.tomosia.com.vn/keycloak-giai-phap-iam-toan-dien-cho-ung-dung-hien-dai/#comments</comments>
		
		<dc:creator><![CDATA[Nguyen Luan]]></dc:creator>
		<pubDate>Tue, 09 Dec 2025 03:26:45 +0000</pubDate>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Solution]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=3519</guid>

					<description><![CDATA[<p>Keycloak là một giải pháp quản lý danh tính và truy cập (IAM – Identity and Access Management)&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/keycloak-giai-phap-iam-toan-dien-cho-ung-dung-hien-dai/">Keycloak: Giải pháp IAM toàn diện cho ứng dụng hiện đại</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph"><em>Keycloak là <strong>một giải pháp quản lý danh tính và truy cập (IAM – Identity and Access Management)</strong> mã nguồn mở, giúp bạn triển khai đăng nhập, phân quyền và bảo mật cho ứng dụng mà không cần tự xây từ đầu.</em><br></p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="538" src="https://blog.tomosia.com.vn/wp-content/uploads/2025/11/image-1-1024x538.png" alt="" class="wp-image-3520" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2025/11/image-1-1024x538.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2025/11/image-1-300x158.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2025/11/image-1-768x403.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2025/11/image-1-380x200.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2025/11/image-1-800x420.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2025/11/image-1.png 1034w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Bảo mật luôn là yếu tố cốt lõi trong quá trình phát triển ứng dụng, đặc biệt khi bạn phải xử lý việc xác thực và phân quyền cho nhiều người dùng khác nhau.</p>



<p class="wp-block-paragraph">Dù các developer hoàn toàn có thể tự xây dựng hệ thống quản lý danh tính từ con số 0, nhưng việc này tốn rất nhiều thời gian, công sức và dễ phát sinh lỗi &#8211; nhất là khi phải hỗ trợ các giao thức tiêu chuẩn như OAuth2 hay OpenID Connect.</p>



<p class="wp-block-paragraph">Keycloak xuất hiện như một giải pháp giúp đơn giản hóa toàn bộ quá trình này. Nó không chỉ giảm tải việc triển khai xác thực mà còn mang đến sự đồng nhất, bảo mật cao và khả năng tùy biến mạnh mẽ. Bạn có thể tích hợp đăng nhập bằng Google, Facebook hoặc thiết lập SSO chỉ trong vài bước cấu hình mà không cần viết lại cả hệ thống.</p>



<h2 id="1-what-keycloak-la-gi" class="wp-block-heading"><strong>1.</strong> <strong>What? Keycloak là gì?</strong></h2>



<p class="wp-block-paragraph"><strong>Keycloak</strong> là một nền tảng mã nguồn mở mạnh mẽ, cung cấp giải pháp đăng nhập một lần (SSO) cùng hệ thống quản lý danh tính và quyền truy cập cho các ứng dụng và dịch vụ.<br><br>Nền tảng này được thiết kế nhằm đơn giản hóa toàn bộ bài toán bảo mật, giúp developer dễ dàng tích hợp cơ chế đăng nhập, phân quyền và xác thực vào ứng dụng mà không phải xây dựng mọi thứ từ đầu. Tất cả đều được Keycloak cung cấp sẵn dưới dạng các tính năng linh hoạt, dễ tùy chỉnh để phù hợp với nhu cầu của từng doanh nghiệp hay tổ chức.<br></p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://mintlify.s3.us-west-1.amazonaws.com/infisical/images/sso/keycloak-oidc/create-client-login-settings.png" alt=""/></figure>



<p class="wp-block-paragraph"><br>Keycloak cho phép bạn tùy chỉnh giao diện người dùng cho các trang đăng nhập, đăng ký, quản lý tài khoản hay trang quản trị. Bên cạnh đó, nó còn hỗ trợ tích hợp với các hệ thống quản lý danh tính sẵn có như LDAP hoặc Active Directory, và cho phép xác thực thông qua các nhà cung cấp bên thứ ba như Google hay Facebook.<br><br><strong>Tóm lại</strong>: đối với developer, Keycloak giống như một “bộ công cụ all-in-one” để giải quyết toàn bộ vấn đề liên quan đến đăng nhập và phân quyền. Thay vì tự xây các tính năng như login, role-based access hay kết nối với các nền tảng bên ngoài, bạn chỉ cần cấu hình và sử dụng những gì Keycloak đã cung cấp.</p>



<p class="wp-block-paragraph"><strong>Luồng hoạt động:</strong><br>+ Spring Boot kiểm tra Token và cho phép truy cập nếu hợp lệ.<br>+ User đăng nhập vào Keycloak.<br>+ Keycloak trả về Token.<br>+ User gửi request kèm Token đến Spring Boot.<br></p>



<h2 id="2-why-tai-sao-lai-la-keycloak" class="wp-block-heading"><strong>2.</strong> <strong>Why? Tại sao lại là Keycloak?</strong></h2>



<figure class="wp-block-image size-large is-resized"><img decoding="async" src="https://www.voiceatthetable.com/wp-content/uploads/why-1780726_640.png" alt="" style="width:680px;height:auto"/></figure>



<p class="wp-block-paragraph">Thay vì tự xây dựng module đăng nhập (Login) trong Spring Boot, việc sử dụng Keycloak mang lại các lợi ích:</p>



<p class="wp-block-paragraph"><strong>a</strong>. <strong>Quản lý tập trung:</strong> Admin có thể khóa user, đổi password, cấp quyền ngay trên giao diện Keycloak mà không cần sửa code hay database của ứng dụng.</p>



<p class="wp-block-paragraph"><strong>b</strong>. <strong>Bảo mật chuyên sâu (Delegated Authentication):</strong> Việc xử lý mật khẩu, mã hóa, 2FA (xác thực 2 bước) được Keycloak đảm nhận. Spring Boot không cần lo về lộ mật khẩu.</p>



<p class="wp-block-paragraph"><strong>c. Single Sign-On (SSO):</strong> Nếu bạn có 10 ứng dụng (Microservices), user chỉ cần đăng nhập 1 lần tại Keycloak là vào được cả 10 ứng dụng.</p>



<p class="wp-block-paragraph"><strong>d. Chuẩn hóa (Standardization):</strong> Sử dụng giao thức OAuth2 và OpenID Connect chuẩn quốc tế. Dễ dàng tích hợp với Frontend (React, Angular, Mobile App).</p>



<h2 id="3-cac-thanh-phan-chinh-trong-keycloak" class="wp-block-heading"><strong>3.</strong> <strong>Các thành phần chính trong Keycloak</strong></h2>



<h3 id="3-1-realm-khong-gian-quan-ly" class="wp-block-heading"><em><strong>3.1. Realm (Không gian quản lý)</strong></em></h3>



<p class="wp-block-paragraph"><strong>Định nghĩa:</strong> Realm là một không gian quản lý định danh hoàn toàn độc lập. Dữ liệu (user, role, client) của Realm A hoàn toàn tách biệt với Realm B.<br><br><strong>Master Realm:</strong> Khi cài đặt xong, Keycloak có sẵn realm tên là <strong><em>Master</em></strong>. Đây là realm dùng để quản trị hệ thống Keycloak. Không nên dùng realm này để quản lý user cho ứng dụng của bạn.<br><br><strong>Ví dụ:</strong> Bạn có thể tạo Realm <strong><em>Users </em></strong>cho người dùng cuối và Realm <strong><em>Admins </em></strong>cho site quản trị.</p>



<h3 id="3-2-client-ung-dung-khach" class="wp-block-heading"><em><strong>3.2. Client (Ứng dụng khách)</strong></em></h3>



<p class="wp-block-paragraph"><strong>Định nghĩa:</strong> Client là các ứng dụng hoặc dịch vụ muốn sử dụng Keycloak để xác thực. Client đại diện cho các ứng dụng hoặc dịch vụ được bảo mật bởi Keycloak. Mỗi client có cấu hình riêng, bao gồm giao thức (OAuth2, OpenID Connect), URL callback, và thông tin bảo mật như client secret.</p>



<h3 id="3-3-user-nguoi-dung" class="wp-block-heading"><strong><em>3.3. User (Người dùng)</em></strong></h3>



<p class="wp-block-paragraph"><strong>Định nghĩa:</strong> Thực thể có thể đăng nhập vào hệ thống.</p>



<p class="wp-block-paragraph"><strong>Thuộc tính:</strong> User có Username, Password, Email, và các thuộc tính tùy chỉnh (Attributes) như số điện thoại, mã nhân viên, phòng ban&#8230;</p>



<p class="wp-block-paragraph">Keycloak hỗ trợ các phương thức xác thực đa dạng:<br>&#8211; Đăng nhập bằng email/mật khẩu.<br>&#8211; Đăng nhập qua các nhà cung cấp bên thứ ba (Google, Facebook).<br>&#8211; Xác thực hai yếu tố (2FA).</p>



<h3 id="3-4-role-vai-tro" class="wp-block-heading"><strong><em>3.4. Role (Vai trò)</em></strong></h3>



<p class="wp-block-paragraph">Role dùng để phân quyền (Authorization). Có 2 loại Role trong Keycloak:<br>&#8211; <strong>Realm Role:</strong> Role toàn cục. Ví dụ: <code>Global_Admin</code>, <code>User</code>. Có hiệu lực trên toàn bộ Realm.<br>&#8211; <strong>Client Role:</strong> Role gắn liền với một Client cụ thể. Ví dụ: Client <code>Inventory-App</code> có role <code>stock-keeper</code>. Role này chỉ có ý nghĩa với app kho, không có ý nghĩa với app nhân sự.</p>



<h3 id="3-5-group-nhom" class="wp-block-heading"><strong><em>3.5. Group (Nhóm)</em></strong></h3>



<p class="wp-block-paragraph"><strong>Định nghĩa:</strong> Dùng để gom nhóm các User lại.<br><br><strong>Đặc điểm:</strong> Group có thể lồng nhau (Parent &#8211; Child). Ví dụ: <code>IT Department</code> -> <code>Dev Team</code>.<br><br><strong>Tác dụng:</strong> Bạn có thể gán Role cho Group, tất cả User trong Group đó sẽ tự động kế thừa Role. Giúp quản lý quyền hạn dễ dàng hơn gán lẻ tẻ.</p>



<h2 id="4-how-lam-the-nao-de-tich-hop-keycloak-vao-1-ung-dung-spring-boot-chi-trong-vai-buoc-don-gian" class="wp-block-heading"><strong>4. How? Làm thế nào để tích hợp Keycloak vào 1 ứng dụng Spring Boot chỉ trong vài bước đơn giản.</strong></h2>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="842" height="1024" src="https://blog.tomosia.com.vn/wp-content/uploads/2025/11/image-16-842x1024.png" alt="" class="wp-image-3591" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2025/11/image-16-842x1024.png 842w, https://blog.tomosia.com.vn/wp-content/uploads/2025/11/image-16-247x300.png 247w, https://blog.tomosia.com.vn/wp-content/uploads/2025/11/image-16-scaled.png 2104w" sizes="auto, (max-width: 842px) 100vw, 842px" /></figure>



<p class="wp-block-paragraph"><strong>Phần A: Cấu hình Keycloak (Docker)</strong><br><strong>Bước 1: Khởi chạy Keycloak</strong> Mở terminal và chạy lệnh Docker (bản developer):</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:7.703125px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">ShellScript</span><span role="button" tabindex="0" data-code="docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:24.0.1 start-dev" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #62E884">docker</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">run</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">-p</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">8080</span><span style="color: #E7EE98">:8080</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">-e</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">KEYCLOAK_ADMIN=admin</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">-e</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">KEYCLOAK_ADMIN_PASSWORD=admin</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">quay.io/keycloak/keycloak:24.0.1</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">start-dev</span></span></code></pre></div>



<p class="wp-block-paragraph"><strong>Bước 2: Thiết lập Realm và Client</strong></p>



<ol class="wp-block-list">
<li>Truy cập <code>http://localhost:8080</code> -&gt; Vào <strong>Administration Console</strong> (Login: admin/admin).</li>



<li><strong>Tạo Realm:</strong> Bấm vào dropdown menu góc trái -&gt; <strong>Create Realm</strong> -&gt; Tên: <code>springboot-demo</code>.</li>



<li><strong>Tạo Client:</strong><br>&#8211; Vào menu <strong>Clients</strong> -&gt; <strong>Create client</strong>.<br>&#8211; Client ID: <code>my-backend-api</code>.<br>&#8211; Capability config: Bật <strong>Client authentication</strong> (để có Client Secret) và <strong>Authorization</strong>.<br>&#8211; Lưu lại.</li>



<li><strong>Tạo Roles:</strong><br>&#8211; Vào menu <strong>Realm roles</strong> -&gt; <strong>Create role</strong>.<br>&#8211; Tạo 2 role: <code>USER</code> và <code>ADMIN</code>.</li>
</ol>



<p class="wp-block-paragraph"><strong>Phần B: Lập trình Spring Boot (Resource Server)</strong><br><strong>Bước 1: Dependencies (<code>pom.xml</code>)</strong> Cần Java 17+ và Spring Boot 3.x.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:15.40625px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">XML</span><span role="button" tabindex="0" data-code="&lt;dependencies&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
    &lt;/dependency&gt;
    &lt;!-- Thư viện quan trọng nhất để biến App thành Resource Server --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-oauth2-resource-server&lt;/artifactId&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-security&lt;/artifactId&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;
        &lt;artifactId&gt;lombok&lt;/artifactId&gt;
        &lt;optional&gt;true&lt;/optional&gt;
    &lt;/dependency&gt;
&lt;/dependencies&gt;" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">&lt;</span><span style="color: #F286C4">dependencies</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">    &lt;</span><span style="color: #F286C4">dependency</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">        &lt;</span><span style="color: #F286C4">groupId</span><span style="color: #F6F6F4">&gt;org.springframework.boot&lt;/</span><span style="color: #F286C4">groupId</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">        &lt;</span><span style="color: #F286C4">artifactId</span><span style="color: #F6F6F4">&gt;spring-boot-starter-web&lt;/</span><span style="color: #F286C4">artifactId</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">    &lt;/</span><span style="color: #F286C4">dependency</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">&lt;!-- Thư viện quan trọng nhất để biến App thành Resource Server --&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">    &lt;</span><span style="color: #F286C4">dependency</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">        &lt;</span><span style="color: #F286C4">groupId</span><span style="color: #F6F6F4">&gt;org.springframework.boot&lt;/</span><span style="color: #F286C4">groupId</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">        &lt;</span><span style="color: #F286C4">artifactId</span><span style="color: #F6F6F4">&gt;spring-boot-starter-oauth2-resource-server&lt;/</span><span style="color: #F286C4">artifactId</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">    &lt;/</span><span style="color: #F286C4">dependency</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">    &lt;</span><span style="color: #F286C4">dependency</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">        &lt;</span><span style="color: #F286C4">groupId</span><span style="color: #F6F6F4">&gt;org.springframework.boot&lt;/</span><span style="color: #F286C4">groupId</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">        &lt;</span><span style="color: #F286C4">artifactId</span><span style="color: #F6F6F4">&gt;spring-boot-starter-security&lt;/</span><span style="color: #F286C4">artifactId</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">    &lt;/</span><span style="color: #F286C4">dependency</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">    &lt;</span><span style="color: #F286C4">dependency</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">        &lt;</span><span style="color: #F286C4">groupId</span><span style="color: #F6F6F4">&gt;org.projectlombok&lt;/</span><span style="color: #F286C4">groupId</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">        &lt;</span><span style="color: #F286C4">artifactId</span><span style="color: #F6F6F4">&gt;lombok&lt;/</span><span style="color: #F286C4">artifactId</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">        &lt;</span><span style="color: #F286C4">optional</span><span style="color: #F6F6F4">&gt;true&lt;/</span><span style="color: #F286C4">optional</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">    &lt;/</span><span style="color: #F286C4">dependency</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">&lt;/</span><span style="color: #F286C4">dependencies</span><span style="color: #F6F6F4">&gt;</span></span></code></pre></div>



<p class="wp-block-paragraph"><strong>Bước 2: Cấu hình (<code>application.yml</code>)</strong> Khai báo địa chỉ của Keycloak để Spring Boot biết nơi xác thực token.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:15.40625px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">YAML</span><span role="button" tabindex="0" data-code="server:
  port: 5000 # Chạy port khác Keycloak

spring:
  application:
    name: keycloak-integration-demo
  security:
    oauth2:
      resourceserver:
        jwt:
          # Đường dẫn Issuer của Realm (Quan trọng)
          # Cấu trúc: http://&lt;host&gt;:&lt;port&gt;/realms/&lt;realm-name&gt;
          issuer-uri: http://localhost:8080/realms/springboot-demo
          # jwk-set-uri sẽ được tự động cấu hình từ issuer-uri" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #97E1F1">server</span><span style="color: #F286C4">:</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #97E1F1">port</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">5000</span><span style="color: #F6F6F4"> </span><span style="color: #7B7F8B"># Chạy port khác Keycloak</span></span>
<span class="line"></span>
<span class="line"><span style="color: #97E1F1">spring</span><span style="color: #F286C4">:</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #97E1F1">application</span><span style="color: #F286C4">:</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #97E1F1">name</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">keycloak-integration-demo</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #97E1F1">security</span><span style="color: #F286C4">:</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #97E1F1">oauth2</span><span style="color: #F286C4">:</span></span>
<span class="line"><span style="color: #F6F6F4">      </span><span style="color: #97E1F1">resourceserver</span><span style="color: #F286C4">:</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1">jwt</span><span style="color: #F286C4">:</span></span>
<span class="line"><span style="color: #F6F6F4">          </span><span style="color: #7B7F8B"># Đường dẫn Issuer của Realm (Quan trọng)</span></span>
<span class="line"><span style="color: #F6F6F4">          </span><span style="color: #7B7F8B"># Cấu trúc: http://&lt;host&gt;:&lt;port&gt;/realms/&lt;realm-name&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">          </span><span style="color: #97E1F1">issuer-uri</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">http://localhost:8080/realms/springboot-demo</span></span>
<span class="line"><span style="color: #F6F6F4">          </span><span style="color: #7B7F8B"># jwk-set-uri sẽ được tự động cấu hình từ issuer-uri</span></span></code></pre></div>



<p class="wp-block-paragraph"><strong>Bước 3: Class <code>SecurityConfig.java</code> (Quan trọng nhất)</strong> Mặc định Keycloak trả về role trong JSON phức tạp, ta cần convert nó về dạng <code>ROLE_ABC</code> để Spring Security hiểu.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:15.40625px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import 
org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.web.SecurityFilterChain;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity // Cho phép dùng @PreAuthorize ở Controller
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -&gt; csrf.disable()) // Tắt CSRF cho API
            .authorizeHttpRequests(auth -&gt; auth
                .requestMatchers(&quot;/public/**&quot;).permitAll() // API public không cần token
                .anyRequest().authenticated() // Các API còn lại yêu cầu phải login
            )
            .oauth2ResourceServer(oauth2 -&gt; oauth2
                .jwt(jwt -&gt; jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()))
            );
        
        return http.build();
    }

    // Converter: Biến đổi thông tin từ JWT Keycloak sang Spring Security Authority
    @Bean
    public JwtAuthenticationConverter jwtAuthenticationConverter() {
        JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
        converter.setJwtGrantedAuthoritiesConverter(source -&gt; {
            // Lấy map roles từ claim &quot;realm_access&quot; của Keycloak
            Map&lt;String, Object&gt; realmAccess = source.getClaimAsMap(&quot;realm_access&quot;);
            
            if (realmAccess == null || !realmAccess.containsKey(&quot;roles&quot;)) {
                return List.of();
            }

            List&lt;String&gt; roles = (List&lt;String&gt;) realmAccess.get(&quot;roles&quot;);
            
            // Convert: [USER, ADMIN] -&gt; [ROLE_USER, ROLE_ADMIN]
            return roles.stream()
                    .map(role -&gt; new org.springframework.security.core.authority.SimpleGrantedAuthority(&quot;ROLE_&quot; + role))
                    .collect(Collectors.toList());
        });
        return converter;
    }
}
" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">com.example.demo.config</span><span style="color: #F6F6F4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> org.springframework.context.annotation.Bean;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> org.springframework.context.annotation.Configuration;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> org.springframework.security.config.annotation.web.builders.HttpSecurity;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> org.springframework.security.web.SecurityFilterChain;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> java.util.Collection;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> java.util.List;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> java.util.Map;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> java.util.stream.Collectors;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">@</span><span style="color: #97E1F1; font-style: italic">Configuration</span></span>
<span class="line"><span style="color: #F6F6F4">@</span><span style="color: #97E1F1; font-style: italic">EnableWebSecurity</span></span>
<span class="line"><span style="color: #F6F6F4">@</span><span style="color: #97E1F1; font-style: italic">EnableMethodSecurity</span><span style="color: #F6F6F4"> </span><span style="color: #7B7F8B">// Cho phép dùng @PreAuthorize ở Controller</span></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">SecurityConfig</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    @</span><span style="color: #97E1F1; font-style: italic">Bean</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">SecurityFilterChain</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">filterChain</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1; font-style: italic">HttpSecurity</span><span style="color: #F6F6F4"> </span><span style="color: #FFB86C; font-style: italic">http</span><span style="color: #F6F6F4">) </span><span style="color: #F286C4">throws</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Exception</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">        http</span></span>
<span class="line"><span style="color: #F6F6F4">            .</span><span style="color: #62E884">csrf</span><span style="color: #F6F6F4">(csrf </span><span style="color: #97E1F1; font-style: italic">-&gt;</span><span style="color: #F6F6F4"> csrf.</span><span style="color: #62E884">disable</span><span style="color: #F6F6F4">()) </span><span style="color: #7B7F8B">// Tắt CSRF cho API</span></span>
<span class="line"><span style="color: #F6F6F4">            .</span><span style="color: #62E884">authorizeHttpRequests</span><span style="color: #F6F6F4">(auth </span><span style="color: #97E1F1; font-style: italic">-&gt;</span><span style="color: #F6F6F4"> auth</span></span>
<span class="line"><span style="color: #F6F6F4">                .</span><span style="color: #62E884">requestMatchers</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">/public/**</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">).</span><span style="color: #62E884">permitAll</span><span style="color: #F6F6F4">() </span><span style="color: #7B7F8B">// API public không cần token</span></span>
<span class="line"><span style="color: #F6F6F4">                .</span><span style="color: #62E884">anyRequest</span><span style="color: #F6F6F4">().</span><span style="color: #62E884">authenticated</span><span style="color: #F6F6F4">() </span><span style="color: #7B7F8B">// Các API còn lại yêu cầu phải login</span></span>
<span class="line"><span style="color: #F6F6F4">            )</span></span>
<span class="line"><span style="color: #F6F6F4">            .</span><span style="color: #62E884">oauth2ResourceServer</span><span style="color: #F6F6F4">(oauth2 </span><span style="color: #97E1F1; font-style: italic">-&gt;</span><span style="color: #F6F6F4"> oauth2</span></span>
<span class="line"><span style="color: #F6F6F4">                .</span><span style="color: #62E884">jwt</span><span style="color: #F6F6F4">(jwt </span><span style="color: #97E1F1; font-style: italic">-&gt;</span><span style="color: #F6F6F4"> jwt.</span><span style="color: #62E884">jwtAuthenticationConverter</span><span style="color: #F6F6F4">(</span><span style="color: #62E884">jwtAuthenticationConverter</span><span style="color: #F6F6F4">()))</span></span>
<span class="line"><span style="color: #F6F6F4">            );</span></span>
<span class="line"><span style="color: #F6F6F4">        </span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> http.</span><span style="color: #62E884">build</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">// Converter: Biến đổi thông tin từ JWT Keycloak sang Spring Security Authority</span></span>
<span class="line"><span style="color: #F6F6F4">    @</span><span style="color: #97E1F1; font-style: italic">Bean</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">JwtAuthenticationConverter</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">jwtAuthenticationConverter</span><span style="color: #F6F6F4">() {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1; font-style: italic">JwtAuthenticationConverter</span><span style="color: #F6F6F4"> converter </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">JwtAuthenticationConverter</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">        converter.</span><span style="color: #62E884">setJwtGrantedAuthoritiesConverter</span><span style="color: #F6F6F4">(source </span><span style="color: #97E1F1; font-style: italic">-&gt;</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #7B7F8B">// Lấy map roles từ claim &quot;realm_access&quot; của Keycloak</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #97E1F1; font-style: italic">Map</span><span style="color: #F6F6F4">&lt;String, Object&gt; realmAccess </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> source.</span><span style="color: #62E884">getClaimAsMap</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">realm_access</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">);</span></span>
<span class="line"><span style="color: #F6F6F4">            </span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> (realmAccess </span><span style="color: #F286C4">==</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">null</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">||</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">!</span><span style="color: #F6F6F4">realmAccess.</span><span style="color: #62E884">containsKey</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">roles</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)) {</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> List.</span><span style="color: #62E884">of</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #97E1F1; font-style: italic">List</span><span style="color: #F6F6F4">&lt;String&gt; roles </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> (</span><span style="color: #97E1F1; font-style: italic">List</span><span style="color: #F286C4">&lt;</span><span style="color: #F6F6F4">String</span><span style="color: #F286C4">&gt;</span><span style="color: #F6F6F4">) realmAccess.</span><span style="color: #62E884">get</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">roles</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">);</span></span>
<span class="line"><span style="color: #F6F6F4">            </span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #7B7F8B">// Convert: [USER, ADMIN] -&gt; [ROLE_USER, ROLE_ADMIN]</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> roles.</span><span style="color: #62E884">stream</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">                    .</span><span style="color: #62E884">map</span><span style="color: #F6F6F4">(role </span><span style="color: #97E1F1; font-style: italic">-&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> org.springframework.security.core.authority.</span><span style="color: #62E884">SimpleGrantedAuthority</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">ROLE_</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">+</span><span style="color: #F6F6F4"> role))</span></span>
<span class="line"><span style="color: #F6F6F4">                    .</span><span style="color: #62E884">collect</span><span style="color: #F6F6F4">(Collectors.</span><span style="color: #62E884">toList</span><span style="color: #F6F6F4">());</span></span>
<span class="line"><span style="color: #F6F6F4">        });</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> converter;</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph"><strong>Bước 4: Class <code>DemoController.java</code></strong></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:15.40625px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package com.example.demo.controller;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    @GetMapping(&quot;/public/hello&quot;)
    public String hello() {
        return &quot;Public: Xin chào, ai cũng xem được!&quot;;
    }

    @GetMapping(&quot;/user/profile&quot;)
    @PreAuthorize(&quot;hasRole('USER')&quot;)
    public String userProfile() {
        return &quot;Secured: Đây là trang dành cho User có role USER.&quot;;
    }

    @GetMapping(&quot;/admin/dashboard&quot;)
    @PreAuthorize(&quot;hasRole('ADMIN')&quot;)
    public String adminDashboard() {
        return &quot;Secured: Đây là trang quản trị (ADMIN ONLY).&quot;;
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">com.example.demo.controller</span><span style="color: #F6F6F4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> org.springframework.security.access.prepost.PreAuthorize;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> org.springframework.web.bind.annotation.GetMapping;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> org.springframework.web.bind.annotation.RestController;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">@</span><span style="color: #97E1F1; font-style: italic">RestController</span></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">DemoController</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    @</span><span style="color: #97E1F1; font-style: italic">GetMapping</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">/public/hello</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">String</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">hello</span><span style="color: #F6F6F4">() {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">Public: Xin chào, ai cũng xem được!</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    @</span><span style="color: #97E1F1; font-style: italic">GetMapping</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">/user/profile</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">    @</span><span style="color: #97E1F1; font-style: italic">PreAuthorize</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">hasRole(&#39;USER&#39;)</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">String</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">userProfile</span><span style="color: #F6F6F4">() {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">Secured: Đây là trang dành cho User có role USER.</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    @</span><span style="color: #97E1F1; font-style: italic">GetMapping</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">/admin/dashboard</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">    @</span><span style="color: #97E1F1; font-style: italic">PreAuthorize</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">hasRole(&#39;ADMIN&#39;)</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">String</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">adminDashboard</span><span style="color: #F6F6F4">() {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">Secured: Đây là trang quản trị (ADMIN ONLY).</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p class="wp-block-paragraph"><strong>4. RESULT: Kiểm thử hoạt động</strong></p>



<p class="wp-block-paragraph">Sử dụng <strong>Postman</strong> để demo luồng OAuth2.</p>



<p class="wp-block-paragraph">Bước 1: Lấy Token từ Keycloak (Login)<br>&#8211; <strong>URL:</strong> <code>http://localhost:8080/realms/springboot-demo/protocol/openid-connect/token</code><br>&#8211; <strong>Body (x-www-form-urlencoded):</strong> <br>   + <code>client_id</code>: <code>my-backend-api</code><br>   + <code>client_secret</code>: <em>(Lấy trong Keycloak: Clients -&gt; my-backend-api -&gt; Credentials)</em><br>   + <code>grant_type</code>: <code>password</code><br>   + <code>username</code>: <code>testuser</code><br>   + <code>password</code>: <code>123</code><br>&#8211; <strong>Kết quả:</strong> Bạn nhận được JSON chứa <code>access_token</code>. Copy chuỗi này.</p>



<p class="wp-block-paragraph">Bước 2: Gọi API bảo mật<br>&#8211; <strong>URL:</strong> <code>http://localhost:8081/user/profile</code><br>&#8211; <strong>Authorization Tab:</strong> Chọn type <strong>Bearer Token</strong> và dán access token vào.<br>&#8211; <strong>Kết quả:</strong><br>   + Nếu token hợp lệ: Trả về <code>Secured: Đây là trang dành cho User...</code> (Status 200).<br>   + Nếu không gửi token: <code>401 Unauthorized</code>.</p>



<p class="wp-block-paragraph"><strong>Kết luận:</strong> Bạn đã xây dựng thành công một hệ thống bảo mật hiện đại, tách biệt hoàn toàn Resource Server và Authorization Server, tuân thủ chuẩn OAuth2.</p>
<p>The post <a href="https://blog.tomosia.com.vn/keycloak-giai-phap-iam-toan-dien-cho-ung-dung-hien-dai/">Keycloak: Giải pháp IAM toàn diện cho ứng dụng hiện đại</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/keycloak-giai-phap-iam-toan-dien-cho-ung-dung-hien-dai/feed/</wfw:commentRss>
			<slash:comments>20</slash:comments>
		
		
			</item>
		<item>
		<title>Helidon là gì ? Hướng dẫn chuyển đổi các dự án Spring Boot sang Helidon bằng AI</title>
		<link>https://blog.tomosia.com.vn/helidon-la-gi-huong-dan-chuyen-doi-cac-du-an-spring-boot-sang-helidon-bang-ai/</link>
					<comments>https://blog.tomosia.com.vn/helidon-la-gi-huong-dan-chuyen-doi-cac-du-an-spring-boot-sang-helidon-bang-ai/#comments</comments>
		
		<dc:creator><![CDATA[Nguyen Minh]]></dc:creator>
		<pubDate>Mon, 08 Dec 2025 01:33:33 +0000</pubDate>
				<category><![CDATA[Java]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=3486</guid>

					<description><![CDATA[<p>Trong thế giới phát triển microservices bằng Java, Spring Boot là lựa chọn phổ biến nhờ tính dễ&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/helidon-la-gi-huong-dan-chuyen-doi-cac-du-an-spring-boot-sang-helidon-bang-ai/">Helidon là gì ? Hướng dẫn chuyển đổi các dự án Spring Boot sang Helidon bằng AI</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Trong thế giới phát triển microservices bằng Java, Spring Boot là lựa chọn phổ biến nhờ tính dễ sử dụng và hệ sinh thái phong phú. Tuy nhiên, khi cần tối ưu hiệu suất, footprint nhỏ, và khả năng khởi động nhanh, <strong>Helidon</strong> xuất hiện như một framework nhẹ, hiện đại, được thiết kế đặc biệt cho cloud-native microservices.<br><br>Trong bài viết này, tôi sẽ giải thích Helidon là gì, điểm mạnh của nó, và hướng dẫn cách <strong>chuyển đổi một dự án Spring Boot sang Helidon</strong> bằng cách sử dụng AI, dựa theo kinh nghiệm triển khai thực tế.</p>



<h4 id="1-helidon-la-gi" class="wp-block-heading">1. Helidon là gì?</h4>



<h5 id="1-1-gioi-thieu-tong-quan" class="wp-block-heading">1.1. Giới thiệu tổng quan</h5>



<p class="wp-block-paragraph">Helidon là một bộ công cụ Java microservices do Oracle phát triển, tập trung vào hiệu suất, tính gọn nhẹ và khả năng triển khai linh hoạt. Helidon cho phép xây dựng ứng dụng cloud-native theo chuẩn Jakarta EE, MicroProfile hoặc theo kiểu lập trình phản ứng</p>



<h5 id="1-2-hai-flavor-cua-helidon-se-va-mp" class="wp-block-heading">1.2. Hai “flavor” của Helidon: SE và MP</h5>



<p class="wp-block-paragraph"><strong>Helidon SE</strong>: Lập trình thủ công theo style functional, phù hợp với ứng dụng hiệu suất cao, yêu cầu kiểm soát chi tiết.</p>



<p class="wp-block-paragraph"><strong>Helidon MP</strong>: Tuân theo chuẩn MicroProfile, tương thích quen thuộc với mô hình enterprise Java (JAX-RS, CDI, Metrics, Config). Đây là lựa chọn gần nhất với Spring Boot.</p>



<h5 id="1-3-nhung-diem-noi-bat" class="wp-block-heading">1.3. Những điểm nổi bật</h5>



<ul class="wp-block-list">
<li>Khởi động nhanh và sử dụng ít tài nguyên.</li>



<li>Thiết kế dành cho microservices và cloud-native.</li>



<li>Tích hợp tốt các tiêu chuẩn Jakarta &amp; MicroProfile.</li>



<li>Cấu hình đơn giản, dễ triển khai.</li>
</ul>



<h5 id="1-4-moi-truong-va-kha-nang-tich-hop" class="wp-block-heading">1.4. Môi trường và khả năng tích hợp</h5>



<p class="wp-block-paragraph">Helidon hoạt động tốt trong môi trường container, Kubernetes, hỗ trợ OpenTelemetry, GraalVM và dễ kết nối với các dịch vụ bên ngoài như databases, messaging hoặc service mesh.</p>



<h4 id="2-tai-sao-nen-can-nhac-chuyen-tu-spring-boot-sang-helidon" class="wp-block-heading">2. Tại sao nên cân nhắc chuyển từ Spring Boot sang Helidon</h4>



<h5 id="2-1-hieu-suat-va-footprint" class="wp-block-heading">2.1. Hiệu suất và footprint</h5>



<p class="wp-block-paragraph">Helidon có footprint nhỏ hơn Spring Boot và thời gian khởi động nhanh, phù hợp cho scaling tự động và serverless.</p>



<h5 id="2-2-microservices-va-kha-nang-quan-sat" class="wp-block-heading">2.2. Microservices và khả năng quan sát</h5>



<p class="wp-block-paragraph">MicroProfile mang theo sẵn metrics, health check, config và fault tolerance, giúp theo dõi và vận hành dịch vụ rõ ràng hơn.</p>



<h5 id="2-3-native-image-voi-graalvm" class="wp-block-heading">2.3. Native-image với GraalVM</h5>



<p class="wp-block-paragraph">Helidon hỗ trợ native-image tốt, mang lại tốc độ khởi động gần tức thì và giảm tối đa tiêu thụ CPU/RAM.</p>



<h5 id="2-4-loi-the-khi-dung-ai" class="wp-block-heading">2.4. Lợi thế khi dùng AI</h5>



<p class="wp-block-paragraph">AI giúp tự động chuyển đổi mã, giảm sai sót, tăng tốc độ refactor và đồng bộ hóa kiến trúc từ Spring Boot sang Helidon.</p>



<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1024" height="1024" data-id="3644" src="https://blog.tomosia.com.vn/wp-content/uploads/2025/11/image-26.png" alt="" class="wp-image-3644" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2025/11/image-26.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2025/11/image-26-300x300.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2025/11/image-26-150x150.png 150w, https://blog.tomosia.com.vn/wp-content/uploads/2025/11/image-26-768x768.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2025/11/image-26-80x80.png 80w, https://blog.tomosia.com.vn/wp-content/uploads/2025/11/image-26-380x380.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2025/11/image-26-800x800.png 800w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</figure>



<p class="wp-block-paragraph"><em>Nguồn :</em> <a href=" https://medium.com/oracledevs/framework-choice-strategy-when-to-use-spring-boot-vs-helidon-in-cloud-native-microservices-ab6e288b9821">https://medium.com/oracledevs/framework-choice-strategy-when-to-use-spring-boot-vs-helidon-in-cloud-native-microservices-ab6e288b9821</a></p>



<h4 id="3-chuyen-doi-spring-boot-sang-helidon-bang-ai" class="wp-block-heading">3 Chuyển đổi Spring Boot sang Helidon bằng AI</h4>



<h5 id="3-1-tao-workspace-chuyen-doi" class="wp-block-heading">3.1 Tạo Workspace chuyển đổi</h5>



<p class="wp-block-paragraph">Mở Terminal và chạy:</p>



<pre class="wp-block-code"><code>mkdir spring-to-helidon
cd spring-to-helidon</code></pre>



<p class="wp-block-paragraph"><em><strong>Thư mục này sẽ chứa toàn bộ converter và output chuyển đổi.</strong></em></p>



<h5 id="3-2-clone-du-an-spring-boot-mau" class="wp-block-heading">3.2 Clone  dự án spring boot mẫu</h5>



<p class="wp-block-paragraph">Chúng ta sử dụng Spring Petclinic để làm mẫu (bạn có thể thay bằng dự án của bạn).</p>



<pre class="wp-block-code"><code>git clone https://github.com/spring-projects/spring-petclinic.git</code></pre>



<p class="wp-block-paragraph">Cấu trúc được tạo:</p>



<pre class="wp-block-code"><code>spring-to-helidon/
 └── spring-petclinic/</code></pre>



<h5 id="3-3-tao-bo-converter-ai" class="wp-block-heading">3.3 Tạo bộ CONVERTER AI</h5>



<pre class="wp-block-code"><code>mkdir converter
cd converter
mkdir prompts
mkdir output</code></pre>



<p class="wp-block-paragraph">Trong đó :</p>



<ul class="wp-block-list">
<li>prompts/  → chứa các prompt hướng dẫn AI convert</li>



<li>output/ → nơi lưu file đã chuyển đổi</li>
</ul>



<h5 id="3-4-tao-prompt-chuyen-doi-chuyen-biet-cho-tung-loai-file" class="wp-block-heading">3.4 Tạo prompt chuyển đổi chuyên biệt cho từng loại File</h5>



<p class="wp-block-paragraph"><em>Mỗi loại file cần một hướng dẫn riêng để AI convert chính xác.</em></p>



<h6 id="3-4-1-prompt-cho-controller" class="wp-block-heading">3.4.1 Prompt cho Controller</h6>



<pre class="wp-block-code has-f-6-f-6-f-4-color has-text-color has-875-rem-font-size"><code>nano prompts/controller.txt</code></pre>



<pre class="wp-block-code has-f-6-f-6-f-4-color has-text-color has-875-rem-font-size"><code>You are converting a Spring Boot REST Controller to a Helidon MP JAX-RS resource.

Rules:
- Replace @RestController with @Path and @ApplicationScoped.
- Replace @GetMapping, @PostMapping, @PutMapping, @DeleteMapping with @GET, @POST, @PUT, @DELETE.
- Replace @Autowired with @Inject (CDI).
- Remove all Spring imports.
- Keep logic unchanged.
- Output FULL source file.</code></pre>



<h6 id="3-4-2-prompt-cho-service" class="wp-block-heading">3.4.2 Prompt cho Service</h6>



<pre class="wp-block-code has-f-6-f-6-f-4-color has-text-color has-875-rem-font-size"><code>nano prompts/service.txt</code></pre>



<pre class="wp-block-code has-f-6-f-6-f-4-color has-text-color has-875-rem-font-size"><code>Convert Spring Service into CDI bean for Helidon MP.

Rules:
- Replace @Service with @ApplicationScoped.
- Replace @Autowired with @Inject.
- Remove Spring imports.
- Output full file.</code></pre>



<h6 id="3-4-3-prompt-cho-repository" class="wp-block-heading">3.4.3 Prompt cho Repository </h6>



<pre class="wp-block-code has-f-6-f-6-f-4-color has-text-color has-875-rem-font-size"><code>nano prompts/repo.txt</code></pre>



<pre class="wp-block-code has-f-6-f-6-f-4-color has-text-color has-875-rem-font-size"><code>Convert a Spring Data repository to a Helidon MP-compatible JPA repository.

Rules:
- Remove Spring Data interfaces.
- Replace query methods (findBy…) with JPQL.
- Use @ApplicationScoped and @Inject.
- Output full file.</code></pre>



<h6 id="3-4-4-prompt-cho-entity" class="wp-block-heading">3.4.4 <strong>Prompt cho Entity</strong></h6>



<pre class="wp-block-code has-f-6-f-6-f-4-color has-text-color has-875-rem-font-size"><code>nano prompts/entity.txt</code></pre>



<pre class="wp-block-code has-f-6-f-6-f-4-color has-text-color has-875-rem-font-size"><code>Convert a Spring JPA Entity into Helidon MP Entity using Jakarta Persistence.

Rules:
- Keep @Entity, @Id, @GeneratedValue.
- Remove Lombok annotations.
- Replace org.springframework imports with jakarta.
- Output full file.</code></pre>



<h6 id="3-4-5-prompt-cho-pom-xml" class="wp-block-heading">3.4.5 Prompt cho pom.xml</h6>



<pre class="wp-block-code has-f-6-f-6-f-4-color has-text-color has-875-rem-font-size"><code>nano prompts/pom.txt</code></pre>



<pre class="wp-block-code"><code>Convert Spring Boot pom.xml to Helidon MP pom.xml.

Replace:
- spring-boot-starter-* with helidon-microprofile modules
- Use jakarta dependencies
Return a valid Maven pom.xml</code></pre>



<h5 id="3-5-tao-script-phan-loai-file-classifier" class="wp-block-heading">3.5 Tạo script phân loại file (classifier)</h5>



<p class="wp-block-paragraph"><strong><em>AI</em></strong> cần biết:<br>→ file nào là Controller?<br>→ Service?<br>→ Repository?<br>→ Entity?</p>



<h6 id="3-5-1-tao-file-classify-py" class="wp-block-heading">3.5.1 Tạo file classify.py</h6>



<pre class="wp-block-code"><code>nano classify.py</code></pre>



<p class="wp-block-paragraph">Code: </p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Python</span><span role="button" tabindex="0" data-code="import os

def classify(path):
    text = open(path).read()

    if path.endswith(&quot;pom.xml&quot;):
        return &quot;pom&quot;

    if &quot;@RestController&quot; in text:
        return &quot;controller&quot;

    if &quot;JpaRepository&quot; in text or &quot;CrudRepository&quot; in text:
        return &quot;repo&quot;

    if &quot;@Service&quot; in text or &quot;@Component&quot; in text:
        return &quot;service&quot;

    if &quot;@Entity&quot; in text:
        return &quot;entity&quot;

    return &quot;java&quot;

if __name__ == &quot;__main__&quot;:
    base_dir = &quot;../spring-petclinic&quot;
    
    for root, dirs, files in os.walk(base_dir):
        for file in files:
            if file.endswith(&quot;.java&quot;) or file.endswith(&quot;pom.xml&quot;):
                path = os.path.join(root, file)
                ftype = classify(path)
                print(f&quot;{path}:{ftype}&quot;)
" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> os</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">def</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">classify</span><span style="color: #F6F6F4">(</span><span style="color: #FFB86C; font-style: italic">path</span><span style="color: #F6F6F4">):</span></span>
<span class="line"><span style="color: #F6F6F4">    text </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">open</span><span style="color: #F6F6F4">(path).read()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> path.endswith(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">pom.xml</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">):</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">pom</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">@RestController</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">in</span><span style="color: #F6F6F4"> text:</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">controller</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">JpaRepository</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">in</span><span style="color: #F6F6F4"> text </span><span style="color: #F286C4">or</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">CrudRepository</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">in</span><span style="color: #F6F6F4"> text:</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">repo</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">@Service</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">in</span><span style="color: #F6F6F4"> text </span><span style="color: #F286C4">or</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">@Component</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">in</span><span style="color: #F6F6F4"> text:</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">service</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">@Entity</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">in</span><span style="color: #F6F6F4"> text:</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">entity</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">java</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">__name__</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">==</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">__main__</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">    base_dir </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">../spring-petclinic</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">for</span><span style="color: #F6F6F4"> root, dirs, files </span><span style="color: #F286C4">in</span><span style="color: #F6F6F4"> os.walk(base_dir):</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">for</span><span style="color: #F6F6F4"> file </span><span style="color: #F286C4">in</span><span style="color: #F6F6F4"> files:</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> file.endswith(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">.java</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">) </span><span style="color: #F286C4">or</span><span style="color: #F6F6F4"> file.endswith(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">pom.xml</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">):</span></span>
<span class="line"><span style="color: #F6F6F4">                path </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> os.path.join(root, file)</span></span>
<span class="line"><span style="color: #F6F6F4">                ftype </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> classify(path)</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">path</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">:</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">ftype</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">Chạy để test:</p>



<pre class="wp-block-code"><code>python3 classify.py</code></pre>



<h5 id="3-6-script-goi-openai-va-chuyen-doi-code" class="wp-block-heading">3.6 Script gọi OpenAI và chuyển đổi code </h5>



<h6 id="3-6-1-export-key" class="wp-block-heading">3.6.1 Export key</h6>



<p class="wp-block-paragraph">Sau đó: </p>



<pre class="wp-block-code"><code>export OPENAI_API_KEY="YOUR_API_KEY"</code></pre>



<h6 id="3-6-2-tao-convert-py" class="wp-block-heading">3.6.2 Tạo convert.py</h6>



<pre class="wp-block-code"><code>nano convert.py</code></pre>



<p class="wp-block-paragraph">Code: </p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:25.2890625px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Python</span><span role="button" tabindex="0" data-code="import os
from openai import OpenAI
from concurrent.futures import ThreadPoolExecutor, as_completed
from threading import Lock
import time
from datetime import datetime

# Support multiple API providers via environment variables
# Default to Ollama if available, otherwise use DeepSeek
api_key = (os.getenv(&quot;OPENAI_API_KEY&quot;) or &quot;&quot;).strip()
base_url = (os.getenv(&quot;OPENAI_BASE_URL&quot;) or &quot;&quot;).strip()
model_name = os.getenv(&quot;MODEL_NAME&quot;, &quot;&quot;).strip()

# Auto-detect Ollama if not explicitly set
if not base_url:
    # Check if Ollama is running
    import socket
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(1)
        result = sock.connect_ex(('localhost', 11434))
        sock.close()
        if result == 0:
            # Ollama is running, use it as default
            base_url = &quot;http://localhost:11434/v1&quot;
            model_name = model_name or &quot;llama3&quot;
            api_key = api_key or &quot;ollama&quot;
            print(&quot;ℹ Auto-detected Ollama, using it as default&quot;)
    except:
        pass
    
    # Fallback to DeepSeek if Ollama not available
    if not base_url:
        base_url = &quot;https://api.deepseek.com&quot;
        model_name = model_name or &quot;deepseek-chat&quot;
        if not api_key:
            print(&quot;⚠ Warning: Using DeepSeek default. Set OPENAI_API_KEY for Ollama or other providers.&quot;)

if not api_key:
    raise ValueError(&quot;OPENAI_API_KEY environment variable is not set&quot;)

client = OpenAI(
    api_key=api_key,
    base_url=base_url
)

PROMPTS = {
    &quot;controller&quot;: &quot;prompts/controller.txt&quot;,
    &quot;repo&quot;: &quot;prompts/repo.txt&quot;,
    &quot;service&quot;: &quot;prompts/service.txt&quot;,
    &quot;entity&quot;: &quot;prompts/entity.txt&quot;,
    &quot;pom&quot;: &quot;prompts/pom.txt&quot;,
    &quot;java&quot;: &quot;prompts/entity.txt&quot;  # fallback
}

# Thread-safe counters for progress tracking
success_lock = Lock()
error_lock = Lock()
progress_lock = Lock()
success_count = 0
error_count = 0
completed_count = 0
start_time = None

def print_progress(completed, total, current_file=None):
    &quot;&quot;&quot;Print progress with percentage and time estimate&quot;&quot;&quot;
    global start_time
    if total == 0:
        return
    
    percentage = (completed / total) * 100
    elapsed = time.time() - start_time if start_time else 0
    
    if completed &gt; 0 and elapsed &gt; 0:
        avg_time = elapsed / completed
        remaining = total - completed
        eta_seconds = avg_time * remaining
        eta_str = f&quot;ETA: {int(eta_seconds//60)}m {int(eta_seconds%60)}s&quot;
    else:
        eta_str = &quot;Calculating...&quot;
    
    elapsed_str = f&quot;{int(elapsed//60)}m {int(elapsed%60)}s&quot;
    
    bar_length = 30
    filled = int(bar_length * completed / total)
    bar = &quot;█&quot; * filled + &quot;░&quot; * (bar_length - filled)
    
    status = f&quot;[{bar}] {completed}/{total} ({percentage:.1f}%) | {elapsed_str} elapsed | {eta_str}&quot;
    if current_file:
        filename = os.path.basename(current_file)
        status += f&quot; | Processing: {filename}&quot;
    
    print(f&quot;\r{status}&quot;, end=&quot;&quot;, flush=True)


def convert(path, ftype, max_retries=3):
    file_start_time = time.time()
    prompt = open(PROMPTS[ftype]).read()
    code = open(path).read()
    
    # Calculate dynamic timeout based on file size
    # Base timeout: 60s, add 2s per KB of code
    code_size_kb = len(code) / 1024
    base_timeout = 60
    dynamic_timeout = int(base_timeout + (code_size_kb * 2))
    # Cap at 10 minutes for very large files
    timeout = min(dynamic_timeout, 600)

    messages = [
        {&quot;role&quot;: &quot;system&quot;, &quot;content&quot;: prompt},
        {&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: code},
    ]

    last_error = None
    for attempt in range(max_retries):
        try:
            # Add small delay between retries
            if attempt &gt; 0:
                wait_time = min(2 ** attempt, 10)  # Exponential backoff, max 10s
                time.sleep(wait_time)
                print(f&quot;  ↻ Retry {attempt}/{max_retries-1} for {os.path.basename(path)}...&quot;)
            
            # Call API (supports multiple providers)
            result = client.chat.completions.create(
                model=model_name,
                temperature=0,
                messages=messages,
                timeout=timeout
            )

            output = result.choices[0].message.content

            # Tạo file output theo cấu trúc map
            out_path = &quot;output/&quot; + path.replace(&quot;../spring-petclinic/&quot;, &quot;&quot;)
            os.makedirs(os.path.dirname(out_path), exist_ok=True)
            with open(out_path, &quot;w&quot;) as f:
                f.write(output)

            elapsed = time.time() - file_start_time
            return True, elapsed
        except Exception as e:
            last_error = e
            error_msg = str(e)
            # If it's not a timeout, don't retry
            if &quot;timeout&quot; not in error_msg.lower() and &quot;timed out&quot; not in error_msg.lower():
                elapsed = time.time() - file_start_time
                return False, elapsed, error_msg
            # For timeout, continue to retry
    
    # All retries failed
    elapsed = time.time() - file_start_time
    return False, elapsed, str(last_error)


def convert_with_counter(path, ftype, total, skip_existing=True):
    global success_count, error_count, completed_count
    
    # Check if output file already exists
    if skip_existing:
        out_path = &quot;output/&quot; + path.replace(&quot;../spring-petclinic/&quot;, &quot;&quot;)
        if os.path.exists(out_path) and os.path.getsize(out_path) &gt; 0:
            with progress_lock:
                completed_count += 1
                success_count += 1
                filename = os.path.basename(path)
                print(f&quot;\n⊘ [{completed_count}/{total}] {filename} (skipped - already exists)&quot;)
                print_progress(completed_count, total)
            return True
    
    result_data = convert(path, ftype)
    
    if len(result_data) == 2:
        success, elapsed = result_data
        error_msg = None
    else:
        success, elapsed, error_msg = result_data
    
    with progress_lock:
        completed_count += 1
        
        if success:
            success_count += 1
            status = &quot;✔&quot;
        else:
            with error_lock:
                error_count += 1
            status = &quot;✗&quot;
        
        # Print detailed result on new line
        filename = os.path.basename(path)
        if success:
            print(f&quot;\n{status} [{completed_count}/{total}] {filename} ({elapsed:.1f}s)&quot;)
        else:
            if error_msg:
                if &quot;402&quot; in error_msg or &quot;Insufficient Balance&quot; in error_msg:
                    print(f&quot;\n{status} [{completed_count}/{total}] {filename} - Insufficient Balance&quot;)
                elif &quot;401&quot; in error_msg or &quot;Unauthorized&quot; in error_msg:
                    print(f&quot;\n{status} [{completed_count}/{total}] {filename} - Invalid API key&quot;)
                elif &quot;timeout&quot; in error_msg.lower():
                    print(f&quot;\n{status} [{completed_count}/{total}] {filename} - Timeout ({elapsed:.1f}s)&quot;)
                else:
                    print(f&quot;\n{status} [{completed_count}/{total}] {filename} - Error: {error_msg[:50]}&quot;)
            else:
                print(f&quot;\n{status} [{completed_count}/{total}] {filename} - Failed&quot;)
        
        # Update progress bar
        print_progress(completed_count, total)
    
    return success


# Cho phép chạy convert thủ công
if __name__ == &quot;__main__&quot;:
    import sys
    
    print(f&quot;Using API: {base_url}&quot;)
    print(f&quot;Using model: {model_name}&quot;)
    print()
    
    if len(sys.argv) == 3:
        result_data = convert(sys.argv[1], sys.argv[2])
        if len(result_data) == 2:
            success, elapsed = result_data
            if success:
                print(f&quot;✔ Converted in {elapsed:.1f}s&quot;)
            else:
                print(f&quot;✗ Failed in {elapsed:.1f}s&quot;)
        else:
            success, elapsed, error_msg = result_data
            print(f&quot;✗ Error: {error_msg}&quot;)
    else:
        success_count = 0
        error_count = 0
        completed_count = 0
        start_time = time.time()
        
        # Parse file list
        tasks = []
        with open(&quot;file-list.txt&quot;, &quot;r&quot;) as f:
            for line in f:
                line = line.strip()
                if not line:
                    continue
                # Support both formats: &quot;path:type&quot; and &quot;path type&quot;
                if &quot;:&quot; in line:
                    parts = line.split(&quot;:&quot;, 1)
                else:
                    parts = line.rsplit(None, 1)  # Split on last whitespace
                
                if len(parts) == 2:
                    path, ftype = parts
                    path = path.strip()
                    ftype = ftype.strip()
                    tasks.append((path, ftype))
                else:
                    print(f&quot;⚠ Skipped invalid line: {line[:50]}...&quot;)
        
        total = len(tasks)
        # Reduce default workers to avoid overwhelming Ollama
        # Ollama can be slow with too many concurrent requests
        max_workers = int(os.getenv(&quot;MAX_WORKERS&quot;, &quot;2&quot;))
        
        print(f&quot;Found {total} files to convert&quot;)
        print(f&quot;Using {max_workers} parallel workers (reduce if getting timeouts)&quot;)
        print(f&quot;Timeout: 60s base + 2s per KB (max 10min)&quot;)
        print(f&quot;Retries: 3 attempts per file&quot;)
        print(f&quot;Started at {datetime.now().strftime('%H:%M:%S')}\n&quot;)
        
        # Initialize progress bar
        print_progress(0, total)
        
        # Use ThreadPoolExecutor for parallel processing
        # Add small delay between submissions to avoid overwhelming the API
        with ThreadPoolExecutor(max_workers=max_workers) as executor:
            futures = []
            for idx, (path, ftype) in enumerate(tasks):
                future = executor.submit(convert_with_counter, path, ftype, total)
                futures.append(future)
                # Small delay to avoid overwhelming Ollama
                if idx &lt; len(tasks) - 1:
                    time.sleep(0.5)
            
            # Wait for all tasks to complete
            for future in as_completed(futures):
                try:
                    future.result()
                except Exception as e:
                    with progress_lock:
                        completed_count += 1
                        error_count += 1
                        print(f&quot;\n✗ Unexpected error: {e}&quot;)
                        print_progress(completed_count, total)
        
        # Final summary
        total_time = time.time() - start_time
        print(f&quot;\n\n{'='*60}&quot;)
        print(f&quot;Summary:&quot;)
        print(f&quot;  ✓ Succeeded: {success_count}&quot;)
        print(f&quot;  ✗ Failed:    {error_count}&quot;)
        print(f&quot;  ⏱ Total time: {int(total_time//60)}m {int(total_time%60)}s&quot;)
        if success_count &gt; 0:
            avg_time = total_time / success_count
            print(f&quot;  📊 Avg time/file: {avg_time:.1f}s&quot;)
        print(f&quot;{'='*60}&quot;)

" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> os</span></span>
<span class="line"><span style="color: #F286C4">from</span><span style="color: #F6F6F4"> openai </span><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> OpenAI</span></span>
<span class="line"><span style="color: #F286C4">from</span><span style="color: #F6F6F4"> concurrent.futures </span><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> ThreadPoolExecutor, as_completed</span></span>
<span class="line"><span style="color: #F286C4">from</span><span style="color: #F6F6F4"> threading </span><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> Lock</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> time</span></span>
<span class="line"><span style="color: #F286C4">from</span><span style="color: #F6F6F4"> datetime </span><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> datetime</span></span>
<span class="line"></span>
<span class="line"><span style="color: #7B7F8B"># Support multiple API providers via environment variables</span></span>
<span class="line"><span style="color: #7B7F8B"># Default to Ollama if available, otherwise use DeepSeek</span></span>
<span class="line"><span style="color: #F6F6F4">api_key </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> (os.getenv(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">OPENAI_API_KEY</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">) </span><span style="color: #F286C4">or</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;&quot;</span><span style="color: #F6F6F4">).strip()</span></span>
<span class="line"><span style="color: #F6F6F4">base_url </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> (os.getenv(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">OPENAI_BASE_URL</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">) </span><span style="color: #F286C4">or</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;&quot;</span><span style="color: #F6F6F4">).strip()</span></span>
<span class="line"><span style="color: #F6F6F4">model_name </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> os.getenv(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">MODEL_NAME</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;&quot;</span><span style="color: #F6F6F4">).strip()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #7B7F8B"># Auto-detect Ollama if not explicitly set</span></span>
<span class="line"><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">not</span><span style="color: #F6F6F4"> base_url:</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B"># Check if Ollama is running</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> socket</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">try</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">        sock </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> socket.socket(socket.</span><span style="color: #BF9EEE">AF_INET</span><span style="color: #F6F6F4">, socket.</span><span style="color: #BF9EEE">SOCK_STREAM</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        sock.settimeout(</span><span style="color: #BF9EEE">1</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        result </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> sock.connect_ex((</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">localhost</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">11434</span><span style="color: #F6F6F4">))</span></span>
<span class="line"><span style="color: #F6F6F4">        sock.close()</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> result </span><span style="color: #F286C4">==</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">0</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #7B7F8B"># Ollama is running, use it as default</span></span>
<span class="line"><span style="color: #F6F6F4">            base_url </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">http://localhost:11434/v1</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">            model_name </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> model_name </span><span style="color: #F286C4">or</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">llama3</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">            api_key </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> api_key </span><span style="color: #F286C4">or</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">ollama</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">ℹ Auto-detected Ollama, using it as default</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">except</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">pass</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B"># Fallback to DeepSeek if Ollama not available</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">not</span><span style="color: #F6F6F4"> base_url:</span></span>
<span class="line"><span style="color: #F6F6F4">        base_url </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">https://api.deepseek.com</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">        model_name </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> model_name </span><span style="color: #F286C4">or</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">deepseek-chat</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">not</span><span style="color: #F6F6F4"> api_key:</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">⚠ Warning: Using DeepSeek default. Set OPENAI_API_KEY for Ollama or other providers.</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">not</span><span style="color: #F6F6F4"> api_key:</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">raise</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">ValueError</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">OPENAI_API_KEY environment variable is not set</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">client </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> OpenAI(</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #FFB86C; font-style: italic">api_key</span><span style="color: #F286C4">=</span><span style="color: #F6F6F4">api_key,</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #FFB86C; font-style: italic">base_url</span><span style="color: #F286C4">=</span><span style="color: #F6F6F4">base_url</span></span>
<span class="line"><span style="color: #F6F6F4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #BF9EEE">PROMPTS</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">controller</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">: </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">prompts/controller.txt</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">repo</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">: </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">prompts/repo.txt</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">service</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">: </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">prompts/service.txt</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">entity</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">: </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">prompts/entity.txt</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">pom</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">: </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">prompts/pom.txt</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">java</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">: </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">prompts/entity.txt</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">  </span><span style="color: #7B7F8B"># fallback</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #7B7F8B"># Thread-safe counters for progress tracking</span></span>
<span class="line"><span style="color: #F6F6F4">success_lock </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> Lock()</span></span>
<span class="line"><span style="color: #F6F6F4">error_lock </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> Lock()</span></span>
<span class="line"><span style="color: #F6F6F4">progress_lock </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> Lock()</span></span>
<span class="line"><span style="color: #F6F6F4">success_count </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">0</span></span>
<span class="line"><span style="color: #F6F6F4">error_count </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">0</span></span>
<span class="line"><span style="color: #F6F6F4">completed_count </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">0</span></span>
<span class="line"><span style="color: #F6F6F4">start_time </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">None</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">def</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">print_progress</span><span style="color: #F6F6F4">(</span><span style="color: #FFB86C; font-style: italic">completed</span><span style="color: #F6F6F4">, </span><span style="color: #FFB86C; font-style: italic">total</span><span style="color: #F6F6F4">, </span><span style="color: #FFB86C; font-style: italic">current_file</span><span style="color: #F286C4">=</span><span style="color: #BF9EEE">None</span><span style="color: #F6F6F4">):</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">&quot;&quot;&quot;Print progress with percentage and time estimate&quot;&quot;&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">global</span><span style="color: #F6F6F4"> start_time</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> total </span><span style="color: #F286C4">==</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">0</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    percentage </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> (completed </span><span style="color: #F286C4">/</span><span style="color: #F6F6F4"> total) </span><span style="color: #F286C4">*</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">100</span></span>
<span class="line"><span style="color: #F6F6F4">    elapsed </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> time.time() </span><span style="color: #F286C4">-</span><span style="color: #F6F6F4"> start_time </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> start_time </span><span style="color: #F286C4">else</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">0</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> completed </span><span style="color: #F286C4">&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">0</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">and</span><span style="color: #F6F6F4"> elapsed </span><span style="color: #F286C4">&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">0</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">        avg_time </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> elapsed </span><span style="color: #F286C4">/</span><span style="color: #F6F6F4"> completed</span></span>
<span class="line"><span style="color: #F6F6F4">        remaining </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> total </span><span style="color: #F286C4">-</span><span style="color: #F6F6F4"> completed</span></span>
<span class="line"><span style="color: #F6F6F4">        eta_seconds </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> avg_time </span><span style="color: #F286C4">*</span><span style="color: #F6F6F4"> remaining</span></span>
<span class="line"><span style="color: #F6F6F4">        eta_str </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;ETA: </span><span style="color: #BF9EEE">{</span><span style="color: #97E1F1; font-style: italic">int</span><span style="color: #F6F6F4">(eta_seconds</span><span style="color: #F286C4">//</span><span style="color: #BF9EEE">60</span><span style="color: #F6F6F4">)</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">m </span><span style="color: #BF9EEE">{</span><span style="color: #97E1F1; font-style: italic">int</span><span style="color: #F6F6F4">(eta_seconds</span><span style="color: #F286C4">%</span><span style="color: #BF9EEE">60</span><span style="color: #F6F6F4">)</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">s&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">else</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">        eta_str </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">Calculating...</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    elapsed_str </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;</span><span style="color: #BF9EEE">{</span><span style="color: #97E1F1; font-style: italic">int</span><span style="color: #F6F6F4">(elapsed</span><span style="color: #F286C4">//</span><span style="color: #BF9EEE">60</span><span style="color: #F6F6F4">)</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">m </span><span style="color: #BF9EEE">{</span><span style="color: #97E1F1; font-style: italic">int</span><span style="color: #F6F6F4">(elapsed</span><span style="color: #F286C4">%</span><span style="color: #BF9EEE">60</span><span style="color: #F6F6F4">)</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">s&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    bar_length </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">30</span></span>
<span class="line"><span style="color: #F6F6F4">    filled </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">int</span><span style="color: #F6F6F4">(bar_length </span><span style="color: #F286C4">*</span><span style="color: #F6F6F4"> completed </span><span style="color: #F286C4">/</span><span style="color: #F6F6F4"> total)</span></span>
<span class="line"><span style="color: #F6F6F4">    bar </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">█</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">*</span><span style="color: #F6F6F4"> filled </span><span style="color: #F286C4">+</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">░</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">*</span><span style="color: #F6F6F4"> (bar_length </span><span style="color: #F286C4">-</span><span style="color: #F6F6F4"> filled)</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    status </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;[</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">bar</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">] </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">completed</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">/</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">total</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98"> (</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">percentage</span><span style="color: #F286C4">:.1f</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">%) | </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">elapsed_str</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98"> elapsed | </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">eta_str</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> current_file:</span></span>
<span class="line"><span style="color: #F6F6F4">        filename </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> os.path.basename(current_file)</span></span>
<span class="line"><span style="color: #F6F6F4">        status </span><span style="color: #F286C4">+=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot; | Processing: </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">filename</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;</span><span style="color: #F286C4">\r</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">status</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">&quot;</span><span style="color: #F6F6F4">, </span><span style="color: #FFB86C; font-style: italic">end</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&quot;&quot;</span><span style="color: #F6F6F4">, </span><span style="color: #FFB86C; font-style: italic">flush</span><span style="color: #F286C4">=</span><span style="color: #BF9EEE">True</span><span style="color: #F6F6F4">)</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">def</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">convert</span><span style="color: #F6F6F4">(</span><span style="color: #FFB86C; font-style: italic">path</span><span style="color: #F6F6F4">, </span><span style="color: #FFB86C; font-style: italic">ftype</span><span style="color: #F6F6F4">, </span><span style="color: #FFB86C; font-style: italic">max_retries</span><span style="color: #F286C4">=</span><span style="color: #BF9EEE">3</span><span style="color: #F6F6F4">):</span></span>
<span class="line"><span style="color: #F6F6F4">    file_start_time </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> time.time()</span></span>
<span class="line"><span style="color: #F6F6F4">    prompt </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">open</span><span style="color: #F6F6F4">(</span><span style="color: #BF9EEE">PROMPTS</span><span style="color: #F6F6F4">[ftype]).read()</span></span>
<span class="line"><span style="color: #F6F6F4">    code </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">open</span><span style="color: #F6F6F4">(path).read()</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B"># Calculate dynamic timeout based on file size</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B"># Base timeout: 60s, add 2s per KB of code</span></span>
<span class="line"><span style="color: #F6F6F4">    code_size_kb </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">len</span><span style="color: #F6F6F4">(code) </span><span style="color: #F286C4">/</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">1024</span></span>
<span class="line"><span style="color: #F6F6F4">    base_timeout </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">60</span></span>
<span class="line"><span style="color: #F6F6F4">    dynamic_timeout </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">int</span><span style="color: #F6F6F4">(base_timeout </span><span style="color: #F286C4">+</span><span style="color: #F6F6F4"> (code_size_kb </span><span style="color: #F286C4">*</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">2</span><span style="color: #F6F6F4">))</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B"># Cap at 10 minutes for very large files</span></span>
<span class="line"><span style="color: #F6F6F4">    timeout </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">min</span><span style="color: #F6F6F4">(dynamic_timeout, </span><span style="color: #BF9EEE">600</span><span style="color: #F6F6F4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    messages </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> [</span></span>
<span class="line"><span style="color: #F6F6F4">        {</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">role</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">: </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">system</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">content</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">: prompt},</span></span>
<span class="line"><span style="color: #F6F6F4">        {</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">role</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">: </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">user</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">content</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">: code},</span></span>
<span class="line"><span style="color: #F6F6F4">    ]</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    last_error </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">None</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">for</span><span style="color: #F6F6F4"> attempt </span><span style="color: #F286C4">in</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">range</span><span style="color: #F6F6F4">(max_retries):</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">try</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #7B7F8B"># Add small delay between retries</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> attempt </span><span style="color: #F286C4">&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">0</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">                wait_time </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">min</span><span style="color: #F6F6F4">(</span><span style="color: #BF9EEE">2</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">**</span><span style="color: #F6F6F4"> attempt, </span><span style="color: #BF9EEE">10</span><span style="color: #F6F6F4">)  </span><span style="color: #7B7F8B"># Exponential backoff, max 10s</span></span>
<span class="line"><span style="color: #F6F6F4">                time.sleep(wait_time)</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;  ↻ Retry </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">attempt</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">/</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">max_retries</span><span style="color: #F286C4">-</span><span style="color: #BF9EEE">1}</span><span style="color: #E7EE98"> for </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">os.path.basename(path)</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">...&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">            </span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #7B7F8B"># Call API (supports multiple providers)</span></span>
<span class="line"><span style="color: #F6F6F4">            result </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> client.chat.completions.create(</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #FFB86C; font-style: italic">model</span><span style="color: #F286C4">=</span><span style="color: #F6F6F4">model_name,</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #FFB86C; font-style: italic">temperature</span><span style="color: #F286C4">=</span><span style="color: #BF9EEE">0</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #FFB86C; font-style: italic">messages</span><span style="color: #F286C4">=</span><span style="color: #F6F6F4">messages,</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #FFB86C; font-style: italic">timeout</span><span style="color: #F286C4">=</span><span style="color: #F6F6F4">timeout</span></span>
<span class="line"><span style="color: #F6F6F4">            )</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">            output </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> result.choices[</span><span style="color: #BF9EEE">0</span><span style="color: #F6F6F4">].message.content</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #7B7F8B"># Tạo file output theo cấu trúc map</span></span>
<span class="line"><span style="color: #F6F6F4">            out_path </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">output/</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">+</span><span style="color: #F6F6F4"> path.replace(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">../spring-petclinic/</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">            os.makedirs(os.path.dirname(out_path), </span><span style="color: #FFB86C; font-style: italic">exist_ok</span><span style="color: #F286C4">=</span><span style="color: #BF9EEE">True</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">with</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">open</span><span style="color: #F6F6F4">(out_path, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">w</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">) </span><span style="color: #F286C4">as</span><span style="color: #F6F6F4"> f:</span></span>
<span class="line"><span style="color: #F6F6F4">                f.write(output)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">            elapsed </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> time.time() </span><span style="color: #F286C4">-</span><span style="color: #F6F6F4"> file_start_time</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">True</span><span style="color: #F6F6F4">, elapsed</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">except</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Exception</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">as</span><span style="color: #F6F6F4"> e:</span></span>
<span class="line"><span style="color: #F6F6F4">            last_error </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> e</span></span>
<span class="line"><span style="color: #F6F6F4">            error_msg </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">str</span><span style="color: #F6F6F4">(e)</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #7B7F8B"># If it&#39;s not a timeout, don&#39;t retry</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">timeout</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">not</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">in</span><span style="color: #F6F6F4"> error_msg.lower() </span><span style="color: #F286C4">and</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">timed out</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">not</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">in</span><span style="color: #F6F6F4"> error_msg.lower():</span></span>
<span class="line"><span style="color: #F6F6F4">                elapsed </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> time.time() </span><span style="color: #F286C4">-</span><span style="color: #F6F6F4"> file_start_time</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">False</span><span style="color: #F6F6F4">, elapsed, error_msg</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #7B7F8B"># For timeout, continue to retry</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B"># All retries failed</span></span>
<span class="line"><span style="color: #F6F6F4">    elapsed </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> time.time() </span><span style="color: #F286C4">-</span><span style="color: #F6F6F4"> file_start_time</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">False</span><span style="color: #F6F6F4">, elapsed, </span><span style="color: #97E1F1; font-style: italic">str</span><span style="color: #F6F6F4">(last_error)</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">def</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">convert_with_counter</span><span style="color: #F6F6F4">(</span><span style="color: #FFB86C; font-style: italic">path</span><span style="color: #F6F6F4">, </span><span style="color: #FFB86C; font-style: italic">ftype</span><span style="color: #F6F6F4">, </span><span style="color: #FFB86C; font-style: italic">total</span><span style="color: #F6F6F4">, </span><span style="color: #FFB86C; font-style: italic">skip_existing</span><span style="color: #F286C4">=</span><span style="color: #BF9EEE">True</span><span style="color: #F6F6F4">):</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">global</span><span style="color: #F6F6F4"> success_count, error_count, completed_count</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B"># Check if output file already exists</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> skip_existing:</span></span>
<span class="line"><span style="color: #F6F6F4">        out_path </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">output/</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">+</span><span style="color: #F6F6F4"> path.replace(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">../spring-petclinic/</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> os.path.exists(out_path) </span><span style="color: #F286C4">and</span><span style="color: #F6F6F4"> os.path.getsize(out_path) </span><span style="color: #F286C4">&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">0</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">with</span><span style="color: #F6F6F4"> progress_lock:</span></span>
<span class="line"><span style="color: #F6F6F4">                completed_count </span><span style="color: #F286C4">+=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">1</span></span>
<span class="line"><span style="color: #F6F6F4">                success_count </span><span style="color: #F286C4">+=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">1</span></span>
<span class="line"><span style="color: #F6F6F4">                filename </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> os.path.basename(path)</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;</span><span style="color: #F286C4">\n</span><span style="color: #E7EE98">⊘ [</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">completed_count</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">/</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">total</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">] </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">filename</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98"> (skipped - already exists)&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">                print_progress(completed_count, total)</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">True</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    result_data </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> convert(path, ftype)</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">len</span><span style="color: #F6F6F4">(result_data) </span><span style="color: #F286C4">==</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">2</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">        success, elapsed </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> result_data</span></span>
<span class="line"><span style="color: #F6F6F4">        error_msg </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">None</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">else</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">        success, elapsed, error_msg </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> result_data</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">with</span><span style="color: #F6F6F4"> progress_lock:</span></span>
<span class="line"><span style="color: #F6F6F4">        completed_count </span><span style="color: #F286C4">+=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">1</span></span>
<span class="line"><span style="color: #F6F6F4">        </span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> success:</span></span>
<span class="line"><span style="color: #F6F6F4">            success_count </span><span style="color: #F286C4">+=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">1</span></span>
<span class="line"><span style="color: #F6F6F4">            status </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">✔</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">else</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">with</span><span style="color: #F6F6F4"> error_lock:</span></span>
<span class="line"><span style="color: #F6F6F4">                error_count </span><span style="color: #F286C4">+=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">1</span></span>
<span class="line"><span style="color: #F6F6F4">            status </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">✗</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">        </span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #7B7F8B"># Print detailed result on new line</span></span>
<span class="line"><span style="color: #F6F6F4">        filename </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> os.path.basename(path)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> success:</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;</span><span style="color: #F286C4">\n</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">status</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98"> [</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">completed_count</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">/</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">total</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">] </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">filename</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98"> (</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">elapsed</span><span style="color: #F286C4">:.1f</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">s)&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">else</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> error_msg:</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">402</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">in</span><span style="color: #F6F6F4"> error_msg </span><span style="color: #F286C4">or</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">Insufficient Balance</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">in</span><span style="color: #F6F6F4"> error_msg:</span></span>
<span class="line"><span style="color: #F6F6F4">                    </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;</span><span style="color: #F286C4">\n</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">status</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98"> [</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">completed_count</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">/</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">total</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">] </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">filename</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98"> - Insufficient Balance&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #F286C4">elif</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">401</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">in</span><span style="color: #F6F6F4"> error_msg </span><span style="color: #F286C4">or</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">Unauthorized</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">in</span><span style="color: #F6F6F4"> error_msg:</span></span>
<span class="line"><span style="color: #F6F6F4">                    </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;</span><span style="color: #F286C4">\n</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">status</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98"> [</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">completed_count</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">/</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">total</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">] </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">filename</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98"> - Invalid API key&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #F286C4">elif</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">timeout</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">in</span><span style="color: #F6F6F4"> error_msg.lower():</span></span>
<span class="line"><span style="color: #F6F6F4">                    </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;</span><span style="color: #F286C4">\n</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">status</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98"> [</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">completed_count</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">/</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">total</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">] </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">filename</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98"> - Timeout (</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">elapsed</span><span style="color: #F286C4">:.1f</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">s)&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #F286C4">else</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">                    </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;</span><span style="color: #F286C4">\n</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">status</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98"> [</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">completed_count</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">/</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">total</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">] </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">filename</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98"> - Error: </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">error_msg[</span><span style="color: #F286C4">:</span><span style="color: #BF9EEE">50</span><span style="color: #F6F6F4">]</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">else</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;</span><span style="color: #F286C4">\n</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">status</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98"> [</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">completed_count</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">/</span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">total</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">] </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">filename</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98"> - Failed&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #7B7F8B"># Update progress bar</span></span>
<span class="line"><span style="color: #F6F6F4">        print_progress(completed_count, total)</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> success</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #7B7F8B"># Cho phép chạy convert thủ công</span></span>
<span class="line"><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">__name__</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">==</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">__main__</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> sys</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;Using API: </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">base_url</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;Using model: </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">model_name</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">len</span><span style="color: #F6F6F4">(sys.argv) </span><span style="color: #F286C4">==</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">3</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">        result_data </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> convert(sys.argv[</span><span style="color: #BF9EEE">1</span><span style="color: #F6F6F4">], sys.argv[</span><span style="color: #BF9EEE">2</span><span style="color: #F6F6F4">])</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">len</span><span style="color: #F6F6F4">(result_data) </span><span style="color: #F286C4">==</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">2</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">            success, elapsed </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> result_data</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> success:</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;✔ Converted in </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">elapsed</span><span style="color: #F286C4">:.1f</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">s&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">else</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;✗ Failed in </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">elapsed</span><span style="color: #F286C4">:.1f</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">s&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">else</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">            success, elapsed, error_msg </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> result_data</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;✗ Error: </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">error_msg</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">else</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">        success_count </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">0</span></span>
<span class="line"><span style="color: #F6F6F4">        error_count </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">0</span></span>
<span class="line"><span style="color: #F6F6F4">        completed_count </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">0</span></span>
<span class="line"><span style="color: #F6F6F4">        start_time </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> time.time()</span></span>
<span class="line"><span style="color: #F6F6F4">        </span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #7B7F8B"># Parse file list</span></span>
<span class="line"><span style="color: #F6F6F4">        tasks </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> []</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">with</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">open</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">file-list.txt</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">r</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">) </span><span style="color: #F286C4">as</span><span style="color: #F6F6F4"> f:</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">for</span><span style="color: #F6F6F4"> line </span><span style="color: #F286C4">in</span><span style="color: #F6F6F4"> f:</span></span>
<span class="line"><span style="color: #F6F6F4">                line </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> line.strip()</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">not</span><span style="color: #F6F6F4"> line:</span></span>
<span class="line"><span style="color: #F6F6F4">                    </span><span style="color: #F286C4">continue</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #7B7F8B"># Support both formats: &quot;path:type&quot; and &quot;path type&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">:</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">in</span><span style="color: #F6F6F4"> line:</span></span>
<span class="line"><span style="color: #F6F6F4">                    parts </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> line.split(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">:</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">1</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #F286C4">else</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">                    parts </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> line.rsplit(</span><span style="color: #BF9EEE">None</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">1</span><span style="color: #F6F6F4">)  </span><span style="color: #7B7F8B"># Split on last whitespace</span></span>
<span class="line"><span style="color: #F6F6F4">                </span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">len</span><span style="color: #F6F6F4">(parts) </span><span style="color: #F286C4">==</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">2</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">                    path, ftype </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> parts</span></span>
<span class="line"><span style="color: #F6F6F4">                    path </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> path.strip()</span></span>
<span class="line"><span style="color: #F6F6F4">                    ftype </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> ftype.strip()</span></span>
<span class="line"><span style="color: #F6F6F4">                    tasks.append((path, ftype))</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #F286C4">else</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">                    </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;⚠ Skipped invalid line: </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">line[</span><span style="color: #F286C4">:</span><span style="color: #BF9EEE">50</span><span style="color: #F6F6F4">]</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">...&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span></span>
<span class="line"><span style="color: #F6F6F4">        total </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">len</span><span style="color: #F6F6F4">(tasks)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #7B7F8B"># Reduce default workers to avoid overwhelming Ollama</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #7B7F8B"># Ollama can be slow with too many concurrent requests</span></span>
<span class="line"><span style="color: #F6F6F4">        max_workers </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">int</span><span style="color: #F6F6F4">(os.getenv(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">MAX_WORKERS</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">2</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">))</span></span>
<span class="line"><span style="color: #F6F6F4">        </span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;Found </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">total</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98"> files to convert&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;Using </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">max_workers</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98"> parallel workers (reduce if getting timeouts)&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;Timeout: 60s base + 2s per KB (max 10min)&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;Retries: 3 attempts per file&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;Started at </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">datetime.now().strftime(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">%H:%M:%S</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">)</span><span style="color: #BF9EEE">}</span><span style="color: #F286C4">\n</span><span style="color: #E7EE98">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #7B7F8B"># Initialize progress bar</span></span>
<span class="line"><span style="color: #F6F6F4">        print_progress(</span><span style="color: #BF9EEE">0</span><span style="color: #F6F6F4">, total)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #7B7F8B"># Use ThreadPoolExecutor for parallel processing</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #7B7F8B"># Add small delay between submissions to avoid overwhelming the API</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">with</span><span style="color: #F6F6F4"> ThreadPoolExecutor(</span><span style="color: #FFB86C; font-style: italic">max_workers</span><span style="color: #F286C4">=</span><span style="color: #F6F6F4">max_workers) </span><span style="color: #F286C4">as</span><span style="color: #F6F6F4"> executor:</span></span>
<span class="line"><span style="color: #F6F6F4">            futures </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> []</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">for</span><span style="color: #F6F6F4"> idx, (path, ftype) </span><span style="color: #F286C4">in</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">enumerate</span><span style="color: #F6F6F4">(tasks):</span></span>
<span class="line"><span style="color: #F6F6F4">                future </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> executor.submit(convert_with_counter, path, ftype, total)</span></span>
<span class="line"><span style="color: #F6F6F4">                futures.append(future)</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #7B7F8B"># Small delay to avoid overwhelming Ollama</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> idx </span><span style="color: #F286C4">&lt;</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">len</span><span style="color: #F6F6F4">(tasks) </span><span style="color: #F286C4">-</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">1</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">                    time.sleep(</span><span style="color: #BF9EEE">0.5</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">            </span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #7B7F8B"># Wait for all tasks to complete</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">for</span><span style="color: #F6F6F4"> future </span><span style="color: #F286C4">in</span><span style="color: #F6F6F4"> as_completed(futures):</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #F286C4">try</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">                    future.result()</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #F286C4">except</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Exception</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">as</span><span style="color: #F6F6F4"> e:</span></span>
<span class="line"><span style="color: #F6F6F4">                    </span><span style="color: #F286C4">with</span><span style="color: #F6F6F4"> progress_lock:</span></span>
<span class="line"><span style="color: #F6F6F4">                        completed_count </span><span style="color: #F286C4">+=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">1</span></span>
<span class="line"><span style="color: #F6F6F4">                        error_count </span><span style="color: #F286C4">+=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">1</span></span>
<span class="line"><span style="color: #F6F6F4">                        </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;</span><span style="color: #F286C4">\n</span><span style="color: #E7EE98">✗ Unexpected error: </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">e</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">                        print_progress(completed_count, total)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #7B7F8B"># Final summary</span></span>
<span class="line"><span style="color: #F6F6F4">        total_time </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> time.time() </span><span style="color: #F286C4">-</span><span style="color: #F6F6F4"> start_time</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;</span><span style="color: #F286C4">\n\n</span><span style="color: #BF9EEE">{</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">=</span><span style="color: #DEE492">&#39;</span><span style="color: #F286C4">*</span><span style="color: #BF9EEE">60}</span><span style="color: #E7EE98">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;Summary:&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;  ✓ Succeeded: </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">success_count</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;  ✗ Failed:    </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">error_count</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;  ⏱ Total time: </span><span style="color: #BF9EEE">{</span><span style="color: #97E1F1; font-style: italic">int</span><span style="color: #F6F6F4">(total_time</span><span style="color: #F286C4">//</span><span style="color: #BF9EEE">60</span><span style="color: #F6F6F4">)</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">m </span><span style="color: #BF9EEE">{</span><span style="color: #97E1F1; font-style: italic">int</span><span style="color: #F6F6F4">(total_time</span><span style="color: #F286C4">%</span><span style="color: #BF9EEE">60</span><span style="color: #F6F6F4">)</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">s&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> success_count </span><span style="color: #F286C4">&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">0</span><span style="color: #F6F6F4">:</span></span>
<span class="line"><span style="color: #F6F6F4">            avg_time </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> total_time </span><span style="color: #F286C4">/</span><span style="color: #F6F6F4"> success_count</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;  📊 Avg time/file: </span><span style="color: #BF9EEE">{</span><span style="color: #F6F6F4">avg_time</span><span style="color: #F286C4">:.1f</span><span style="color: #BF9EEE">}</span><span style="color: #E7EE98">s&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">f</span><span style="color: #E7EE98">&quot;</span><span style="color: #BF9EEE">{</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">=</span><span style="color: #DEE492">&#39;</span><span style="color: #F286C4">*</span><span style="color: #BF9EEE">60}</span><span style="color: #E7EE98">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"></span>
<span class="line"></span></code></pre></div>



<h5 id="3-7-chay-chuyen-doi" class="wp-block-heading">3.7 Chạy chuyển đổi</h5>



<h6 id="3-7-1-xuat-danh-sach-file-can-convert" class="wp-block-heading">3.7.1 <strong>Xuất danh sách file cần convert</strong></h6>



<pre class="wp-block-code"><code>python3 classify.py &gt; file-list.txt</code></pre>



<h6 id="3-7-2-chay-convert-tu-dong-toan-bo-project" class="wp-block-heading">3.7.2 Chạy convert tự động toàn bộ project</h6>



<pre class="wp-block-code"><code>python3 convert.py</code></pre>



<p class="wp-block-paragraph">Kết quả sẽ được lưu ở :</p>



<pre class="wp-block-code"><code>converter/output/</code></pre>



<h5 id="3-8-tao-project-helidon-va-ghep-code" class="wp-block-heading">3.8 <strong><strong>Tạo project helidon và ghép code</strong><br></strong></h5>



<p class="wp-block-paragraph">Tạo Helidon MP project:</p>



<pre class="wp-block-code"><code>cd ..
mvn archetype:generate -DinteractiveMode=false \
  -DarchetypeGroupId=io.helidon.archetypes \
  -DarchetypeArtifactId=helidon-mp</code></pre>



<p class="wp-block-paragraph">Sau đó copy code đã chuyển đổi vào project Helidon.</p>



<pre class="wp-block-code"><code>mvn package
java -jar target/*.jar</code></pre>



<h5 id="3-9-ket-qua" class="wp-block-heading">3.9 Kết quả</h5>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="632" src="https://blog.tomosia.com.vn/wp-content/uploads/2025/12/image-1024x632.png" alt="" class="wp-image-3714" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2025/12/image-1024x632.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2025/12/image-300x185.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2025/12/image-768x474.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2025/12/image-1536x948.png 1536w, https://blog.tomosia.com.vn/wp-content/uploads/2025/12/image-2048x1264.png 2048w, https://blog.tomosia.com.vn/wp-content/uploads/2025/12/image-380x234.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2025/12/image-800x494.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2025/12/image-1160x716.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2025/12/image-scaled.png 2560w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h4 id="4-ket-luan" class="wp-block-heading">4. Kết luận</h4>



<p class="wp-block-paragraph">AI chắc chắn đang trở thành một phần cốt lõi trong cuộc sống của chúng ta, làm thay đổi cách chúng ta tiếp cận lập trình. Nhiệm vụ chuyển đổi một dự án từ framework này sang framework khác vốn phức tạp giờ đây đã trở nên dễ dàng hơn rất nhiều với sự trợ giúp của AI, giúp tiết kiệm thời gian và tài nguyên, đồng thời mang lại hiệu quả chi phí cao.</p>



<p class="wp-block-paragraph">Tuy nhiên, các bộ chuyển đổi này không chỉ giới hạn ở việc chuyển đổi Spring sang Helidon. Với bộ gợi ý phù hợp, chúng cũng có thể hỗ trợ việc chuyển đổi và di chuyển các dự án Java EE cũ dựa trên javax.* sang Jakarta EE hiện đại.</p>



<p class="wp-block-paragraph">Tôi dự định sẽ tiếp tục cải thiện các bộ chuyển đổi. Như thường lệ, tôi hoan nghênh phản hồi của bạn, dù là trong phần bình luận hay trên mạng xã hội.</p>



<p class="wp-block-paragraph">Dưới đây là các liên kết đến các dự án GitHub được đề cập trong bài viết này:</p>



<ul class="wp-block-list">
<li><a href="https://github.com/m0mus/spring-pets">Spring Pets</a> – Một dự án thử nghiệm được sử dụng để kiểm tra chuyển đổi</li>



<li><a href="https://github.com/m0mus/ai-converter-ctx">Bộ chuyển đổi theo ngữ cảnh</a></li>



<li><a href="https://github.com/m0mus/ai-converter-inc">Bộ chuyển đổi gia tăng</a></li>
</ul>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://blog.tomosia.com.vn/helidon-la-gi-huong-dan-chuyen-doi-cac-du-an-spring-boot-sang-helidon-bang-ai/">Helidon là gì ? Hướng dẫn chuyển đổi các dự án Spring Boot sang Helidon bằng AI</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/helidon-la-gi-huong-dan-chuyen-doi-cac-du-an-spring-boot-sang-helidon-bang-ai/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title>Re-platforming Java với AWS Serverless Java Container</title>
		<link>https://blog.tomosia.com.vn/re-platforming-java-aws-serverless-java-container/</link>
					<comments>https://blog.tomosia.com.vn/re-platforming-java-aws-serverless-java-container/#comments</comments>
		
		<dc:creator><![CDATA[phi lai]]></dc:creator>
		<pubDate>Thu, 23 Oct 2025 02:39:15 +0000</pubDate>
				<category><![CDATA[Java]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=3434</guid>

					<description><![CDATA[<p>1. Vì sao nên đưa Java lên serverless? Java đã là một trong những lựa chọn hàng đầu&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/re-platforming-java-aws-serverless-java-container/">Re-platforming Java với AWS Serverless Java Container</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph"></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="780" height="268" src="https://blog.tomosia.com.vn/wp-content/uploads/2025/10/serverless-java-container.png" alt="" class="wp-image-3437" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2025/10/serverless-java-container.png 780w, https://blog.tomosia.com.vn/wp-content/uploads/2025/10/serverless-java-container-300x103.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2025/10/serverless-java-container-768x264.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2025/10/serverless-java-container-380x131.png 380w" sizes="auto, (max-width: 780px) 100vw, 780px" /></figure>



<h3 id="1-vi-sao-nen-dua-java-len-serverless" class="wp-block-heading">1. Vì sao nên đưa Java lên serverless?</h3>



<p class="wp-block-paragraph">Java đã là một trong những lựa chọn hàng đầu cho ứng dụng doanh nghiệp trong hơn 25 năm, nhờ khả năng chạy đa nền tảng, hiệu suất ổn định và cộng đồng phát triển lớn. Tuy nhiên, khi nói đến kiến trúc <strong>serverless</strong> – đặc biệt với AWS Lambda – cách triển khai ứng dụng Java đã thay đổi đáng kể.</p>



<p class="wp-block-paragraph">Thay vì phải duy trì JVM và máy chủ hoạt động 24/7, ứng dụng của bạn chỉ chạy khi có sự kiện được kích hoạt. Hạ tầng tự động mở rộng, và bạn chỉ trả phí cho thời gian thực thi thực tế. Mô hình này giúp giảm chi phí vận hành, đơn giản hóa hạ tầng, đồng thời cho phép bạn tập trung vào phần logic ứng dụng.</p>



<h3 id="2-java-truyen-thong-so-voi-aws-lambda" class="wp-block-heading">2. Java truyền thống so với AWS Lambda</h3>



<p class="wp-block-paragraph">Ứng dụng Java truyền thống thường chạy trên các máy chủ ứng dụng như <em>Apache Tomcat</em>, <em>Jetty</em>, <em>JBoss</em> hoặc <em>WebLogic</em>. JVM luôn hoạt động, lắng nghe request HTTP, và xử lý luồng song song.</p>



<p class="wp-block-paragraph">Trong khi đó, <strong>AWS Lambda</strong> hoạt động theo mô hình hướng sự kiện (event-driven):</p>



<ul class="wp-block-list">
<li>Mỗi hàm chỉ xử lý một request hoặc event duy nhất.</li>



<li>Không cần máy chủ HTTP riêng.</li>



<li>Tự động mở rộng và dừng khi không có request.</li>



<li>Giới hạn thời gian thực thi tối đa 15 phút và 10 GB RAM.</li>
</ul>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="947" height="634" src="https://blog.tomosia.com.vn/wp-content/uploads/2025/10/image.png" alt="" class="wp-image-3438" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2025/10/image.png 947w, https://blog.tomosia.com.vn/wp-content/uploads/2025/10/image-300x201.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2025/10/image-768x514.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2025/10/image-380x254.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2025/10/image-800x536.png 800w" sizes="auto, (max-width: 947px) 100vw, 947px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="497" height="374" src="https://blog.tomosia.com.vn/wp-content/uploads/2025/10/image-1.png" alt="" class="wp-image-3439" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2025/10/image-1.png 497w, https://blog.tomosia.com.vn/wp-content/uploads/2025/10/image-1-300x225.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2025/10/image-1-380x286.png 380w" sizes="auto, (max-width: 497px) 100vw, 497px" /></figure>



<h3 id="3-aws-serverless-java-container-la-gi" class="wp-block-heading">3. AWS Serverless Java Container là gì?</h3>



<p class="wp-block-paragraph"><strong>AWS Serverless Java Container</strong> là thư viện giúp bạn chạy các framework Java phổ biến như <em>Spring Boot</em>, <em>Jakarta EE</em>, <em>Micronaut</em> hoặc <em>Jersey</em> trên AWS Lambda mà không cần viết lại toàn bộ mã nguồn.</p>



<p class="wp-block-paragraph">Thư viện hoạt động như một <strong>adapter</strong> – nó chuyển các sự kiện từ Lambda thành request/response kiểu <em>Servlet</em> quen thuộc. Nhờ đó, ứng dụng Java có thể hoạt động bình thường chỉ với một số thay đổi nhỏ ở phần entry point.</p>



<p class="wp-block-paragraph">Phiên bản <strong>v2 (2024)</strong> mang lại nhiều cải tiến quan trọng:</p>



<ul class="wp-block-list">
<li>Hỗ trợ <strong>Jakarta EE 10+</strong>.</li>



<li>Tương thích <strong>Spring Framework 6.x</strong> và <strong>Spring Boot 3.x</strong>.</li>



<li>Cập nhật hỗ trợ <strong>Jersey 3.x</strong>.</li>



<li>Tối ưu hiệu suất và giảm kích thước file JAR.</li>
</ul>



<h3 id="4-vi-du-chuyen-ung-dung-spring-boot-3-sang-lambda" class="wp-block-heading">4. Ví dụ: Chuyển ứng dụng Spring Boot 3 sang Lambda</h3>



<p class="wp-block-paragraph">Dưới đây là ví dụ cơ bản để triển khai ứng dụng Spring Boot 3 lên AWS Lambda sử dụng Serverless Java Container v2.</p>



<h4 id="buoc-1-them-dependency-vao-pom-xml" class="wp-block-heading">Bước 1: Thêm dependency vào <code>pom.xml</code></h4>



<pre class="wp-block-code"><code>
&lt;dependency&gt;
    &lt;groupId&gt;com.amazonaws.serverless&lt;/groupId&gt;
    &lt;artifactId&gt;aws-serverless-java-container-springboot3&lt;/artifactId&gt;
    &lt;version&gt;2.0.0&lt;/version&gt;
&lt;/dependency&gt;
  </code></pre>



<h4 id="buoc-2-loai-bo-tomcat-khoi-build" class="wp-block-heading">Bước 2: Loại bỏ Tomcat khỏi build</h4>



<p class="wp-block-paragraph">Spring Boot mặc định nhúng Tomcat. Vì Lambda sử dụng API Gateway để xử lý HTTP, bạn có thể loại bỏ Tomcat để giảm kích thước package và tăng tốc khởi động.</p>



<pre class="wp-block-code"><code>&lt;excludes&gt;
    &lt;exclude&gt;org.apache.tomcat.embed:*&lt;/exclude&gt;
&lt;/excludes&gt;
  </code></pre>



<h4 id="buoc-3-tao-handler-cho-lambda" class="wp-block-heading">Bước 3: Tạo handler cho Lambda</h4>



<p class="wp-block-paragraph">Ví dụ cơ bản:</p>



<pre class="wp-block-code"><code>
public class StreamLambdaHandler 
        extends SpringBootRequestHandler&lt;APIGatewayV2HTTPEvent, APIGatewayV2HTTPResponse&gt; {
}
  </code></pre>



<p class="wp-block-paragraph">Hoặc nếu bạn cần nhiều quyền kiểm soát hơn:</p>



<pre class="wp-block-code"><code>
public class CustomHandler implements RequestStreamHandler {

    private static final SpringDelegatingLambdaContainerHandler&lt;
        APIGatewayV2HTTPEvent, APIGatewayV2HTTPResponse&gt; handler;

    static {
        handler = new SpringDelegatingLambdaContainerHandler&lt;&gt;(Application.class);
    }

    @Override
    public void handleRequest(InputStream input, OutputStream output, Context context)
            throws IOException {
        handler.proxyStream(input, output, context);
    }
}

  </code></pre>



<h4 id="buoc-4-cau-hinh-bien-moi-truong" class="wp-block-heading">Bước 4: Cấu hình biến môi trường</h4>



<p class="wp-block-paragraph">Đặt biến môi trường <code>MAIN_CLASS</code> trỏ tới class chính có annotation <code>@SpringBootApplication</code> của bạn.</p>



<h3 id="5-toi-uu-hieu-suat-voi-aws-lambda-snapstart" class="wp-block-heading">5. Tối ưu hiệu suất với AWS Lambda SnapStart</h3>



<p class="wp-block-paragraph">Một trong những thách thức lớn khi chạy Java trên Lambda là <strong>thời gian khởi động (cold start)</strong>. AWS đã giới thiệu <strong>SnapStart</strong> để giải quyết vấn đề này bằng cách “chụp ảnh” trạng thái Lambda sau khi khởi tạo, rồi khôi phục lại cực nhanh cho các request tiếp theo.</p>



<p class="wp-block-paragraph">Để bật SnapStart, chỉ cần thêm cấu hình vào AWS SAM hoặc CloudFormation:</p>



<pre class="wp-block-code"><code>
SnapStart:
  ApplyOn: PublishedVersions
  AutoPublishAlias: prod
  </code></pre>



<p class="wp-block-paragraph">Kết quả là ứng dụng Java có thể khởi động nhanh hơn gấp 5–10 lần, giúp cải thiện đáng kể trải nghiệm người dùng, đặc biệt với các API có tần suất gọi thấp hoặc không liên tục.</p>



<h3 id="6-ket-noi-co-so-du-lieu-hieu-qua-trong-moi-truong-lambda" class="wp-block-heading">6. Kết nối cơ sở dữ liệu hiệu quả trong môi trường Lambda</h3>



<p class="wp-block-paragraph">Khi làm việc với RDS hoặc Aurora, cần lưu ý:</p>



<ul class="wp-block-list">
<li>Dùng connection pool nhỏ (ví dụ HikariCP với 2–5 connection).</li>



<li>Tận dụng cache tầng ứng dụng (Redis, DAX) để giảm truy vấn trực tiếp DB.</li>



<li>Hạn chế query đồng bộ trong <em>hot path</em>; nên dùng async hoặc event-driven khi có thể.</li>
</ul>



<h3 id="7-nhuoc-diem-va-luu-y-khi-trien-khai-java-tren-aws-lambda" class="wp-block-heading">7. Nhược điểm và lưu ý khi triển khai Java trên AWS Lambda</h3>



<p class="wp-block-paragraph">Dù <strong>AWS Serverless Java Container</strong> giúp việc chuyển ứng dụng Java lên Lambda trở nên dễ dàng hơn, developer vẫn cần hiểu rõ một số hạn chế để thiết kế và vận hành hiệu quả hơn.</p>



<p class="wp-block-paragraph"></p>



<figure class="wp-block-table is-style-stripes"><table class="has-fixed-layout"><thead><tr><th>Vấn đề</th><th>Mô tả chi tiết</th><th>Giải pháp / Lưu ý</th></tr></thead><tbody><tr><td>Thời gian khởi động (Cold Start)</td><td>  &#8211; Java cần khởi tạo JVM và load class khi Lambda được gọi lần đầu, có thể mất vài trăm mili-giây tới vài giây.<br><br>  &#8211; Đặc biệt nếu ứng dụng dùng Spring Boot hoặc nhiều dependency.<br></td><td>   &#8211; Dùng <strong>AWS Lambda SnapStart</strong> (Java 11+) để tạo snapshot môi trường khởi tạo.<br><br>  &#8211; Cân nhắc <strong>GraalVM Native Image</strong> cho các hàm nhỏ, khởi động cực nhanh.</td></tr><tr><td>Kích thước file JAR lớn</td><td>  &#8211; Ứng dụng Spring Boot hoặc Jakarta EE thường tạo “fat JAR” có thể trên 50 MB, làm tăng thời gian tải và ảnh hưởng cold start. </td><td>  &#8211; Dùng <strong>Spring Boot Layered JAR</strong> hoặc <strong>spring-boot-thin-launcher</strong>. <br><br>  &#8211; Xem xét đóng gói dưới dạng <strong>container image</strong> để kiểm soát tốt kích thước.<br></td></tr><tr><td>Timeout bị giới hạn</td><td>  &#8211; Lambda chỉ chạy tối đa 15 phút, không phù hợp cho các batch job hoặc xử lý dữ liệu lớn kéo dài. <br><br>  &#8211; Nếu dùng qua API Gateway thì chỉ còn <strong>29s</strong><br></td><td>  &#8211; Chia nhỏ công việc hoặc sử dụng <strong>AWS Step Functions</strong> để quản lý workflow dài.<br><br>&#8211; Sử dụng SQS trong AWS để thực hiện các công việc như import, xử lý data.</td></tr><tr><td>Kết nối cơ sở dữ liệu</td><td>  &#8211; Lambda không duy trì kết nối DB giữa các lần chạy, có thể gây quá tải connection pool hoặc chậm kết nối mới. </td><td>  &#8211; Dùng <strong>RDS Proxy</strong> để giữ kết nối trung gian hoặc giới hạn connection pool nhỏ (2–5 connection). <br><br>Kết hợp caching như <strong>Redis</strong> để giảm truy vấn DB.<br></td></tr><tr><td>Chi phí khi có nhiều hàm nhỏ</td><td>  &#8211; Nếu tách quá nhiều hàm (micro-Lambda), tổng chi phí gọi (invocations) và log có thể tăng, đặc biệt khi dùng API Gateway nhiều lớp. </td><td>  &#8211; Gộp các hàm có logic gần nhau, hoặc dùng <strong>Function URLs</strong> để giảm chi phí API Gateway khi phù hợp.</td></tr><tr><td>Debug và quan sát hệ thống</td><td>  &#8211; Debug Lambda Java khó hơn vì không thể xem log real-time hoặc attach debugger như local Tomcat. </td><td>  &#8211; Dùng <strong>AWS SAM CLI</strong> để test local, kết hợp <strong>CloudWatch Logs</strong>, <strong>X-Ray</strong> và <strong>AWS Lambda Powertools for Java</strong> để logging và tracing hiệu quả.</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">Tóm lại, Java vẫn là lựa chọn mạnh mẽ trong môi trường AWS Lambda nhờ tính ổn định và hệ sinh thái lớn, nhưng để đạt hiệu năng tối ưu, cần chú trọng vào cấu trúc ứng dụng, cách đóng gói, và kỹ thuật giảm thời gian khởi động.</p>



<h4 id="tai-nguyen-tham-khao" class="wp-block-heading">Tài nguyên tham khảo</h4>



<ul class="wp-block-list">
<li><a href="https://github.com/aws/serverless-java-container/" target="_blank" rel="noreferrer noopener">GitHub: AWS Serverless Java Container</a></li>



<li><a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-java.html" target="_blank" rel="noreferrer noopener">Tài liệu chính thức: AWS Lambda cho Java Developer</a></li>



<li><a href="https://aws.amazon.com/blogs/compute/" target="_blank" rel="noreferrer noopener">AWS Compute Blog</a></li>
</ul>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://blog.tomosia.com.vn/re-platforming-java-aws-serverless-java-container/">Re-platforming Java với AWS Serverless Java Container</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/re-platforming-java-aws-serverless-java-container/feed/</wfw:commentRss>
			<slash:comments>6</slash:comments>
		
		
			</item>
		<item>
		<title>Dependency Injection (DI) framework &#8211; Google Guice</title>
		<link>https://blog.tomosia.com.vn/dependency-injection-di-framework-google-guice/</link>
					<comments>https://blog.tomosia.com.vn/dependency-injection-di-framework-google-guice/#comments</comments>
		
		<dc:creator><![CDATA[admin_tomosia]]></dc:creator>
		<pubDate>Tue, 05 Mar 2024 06:19:13 +0000</pubDate>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[Design Patterns]]></category>
		<category><![CDATA[Dependency Injection]]></category>
		<category><![CDATA[Guice]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=3189</guid>

					<description><![CDATA[<p>Trong bài trước, chúng ta đã được đi tìm hiểu Dependency Injection là gì. Tiếp tục chuỗi thông&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/dependency-injection-di-framework-google-guice/">Dependency Injection (DI) framework &#8211; Google Guice</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph" style="font-size:16px">Trong bài trước, chúng ta đã được đi tìm hiểu <strong><a href="http://blog.tomosia.com.vn/thong-nao-dependency-injection-bang-eimi-fukada/">Dependency Injection</a></strong> là gì. Tiếp tục chuỗi thông não, chúng ta cùng nhau làm tý &#8220;inova&#8221; với cái thằng Google Guice này nhé.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="332" height="456" src="http://blog.tomosia.com.vn/wp-content/uploads/2024/03/1_OXaU46Rv8B3MV7YuaAanaA.png" alt="" class="wp-image-3190" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2024/03/1_OXaU46Rv8B3MV7YuaAanaA.png 332w, https://blog.tomosia.com.vn/wp-content/uploads/2024/03/1_OXaU46Rv8B3MV7YuaAanaA-218x300.png 218w" sizes="auto, (max-width: 332px) 100vw, 332px" /></figure>
</div>


<h2 id="1-google-guice-la-gi" class="wp-block-heading" style="font-size:30px"><strong>1. Google Guice là gì?</strong></h2>



<p class="wp-block-paragraph" style="font-size:16px"><strong>Google Guice</strong> là một framework DI gọn nhẹ, mã nguồn mở giúp chúng ta phát triển các ứng dụng dạng module được phát triển và quản lý bởi Google. Guice hỗ trợ từ Java 6 trở lên nhé anh em.</p>



<p class="wp-block-paragraph" style="font-size:16px">Trong Guice, annotation&nbsp;<strong>@Inject</strong>&nbsp;được sử dụng để tiêm phụ thuộc. Nó cho phép chúng ta inject sự phụ thuộc tại các constructor, field hoặc method. Sử dụng Guice, chúng ta có thể xác định scope của instance đối tượng phụ thuộc. Nó cũng có các tính năng để tích hợp với Spring và&nbsp;<a href="https://github.com/google/guice/wiki/AOP">Aspect Oriented Programming (AOP)</a>.</p>



<h2 id="2-cai-dat-google-guice" class="wp-block-heading" style="font-size:30px"><strong>2. Cài đặt&nbsp;Google Guice</strong></h2>



<p class="wp-block-paragraph" style="font-size:16px">Để sử dụng<strong> Google Guice</strong>, anh em thêm thư viện Guice vào trong file <em>pom.xml</em> như sau:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="&lt;!-- https://mvnrepository.com/artifact/com.google.inject/guice --&gt;
&lt;dependency&gt;
    &lt;groupId&gt;com.google.inject&lt;/groupId&gt;
    &lt;artifactId&gt;guice&lt;/artifactId&gt;
    &lt;version&gt;4.2.2&lt;/version&gt;
&lt;/dependency&gt;" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">&lt;!--</span><span style="color: #F6F6F4"> https</span><span style="color: #F286C4">:</span><span style="color: #7B7F8B">//mvnrepository.com/artifact/com.google.inject/guice --&gt;</span></span>
<span class="line"><span style="color: #F286C4">&lt;</span><span style="color: #F6F6F4">dependency</span><span style="color: #F286C4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">&lt;</span><span style="color: #F6F6F4">groupId</span><span style="color: #F286C4">&gt;</span><span style="color: #F6F6F4">com.google.inject</span><span style="color: #F286C4">&lt;/</span><span style="color: #F6F6F4">groupId</span><span style="color: #F286C4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">&lt;</span><span style="color: #F6F6F4">artifactId</span><span style="color: #F286C4">&gt;</span><span style="color: #F6F6F4">guice</span><span style="color: #F286C4">&lt;/</span><span style="color: #F6F6F4">artifactId</span><span style="color: #F286C4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">&lt;</span><span style="color: #F6F6F4">version</span><span style="color: #F286C4">&gt;</span><span style="color: #BF9EEE">4.2</span><span style="color: #F6F6F4">.</span><span style="color: #EE6666; font-style: italic; text-decoration: underline">2</span><span style="color: #F286C4">&lt;/</span><span style="color: #F6F6F4">version</span><span style="color: #F286C4">&gt;</span></span>
<span class="line"><span style="color: #F286C4">&lt;/</span><span style="color: #F6F6F4">dependency</span><span style="color: #F286C4">&gt;</span></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:16px">Hoặc muốn sử dụng các version khác thì anh em có thể truy cập vào 👉 <a href="https://mvnrepository.com/artifact/com.google.inject/guice">đây</a> 👈. Cứ version nào nhiều người sử dụng mà &#8220;phang&#8221; thôi =))). </p>



<h2 id="3-vi-du-co-ban-ve-dependency-injection-voi-google-guice" class="wp-block-heading" style="font-size:30px"><strong>3. Ví dụ cơ bản về Dependency Injection với Google Guice</strong></h2>



<p class="wp-block-paragraph" style="font-size:16px">Để sử dụng Guice cần nhớ 2 thông tin quan trọng sau:</p>



<ul class="wp-block-list">
<li style="font-size:16px">Nói với Guice cái gì cần được inject với annotation&nbsp;<strong>@Inject</strong>.</li>



<li style="font-size:16px">Nói với Guice cách inject mô-đun:&nbsp;implement&nbsp;<strong>Asbstract Module</strong>, bind các interface với các implementation mong muốn.</li>
</ul>



<p class="wp-block-paragraph" style="font-size:16px">Quay trở lại với ví dụ em gái Eimi Fukada trong bài <a href="http://blog.tomosia.com.vn/thong-nao-dependency-injection-bang-eimi-fukada/">trước</a> lần này tôi sẽ sử dụng Guice nha:</p>



<p class="wp-block-paragraph" style="font-size:16px">GuiceModule.java</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package org.blogtms;

import com.google.inject.AbstractModule;

public class GuiceModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(Outfit.class).to(Bikini.class);
        bind(Accessories.class).to(GucciAccessories.class);
        bind(HairStyle.class).to(KoreanHairStyle.class);
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">org.blogtms</span><span style="color: #F6F6F4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> com.google.inject.AbstractModule;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">GuiceModule</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">extends</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">AbstractModule</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    @</span><span style="color: #97E1F1; font-style: italic">Override</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">protected</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">void</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">configure</span><span style="color: #F6F6F4">() {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #62E884">bind</span><span style="color: #F6F6F4">(Outfit.class).</span><span style="color: #62E884">to</span><span style="color: #F6F6F4">(Bikini.class);</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #62E884">bind</span><span style="color: #F6F6F4">(Accessories.class).</span><span style="color: #62E884">to</span><span style="color: #F6F6F4">(GucciAccessories.class);</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #62E884">bind</span><span style="color: #F6F6F4">(HairStyle.class).</span><span style="color: #62E884">to</span><span style="color: #F6F6F4">(KoreanHairStyle.class);</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:16px">Girl.java thêm @Inject</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package org.blogtms;

import com.google.inject.Inject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

//@Component
public class Girl {

    @Autowired
    Outfit outfit;

    @Autowired
    Accessories accessories;

    @Autowired
    HairStyle hairStyle;

    @Inject
    public Girl(Outfit outfit, Accessories accessories, HairStyle hairStyle) {
        this.outfit = outfit;
        this.accessories = accessories;
        this.hairStyle = hairStyle;
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">org.blogtms</span><span style="color: #F6F6F4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> com.google.inject.Inject;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> org.springframework.beans.factory.annotation.Autowired;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> org.springframework.stereotype.Component;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #7B7F8B">//@Component</span></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Girl</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    @</span><span style="color: #97E1F1; font-style: italic">Autowired</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #97E1F1; font-style: italic">Outfit</span><span style="color: #F6F6F4"> outfit;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    @</span><span style="color: #97E1F1; font-style: italic">Autowired</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #97E1F1; font-style: italic">Accessories</span><span style="color: #F6F6F4"> accessories;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    @</span><span style="color: #97E1F1; font-style: italic">Autowired</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #97E1F1; font-style: italic">HairStyle</span><span style="color: #F6F6F4"> hairStyle;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    @</span><span style="color: #97E1F1; font-style: italic">Inject</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">Girl</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1; font-style: italic">Outfit</span><span style="color: #F6F6F4"> </span><span style="color: #FFB86C; font-style: italic">outfit</span><span style="color: #F6F6F4">, </span><span style="color: #97E1F1; font-style: italic">Accessories</span><span style="color: #F6F6F4"> </span><span style="color: #FFB86C; font-style: italic">accessories</span><span style="color: #F6F6F4">, </span><span style="color: #97E1F1; font-style: italic">HairStyle</span><span style="color: #F6F6F4"> </span><span style="color: #FFB86C; font-style: italic">hairStyle</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #BF9EEE; font-style: italic">this</span><span style="color: #F6F6F4">.outfit </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> outfit;</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #BF9EEE; font-style: italic">this</span><span style="color: #F6F6F4">.accessories </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> accessories;</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #BF9EEE; font-style: italic">this</span><span style="color: #F6F6F4">.hairStyle </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> hairStyle;</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:16px">Main.java</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package org.blogtms;

import com.google.inject.Guice;
import com.google.inject.Injector;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

//@SpringBootApplication
public class Main {
    public static void main(String[] args) {
//        ApplicationContext context = SpringApplication.run(Main.class, args);
//
//        Girl girl = context.getBean(Girl.class);

        Injector injector = Guice.createInjector(new GuiceModule());

        Girl girl = injector.getInstance(Girl.class);
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">org.blogtms</span><span style="color: #F6F6F4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> com.google.inject.Guice;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> com.google.inject.Injector;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> org.springframework.boot.SpringApplication;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> org.springframework.boot.autoconfigure.SpringBootApplication;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> org.springframework.context.ApplicationContext;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #7B7F8B">//@SpringBootApplication</span></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Main</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">void</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">main</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1; font-style: italic">String</span><span style="color: #F6F6F4">[] </span><span style="color: #FFB86C; font-style: italic">args</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #7B7F8B">//        ApplicationContext context = SpringApplication.run(Main.class, args);</span></span>
<span class="line"><span style="color: #7B7F8B">//</span></span>
<span class="line"><span style="color: #7B7F8B">//        Girl girl = context.getBean(Girl.class);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1; font-style: italic">Injector</span><span style="color: #F6F6F4"> injector </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> Guice.</span><span style="color: #62E884">createInjector</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">GuiceModule</span><span style="color: #F6F6F4">());</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1; font-style: italic">Girl</span><span style="color: #F6F6F4"> girl </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> injector.</span><span style="color: #62E884">getInstance</span><span style="color: #F6F6F4">(Girl.class);</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:16px">Thế là xong rồi =))) </p>



<p class="wp-block-paragraph" style="font-size:16px">Trong ví dụ trên, chúng ta thấy một số phần thay đổi so với ví dụ ở bài trước:</p>



<ul class="wp-block-list">
<li style="font-size:16px">Tạo một class extends từ&nbsp;<strong>AbstractModule</strong>, lớp này được sử dụng để định nghĩa cách mà một class/ interface sẽ được inject bởi một implement cụ thể nào. Trong ví dụ này, <em>Outfit</em> sẽ được inject bởi implement&nbsp;<em>Bikini</em>.</li>



<li style="font-size:16px">Lớp <em>Girl</em> có dependency với&nbsp;<em>Outfit</em>. Trong ví dụ này, chúng ta sử dụng&nbsp;@Inject tại hàm xây dựng (constructor) để inject đối tượng&nbsp;<em>Outfit</em>.</li>



<li style="font-size:16px">Khởi tạo Guice Injector với config.</li>



<li style="font-size:16px">Lấy thể hiện đối tượng với các dependency để sử dụng.</li>
</ul>



<h2 id="4-scope-trong-google-guice" class="wp-block-heading" style="font-size:30px"><strong>4. Scope trong&nbsp;Google Guice</strong></h2>



<p class="wp-block-paragraph" style="font-size:16px">Theo mặc định, Guice trả về một instance mới mỗi lần nó cần cung cấp một giá trị. Hành vi này có thể cấu hình thông qua phạm vi (<a href="https://github.com/google/guice/wiki/Scopes">scope</a>). Scope cho phép chúng ta tái sử dụng instance.</p>



<p class="wp-block-paragraph" style="font-size:16px">Các loại Scope được hỗ trợ bởi&nbsp;Google Guice:</p>



<ul class="wp-block-list">
<li style="font-size:16px"><strong>@Singleton</strong>&nbsp;: một instance duy nhất được sử dụng trong toàn bộ ứng dụng.</li>



<li style="font-size:16px"><strong>@SessionScoped</strong>&nbsp;: mỗi session sẽ có một instance khác nhau.</li>



<li style="font-size:16px"><strong>@RequestScoped</strong>&nbsp;: mỗi request sẽ có một instance khác nhau.</li>
</ul>



<p class="wp-block-paragraph" style="font-size:16px">Trên đây là giới thiệu cơ bản về Google Guice.&nbsp;Nó cung cấp một cách tiếp cận mới cho&nbsp;<a href="http://blog.tomosia.com.vn/thong-nao-dependency-injection-bang-eimi-fukada/"><strong>Dependency Injection</strong></a>, tận dụng thế mạnh của&nbsp;<strong>Annotation</strong>&nbsp;và&nbsp;<strong>Generic</strong>&nbsp;để giúp chúng ta dễ dàng hơn trong việc quản lý và sử dụng các Dependency, cũng như dễ dàng phát triển các ứng dụng dạng module.</p>



<p class="wp-block-paragraph" style="font-size:16px"><strong>Tài liệu tham khảo:</strong></p>



<ul class="wp-block-list">
<li style="font-size:16px"><a href="https://github.com/google/guice">https://github.com/google/guice</a></li>



<li style="font-size:16px"><a href="https://www.tutorialspoint.com/guice/index.htm">https://www.tutorialspoint.com/guice/index.htm</a></li>
</ul>



<p class="wp-block-paragraph" style="font-size:16px">Cảm ơn anh em đã theo dõi bài viết 🙋‍♂️. Vẫn như mọi khi, <a href="https://github.com/chienhnc/di">đây là source code</a></p>
<p>The post <a href="https://blog.tomosia.com.vn/dependency-injection-di-framework-google-guice/">Dependency Injection (DI) framework &#8211; Google Guice</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/dependency-injection-di-framework-google-guice/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title>Thông não Dependency Injection bằng Eimi Fukada</title>
		<link>https://blog.tomosia.com.vn/thong-nao-dependency-injection-bang-eimi-fukada/</link>
					<comments>https://blog.tomosia.com.vn/thong-nao-dependency-injection-bang-eimi-fukada/#comments</comments>
		
		<dc:creator><![CDATA[admin_tomosia]]></dc:creator>
		<pubDate>Fri, 01 Mar 2024 02:39:09 +0000</pubDate>
				<category><![CDATA[Solution]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[Design Patterns]]></category>
		<category><![CDATA[Dependency Injection]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=3173</guid>

					<description><![CDATA[<p>Hello what&#8217;s up, dạo này anh em còn bị chửi code như shit nữa không nhờ. Tôi thì&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/thong-nao-dependency-injection-bang-eimi-fukada/">Thông não Dependency Injection bằng Eimi Fukada</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph" style="font-size:16px">Hello what&#8217;s up, dạo này anh em còn bị chửi code như shit nữa không nhờ. Tôi thì vẫn thế =)))), vẫn như shit nhưng hôm nay tôi muốn giới thiệu đến anh em một kỹ thuật mới là <strong>Dependency Injection</strong> để code của anh em đỡ bốc mùi hơn một chút nhé, vào việc.</p>



<h2 id="1-dependency-injection-la-gi" class="wp-block-heading" style="font-size:30px"><strong>1. Dependency Injection là gì?</strong></h2>



<p class="wp-block-paragraph">Theo tài liệu thì:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph" style="font-size:16px">Dependency Injection is a design pattern, &#8230;</p>
</blockquote>



<p class="wp-block-paragraph" style="font-size:16px">Nôm na Dependency Injection (DI) là một kỹ thuật cho phép xóa bỏ sự phụ thuộc giữa các module, làm cho ứng dụng dễ dàng hơn trong việc thay đổi module, bảo trì code và&nbsp;testing.</p>



<p class="wp-block-paragraph" style="font-size:16px"><strong>DI</strong>&nbsp;cung cấp cho một đối tượng các thể hiện phụ thuộc (dependencies) của nó&nbsp;từ bên ngoài truyền vào mà không phải khởi tạo trực tiếp từ trong class sử dụng.</p>



<h2 id="2-nhiem-vu-cua-dependency-injection" class="wp-block-heading" style="font-size:30px"><strong>2. Nhiệm vụ của dependency injection</strong></h2>



<ul class="wp-block-list">
<li style="font-size:16px">Tạo các đối tượng.</li>



<li style="font-size:16px">Quản lý sự phụ thuộc (dependencies) giữa các đối tượng.</li>



<li style="font-size:16px">Cung cấp (inject) các phụ thuộc được yêu cầu cho đối tượng (được truyền từ bên ngoài đối tượng).</li>
</ul>



<h2 id="3-nguyen-tac-hoat-dong-cua-di" class="wp-block-heading" style="font-size:30px"><strong>3. Nguyên tắc hoạt động của&nbsp;DI</strong></h2>



<ul class="wp-block-list">
<li style="font-size:16px">Các module không giao tiếp trực tiếp với nhau, mà thông qua interface. Module cấp thấp sẽ implement interface, module cấp cao sẽ gọi module cấp thấp thông qua interface.</li>



<li style="font-size:16px">Việc khởi tạo các module cấp thấp sẽ do DI Container/ IoC Container thực hiện.</li>



<li style="font-size:16px">Việc Module nào gắn với interface nào sẽ được config trong file properties, trong file XML hoặc thông qua Annotation. Annotation là một cách thường được sử dụng trong các Framework, chẳng hạn như @Inject với&nbsp;<a href="http://www.cdi-spec.org/">CDI</a>, @Autowired với&nbsp;<a href="https://spring.io/">Spring</a>&nbsp;hay @ManagedProperty với&nbsp;<a href="https://www.oracle.com/technetwork/java/javaee/javaserverfaces-139869.html">JSF</a>.</li>
</ul>



<h2 id="4-cac-dang-dependency-injection" class="wp-block-heading" style="font-size:30px"><strong>4. Các dạng Dependency Injection</strong></h2>



<ul class="wp-block-list">
<li style="font-size:16px"><strong>Constructor Injection</strong>: Các dependency sẽ được container&nbsp;truyền vào (inject vào)&nbsp;1 class thông qua constructor của class đó. Đây là cách thông dụng nhất.</li>



<li style="font-size:16px"><strong>Setter Injection</strong>: Các dependency sẽ được truyền vào 1 class thông qua các hàm Setter.</li>



<li style="font-size:16px"><strong>Fields/ properties</strong>: Các dependency sẽ được truyền vào 1 class một cách trực tiếp vào các field.</li>



<li style="font-size:16px"><strong>Interface Injection</strong>: Class cần inject sẽ implement 1 interface. Interface này chứa 1 hàm tên&nbsp;Inject. Container sẽ injection dependency vào 1 class thông qua việc gọi hàm&nbsp;Inject&nbsp;của interface đó. Đây là cách rườm rà và cũng ít được sử dụng.</li>



<li style="font-size:16px"><strong>Service Locator</strong>: nó hoạt động như một mapper, cho phép thay đổi code tại thời điểm run-time mà không cần biên dịch lại ứng dụng hoặc phải khởi động lại.</li>
</ul>



<p class="wp-block-paragraph" style="font-size:16px">Anh em đọc đến đây đã hiểu <strong>Dependency Injection</strong> là gì chưa? Anh em nào đã thông não thì có thể bỏ qua, anh em nào vẫn lơ tơ mơ thì tiếp tục, full hd không che phía dưới =)))).</p>



<h2 id="5-vi-du-su-dung-dependency-injection" class="wp-block-heading" style="font-size:30px"><strong>5. Ví dụ sử dụng Dependency Injection.</strong></h2>



<p class="wp-block-paragraph" style="font-size:16px">Bikini.java</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package org.tmsblog.patterns.di;

public class Bikini {
    public void wear() {
        System.out.println(&quot;Đã mặc bikini&quot;);
    }
}
" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">org.tmsblog.patterns.di</span><span style="color: #F6F6F4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Bikini</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">void</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">wear</span><span style="color: #F6F6F4">() {</span></span>
<span class="line"><span style="color: #F6F6F4">        System.out.</span><span style="color: #62E884">println</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">Đã mặc bikini</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">);</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:16px">Girl.java</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package org.tmsblog.patterns.di;

public class Girl {

    private Bikini outfit = new Bikini();
    
    public void result() {
        outfit.wear();
    }
}
" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">org.tmsblog.patterns.di</span><span style="color: #F6F6F4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Girl</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Bikini</span><span style="color: #F6F6F4"> outfit </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">Bikini</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">void</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">result</span><span style="color: #F6F6F4">() {</span></span>
<span class="line"><span style="color: #F6F6F4">        outfit.</span><span style="color: #62E884">wear</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:16px">Ví dụ anh em tạo 1 <strong>Girl</strong>, anh em sẽ tạo ra thêm 1 bộ&nbsp;<strong>Bikini</strong>&nbsp;đi kèm với cô gái đó. Lúc này,&nbsp;<code>Bikini</code>&nbsp;tồn tại mang ý nghĩa là&nbsp;<strong>dependency</strong>&nbsp;(phụ thuộc) của&nbsp;<strong>Girl</strong>.</p>



<p class="wp-block-paragraph" style="font-size:16px">Khi khởi tạo thuộc tính như này, vô tình tạo ra một điểm thắt nút trong chương trình, giả sử,&nbsp;<strong>Girl</strong>&nbsp;muốn mặc một bộ&nbsp;<em>Quần Jeans + áo hai dây</em> thì anh em sẽ phải thay class&nbsp;<strong>Bikini</strong>&nbsp;thành&nbsp;<strong>JeansWithTshirt</strong>(quần jeans với áo T-shirt) ư?</p>



<p class="wp-block-paragraph" style="font-size:16px">Hay nguy hiểm hơn, bộ đồ&nbsp;<strong>Bikini</strong>&nbsp;bị hỏng? (code lớp&nbsp;<strong>Bikini</strong>&nbsp;không hoạt động?) nó sẽ ảnh hưởng trực tiếp tới&nbsp;<strong>Girl</strong>.</p>



<p class="wp-block-paragraph" style="font-size:16px">Vấn đề là ở đó, chúng ta cùng đọc lại nguyên tắc D của <strong><a href="http://blog.tomosia.com.vn/solid-principles/">SOLID</a></strong>:</p>



<ul class="wp-block-list">
<li style="font-size:16px">Các mô-đun cấp cao không được nhập bất cứ thứ gì từ các mô-đun cấp thấp. Cả hai nên phụ thuộc vào sự trừu tượng</li>
</ul>



<p class="wp-block-paragraph" style="font-size:16px">Bây giờ chúng ta sẽ áp dụng <strong>Dependency Injection</strong> để giải quyết vấn đề trên.</p>



<p class="wp-block-paragraph" style="font-size:16px">Girl.java</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package org.tmsblog.patterns.di;

public class Girl {

    private final Outfit anything;

    public Girl(Outfit anything) {
        this.anything = anything;
    }
}
" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">org.tmsblog.patterns.di</span><span style="color: #F6F6F4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Girl</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">final</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Outfit</span><span style="color: #F6F6F4"> anything;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">Girl</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1; font-style: italic">Outfit</span><span style="color: #F6F6F4"> </span><span style="color: #FFB86C; font-style: italic">anything</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #BF9EEE; font-style: italic">this</span><span style="color: #F6F6F4">.anything </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> anything;</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:16px">Bikini.java</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package org.tmsblog.patterns.di;

public class Bikini implements Outfit{
   @Override
    public void wear() {
        System.out.println(&quot;Đã mặc bikini&quot;);
    }
}
" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">org.tmsblog.patterns.di</span><span style="color: #F6F6F4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Bikini</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">implements</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Outfit</span><span style="color: #F6F6F4">{</span></span>
<span class="line"><span style="color: #F6F6F4">   @</span><span style="color: #97E1F1; font-style: italic">Override</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">void</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">wear</span><span style="color: #F6F6F4">() {</span></span>
<span class="line"><span style="color: #F6F6F4">        System.out.</span><span style="color: #62E884">println</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">Đã mặc bikini</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">);</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:16px">Outfit.java</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package org.tmsblog.patterns.di;

public interface Outfit {
    public void wear();
}
" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">org.tmsblog.patterns.di</span><span style="color: #F6F6F4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">interface</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Outfit</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">void</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">wear</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:16px">Main.java</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package org.tmsblog.patterns.di;

public class Main {
    public static void main(String[] args) {
        Outfit bikini = new Bikini();

        Girl fukada = new Girl(bikini);
        //Vậy là đã lấy được đối tượng Girl nha
    }
}
" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">org.tmsblog.patterns.di</span><span style="color: #F6F6F4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Main</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">void</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">main</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1; font-style: italic">String</span><span style="color: #F6F6F4">[] </span><span style="color: #FFB86C; font-style: italic">args</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1; font-style: italic">Outfit</span><span style="color: #F6F6F4"> bikini </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">Bikini</span><span style="color: #F6F6F4">();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1; font-style: italic">Girl</span><span style="color: #F6F6F4"> fukada </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">Girl</span><span style="color: #F6F6F4">(bikini);</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #7B7F8B">//Vậy là đã lấy được đối tượng Girl nha</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:16px">Với đoạn code ở trên, chúng ta đã&nbsp;<em>gần như</em>&nbsp;tách được&nbsp;<strong>Bikini</strong>&nbsp;ra hoàn toàn khỏi&nbsp;<strong>Girl</strong>. điều này làm giảm sự phụ thuộc giữa&nbsp;<strong>Girl</strong>&nbsp;và&nbsp;<strong>Bikini</strong>. Mà tăng tính tùy biến, linh hoạt cho&nbsp;<code>code</code>.</p>



<p class="wp-block-paragraph" style="font-size:16px">Bây giờ&nbsp;<strong>Girl</strong>&nbsp;sẽ hoạt động với&nbsp;<strong>Outfit</strong>&nbsp;mà thôi. Và&nbsp;<strong>Outfit</strong>&nbsp;ở đâu ra? Chúng ta&nbsp;<strong>tạo ra</strong>&nbsp;và&nbsp;<strong>đưa nó vào</strong>&nbsp;<code>(Inject)</code>&nbsp;cô gái&nbsp;<strong>Girl</strong>.</p>



<p class="wp-block-paragraph" style="font-size:16px">Khái niệm&nbsp;<code>Dependency Injection</code>&nbsp;từ đây mà ra: <br><em><mark style="background-color:#e9ecef" class="has-inline-color has-black-color"><strong>Dependency Injection là việc các&nbsp;Object&nbsp;nên phụ thuộc vào các&nbsp;Abstract Class&nbsp;và thể hiện chi tiết của nó sẽ được&nbsp;Inject&nbsp;vào đối tượng lúc runtime.</strong></mark></em></p>



<p class="wp-block-paragraph" style="font-size:16px">Bây giờ muốn&nbsp;<strong>Girl</strong>&nbsp;mặc gì khác, anh em chỉ cần tạo một Class kế thừa&nbsp;<strong>Outfit</strong>&nbsp;và&nbsp;<em>Inject</em>&nbsp;nó vào&nbsp;<strong>Girl</strong>&nbsp;là xong!</p>



<p class="wp-block-paragraph" style="font-size:16px"><strong>Dependency Injection</strong>&nbsp;giúp chúng ta dễ dàng mở rộng&nbsp;<code>code</code>&nbsp;và giảm sự phụ thuộc giữa các dependency với nhau. Tuy nhiên, lúc này, khi code bạn sẽ phải kiêm thêm nhiệm vụ&nbsp;<strong>Inject dependency (tiêm sự phụ thuộc)</strong>. Thử tưởng tượng một&nbsp;<code>Class</code>&nbsp;có hàng chục dependency thì bạn sẽ phải tự tay inject từng ý cái. Việc này lại dẫn tới khó khăn trong việc code, quản lý code và dependency</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package org.tmsblog.patterns.di;

public class Main {
    public static void main(String[] args) {
       Outfit bikini = new Bikini();
       Accessories gucci = new GucciAccessories();
       HairStyle hair = new KoreanHairStyle();
       Girl fukada = new Girl(bikini, gucci, hair);
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">org.tmsblog.patterns.di</span><span style="color: #F6F6F4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Main</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">void</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">main</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1; font-style: italic">String</span><span style="color: #F6F6F4">[] </span><span style="color: #FFB86C; font-style: italic">args</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">       </span><span style="color: #97E1F1; font-style: italic">Outfit</span><span style="color: #F6F6F4"> bikini </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">Bikini</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">       </span><span style="color: #97E1F1; font-style: italic">Accessories</span><span style="color: #F6F6F4"> gucci </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">GucciAccessories</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">       </span><span style="color: #97E1F1; font-style: italic">HairStyle</span><span style="color: #F6F6F4"> hair </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">KoreanHairStyle</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">       </span><span style="color: #97E1F1; font-style: italic">Girl</span><span style="color: #F6F6F4"> fukada </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">Girl</span><span style="color: #F6F6F4">(bikini, gucci, hair);</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:16px">Vãi chưởng nhỉ, lúc này có thằng nào làm hộ được chúng ta việc này thì tốt biết mấy =))).</p>



<p class="wp-block-paragraph" style="font-size:16px">Bây giờ giả sử, chúng ta định nghĩa trước toàn bộ các&nbsp;<em>dependency</em>&nbsp;có trong Project, mô tả nó và tống nó vào 1 cái&nbsp;<code>kho</code>&nbsp;và giao cho một thằng tên là&nbsp;<code>framework</code>&nbsp;quản lý. Bất kỳ các&nbsp;<code>Class</code>&nbsp;nào khi khởi tạo, nó cần dependency gì, thì cái&nbsp;<code>framework</code>&nbsp;này sẽ tự tìm trong&nbsp;<code>kho</code>&nbsp;rồi&nbsp;<em>inject</em>&nbsp;vào đối tượng thay chúng ta. sẽ tiện hơn phải không?</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1020" height="1024" src="http://blog.tomosia.com.vn/wp-content/uploads/2024/02/Screenshot-2024-02-27-at-11.27.02-1020x1024.png" alt="" class="wp-image-3185" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2024/02/Screenshot-2024-02-27-at-11.27.02-1020x1024.png 1020w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/Screenshot-2024-02-27-at-11.27.02-300x300.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/Screenshot-2024-02-27-at-11.27.02-150x150.png 150w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/Screenshot-2024-02-27-at-11.27.02-768x771.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/Screenshot-2024-02-27-at-11.27.02-1530x1536.png 1530w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/Screenshot-2024-02-27-at-11.27.02-80x80.png 80w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/Screenshot-2024-02-27-at-11.27.02-380x381.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/Screenshot-2024-02-27-at-11.27.02-800x803.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/Screenshot-2024-02-27-at-11.27.02-1160x1164.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/Screenshot-2024-02-27-at-11.27.02.png 1590w" sizes="auto, (max-width: 1020px) 100vw, 1020px" /></figure>



<p class="wp-block-paragraph" style="font-size:16px">Úi dời ơi, tuyệt chứ nhỉ, đó cũng chính là nguyên lý chính của&nbsp;<strong>Inversion of Control (IOC)&nbsp;&#8211;&nbsp;Đảo chiều sự điều khiển</strong></p>



<p class="wp-block-paragraph" style="font-size:16px">Nguyên văn Wiki:</p>



<ul class="wp-block-list">
<li style="font-size:16px"><span style="color: rgb(55, 53, 47); font-family: Inter, Inter-fallback, Helvetica, &quot;Apple Color Emoji&quot;, &quot;Segoe UI Emoji&quot;, NotoColorEmoji, &quot;Noto Color Emoji&quot;, &quot;Segoe UI Symbol&quot;, &quot;Android Emoji&quot;, EmojiSymbols, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, Roboto, &quot;Helvetica Neue&quot;, &quot;Noto Sans&quot;, sans-serif; font-size: 19.2px; letter-spacing: -0.1px; background-color: rgb(251, 251, 253);">Inversion of Control is a programming principle. flow of control within the application is not controlled by the application itself, but rather by the underlying framework.</span></li>
</ul>



<p class="wp-block-paragraph" style="font-size:16px">Khi đó, code chúng ta sẽ chỉ cần như này, để lấy ra đối tượng <strong>Girl</strong>, còn trong <strong>Girl.java</strong> anh em tiêm chích <strong>Accessories</strong> và <strong>HairStyle</strong> như <strong>Outfit</strong> nha:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package org.tmsblog.patterns.di;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(Main.class, args);

        Girl girl = context.getBean(Girl.class);
        // Anh em muốn lấy gì từ Girl thì cứ chấm mút ra nha =))
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">org.tmsblog.patterns.di</span><span style="color: #F6F6F4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> org.springframework.boot.SpringApplication;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> org.springframework.boot.autoconfigure.SpringBootApplication;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> org.springframework.context.ApplicationContext;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">@</span><span style="color: #97E1F1; font-style: italic">SpringBootApplication</span></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Main</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">void</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">main</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1; font-style: italic">String</span><span style="color: #F6F6F4">[] </span><span style="color: #FFB86C; font-style: italic">args</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1; font-style: italic">ApplicationContext</span><span style="color: #F6F6F4"> context </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> SpringApplication.</span><span style="color: #62E884">run</span><span style="color: #F6F6F4">(Main.class, args);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1; font-style: italic">Girl</span><span style="color: #F6F6F4"> girl </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> context.</span><span style="color: #62E884">getBean</span><span style="color: #F6F6F4">(Girl.class);</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #7B7F8B">// Anh em muốn lấy gì từ Girl thì cứ chấm mút ra nha =))</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:16px"><strong>Lời kết:</strong></p>



<p class="wp-block-paragraph" style="font-size:16px"><strong>Dependency Injection</strong> và <strong>IoC container </strong>là những khái niệm rất quan trọng, hầu hết các ứng dụng, framework hiện tại đều sử dụng nó. Chúng ta cần tìm hiểu để biết rõ DI và IoC được ứng dụng trong trường hợp nào. Nếu áp dụng hợp lý code của chúng ta sẽ ít kết dính hơn (loose coupling), dễ bảo trì, dễ test hơn, tổ chức ứng dụng hợp lý và gọn gàng hơn.</p>



<p class="wp-block-paragraph">Source Code tham khảo tại <a href="https://github.com/chienhnc/di">đây</a>: </p>
<p>The post <a href="https://blog.tomosia.com.vn/thong-nao-dependency-injection-bang-eimi-fukada/">Thông não Dependency Injection bằng Eimi Fukada</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/thong-nao-dependency-injection-bang-eimi-fukada/feed/</wfw:commentRss>
			<slash:comments>9</slash:comments>
		
		
			</item>
		<item>
		<title>Hướng dẫn Java Design Pattern – Factory Method</title>
		<link>https://blog.tomosia.com.vn/huong-dan-java-design-pattern-factory-method/</link>
					<comments>https://blog.tomosia.com.vn/huong-dan-java-design-pattern-factory-method/#comments</comments>
		
		<dc:creator><![CDATA[admin_tomosia]]></dc:creator>
		<pubDate>Fri, 01 Mar 2024 02:38:28 +0000</pubDate>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[Design Patterns]]></category>
		<category><![CDATA[Factory Method]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=3166</guid>

					<description><![CDATA[<p>Trong bài trước chúng ta đã tìm hiểu về&#160;Singleton Design Pattern.&#160;Để tiếp tục series về Design Pattern hôm&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/huong-dan-java-design-pattern-factory-method/">Hướng dẫn Java Design Pattern – Factory Method</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph" style="font-size:16px">Trong bài trước chúng ta đã tìm hiểu về&nbsp;<a href="http://blog.tomosia.com.vn/huong-dan-java-design-pattern-singleton/">Singleton Design Pattern</a>.&nbsp;Để tiếp tục series về <strong>Design Pattern</strong> hôm nay chúng ta sẽ tiếp tục tìm hiểu một Pattern khác trong nhóm&nbsp;<strong>Creational Design Pattern</strong>&nbsp;là&nbsp;<strong>Factory Method Design Pattern</strong>.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="719" src="http://blog.tomosia.com.vn/wp-content/uploads/2024/02/1_OrtMdN8PgLN7SwrEnhjA_Q-1024x719.png" alt="" class="wp-image-3169" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2024/02/1_OrtMdN8PgLN7SwrEnhjA_Q-1024x719.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/1_OrtMdN8PgLN7SwrEnhjA_Q-300x211.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/1_OrtMdN8PgLN7SwrEnhjA_Q-768x539.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/1_OrtMdN8PgLN7SwrEnhjA_Q-200x140.png 200w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/1_OrtMdN8PgLN7SwrEnhjA_Q-380x267.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/1_OrtMdN8PgLN7SwrEnhjA_Q-800x562.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/1_OrtMdN8PgLN7SwrEnhjA_Q-1160x814.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/1_OrtMdN8PgLN7SwrEnhjA_Q.png 1400w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h2 id="1-factory-method-pattern-la-gi" class="wp-block-heading" style="font-size:30px"><strong>1. Factory Method Pattern là gì?</strong></h2>



<p class="wp-block-paragraph" style="font-size:16px"><strong>Factory Method Design Pattern&nbsp;</strong>hay gọi ngắn gọn là&nbsp;<strong>Factory Pattern</strong>&nbsp;là một trong những Pattern thuộc nhóm&nbsp;<strong>Creational Design Pattern</strong>. Nhiệm vụ của Factory Pattern là quản lý và trả về các đối tượng theo yêu cầu, giúp cho việc khởi tạo đổi tượng một cách linh hoạt hơn.</p>



<p class="wp-block-paragraph" style="font-size:16px">Trong Factory Pattern, chúng ta tạo đối tượng mà không để lộ logic tạo đối tượng ở phía người dùng và tham chiếu đến đối tượng mới được tạo ra bằng cách sử dụng một interface chung.</p>



<p class="wp-block-paragraph" style="font-size:16px">Factory Pattern được sử dụng khi có một class cha (super-class) với nhiều class con (sub-class), dựa trên đầu vào và phải trả về 1 trong những class con đó.</p>



<h2 id="2-cai-dat-factory-pattern-nhu-the-nao" class="wp-block-heading" style="font-size:30px"><strong>2. Cài đặt Factory Pattern như thế nào?</strong></h2>



<p class="wp-block-paragraph" style="font-size:16px">Một Factory Pattern bao gồm các thành phần cơ bản sau:</p>



<ul class="wp-block-list">
<li style="font-size:16px"><strong>Super Class</strong>: môt supper class trong Factory Pattern có thể là một&nbsp;<strong>interface</strong>,&nbsp;<strong>abstract class</strong>&nbsp;hay một&nbsp;<strong>class</strong>&nbsp;thông thường.</li>



<li style="font-size:16px"><strong>Sub Classes</strong>: các sub class sẽ implement các phương thức của&nbsp;<strong>supper class</strong>&nbsp;theo nghiệp vụ riêng của nó.</li>



<li style="font-size:16px"><strong>Factory Class</strong>: một class chịu tránh nhiệm khởi tạo các đối tượng&nbsp;<strong>sub class</strong>&nbsp;dựa theo tham số đầu vào. Lưu ý: lớp này là&nbsp;<a href="http://blog.tomosia.com.vn/huong-dan-java-design-pattern-singleton/"><strong>Singleton</strong>&nbsp;</a>hoặc cung cấp một&nbsp;<strong>public&nbsp;static method</strong>&nbsp;cho việc truy xuất và khởi tạo đối tượng.&nbsp;Factory class sử dụng if-else hoặc switch-case để xác định class con đầu ra.</li>
</ul>



<p class="wp-block-paragraph" style="font-size:16px">Ví dụ: Tất cả hệ thống Football Club có cung cấp API để truy cập đến hệ thống của họ. Team được giao nhiệm vụ thiết kế một API để client có thể sử dụng dịch vụ của một CLB bất kỳ. Hiện tại, phía client chỉ cần sử dụng dịch vụ của 1 số đội bóng như Becamex Bình Dương, Hoàng Anh Gia Lai, Hà Nội, &#8230; Tuy nhiên để dễ mở rộng sau này, và phía client mong muốn không cần phải thay đổi code của họ khi cần sử dụng thêm dịch vụ của đội bóng khác. Với yêu cầu như vậy, chúng ta có thể sử dụng một Pattern phù hợp là Factory Method Pattern.</p>



<p class="wp-block-paragraph" style="font-size:16px">Hệ thống được minh họa như sau:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="801" height="421" src="http://blog.tomosia.com.vn/wp-content/uploads/2024/02/Untitled1.drawio-1.png" alt="" class="wp-image-3168" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2024/02/Untitled1.drawio-1.png 801w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/Untitled1.drawio-1-300x158.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/Untitled1.drawio-1-768x404.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/Untitled1.drawio-1-380x200.png 380w" sizes="auto, (max-width: 801px) 100vw, 801px" /></figure>



<p class="wp-block-paragraph" style="font-size:16px">Chương trình được cài đặt theo Factory Pattern như sau:</p>



<p class="wp-block-paragraph" style="font-size:16px">Supper Class:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package com.tmsblog.patterns.creational.factorymethod;

public interface FC {
    String getFCName();
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">com.tmsblog.patterns.creational.factorymethod</span><span style="color: #F6F6F4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">interface</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">FC</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #97E1F1; font-style: italic">String</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">getFCName</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:16px">Sub Classes:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package com.tmsblog.patterns.creational.factorymethod;
 
public class BECAMEXBinhDuong implements FC {
 
    @Override
    public String getFCName() {
        return &quot;Becamex Binh Duong&quot;;
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">com.tmsblog.patterns.creational.factorymethod</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">BECAMEXBinhDuong</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">implements</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">FC</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    @</span><span style="color: #97E1F1; font-style: italic">Override</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">String</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">getFCName</span><span style="color: #F6F6F4">() {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">Becamex Binh Duong</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package com.tmsblog.patterns.creational.factorymethod;
 
public class HAGL implements FC {
 
    @Override
    public String getFCName() {
        return &quot;Hoang Anh Gia Lai&quot;;
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">com.tmsblog.patterns.creational.factorymethod</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">HAGL</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">implements</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">FC</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    @</span><span style="color: #97E1F1; font-style: italic">Override</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">String</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">getFCName</span><span style="color: #F6F6F4">() {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">Hoang Anh Gia Lai</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package com.tmsblog.patterns.creational.factorymethod;
 
public class Hanoi implements FC {
 
    @Override
    public String getFCName() {
        return &quot;Ha Noi&quot;;
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">com.tmsblog.patterns.creational.factorymethod</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Hanoi</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">implements</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">FC</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    @</span><span style="color: #97E1F1; font-style: italic">Override</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">String</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">getFCName</span><span style="color: #F6F6F4">() {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">Ha Noi</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:16px">Factory class:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package org.tmsblog.patterns.creational.factorymethod;

import org.tmsblog.enums.FCType;

public class FCFactory {
    public FCFactory() {
    }

    public static final FC getFC(FCType fcType) {
        switch (fcType) {
            case BECAMEX_BD:
                return new BECAMEXBinhDuong();
            case HAGL:
                return new HAGL();
            case HANOI:
                return new Hanoi();
            default:
                throw new IllegalArgumentException(&quot;This fc type is unsupported&quot;);
        }
    }
}
" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">org.tmsblog.patterns.creational.factorymethod</span><span style="color: #F6F6F4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> org.tmsblog.enums.FCType;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">FCFactory</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">FCFactory</span><span style="color: #F6F6F4">() {</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">final</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">FC</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">getFC</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1; font-style: italic">FCType</span><span style="color: #F6F6F4"> </span><span style="color: #FFB86C; font-style: italic">fcType</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">switch</span><span style="color: #F6F6F4"> (fcType) {</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">case</span><span style="color: #F6F6F4"> BECAMEX_BD</span><span style="color: #F286C4">:</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">BECAMEXBinhDuong</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">case</span><span style="color: #F6F6F4"> HAGL</span><span style="color: #F286C4">:</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">HAGL</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">case</span><span style="color: #F6F6F4"> HANOI</span><span style="color: #F286C4">:</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">Hanoi</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">default:</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #F286C4">throw</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">IllegalArgumentException</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">This fc type is unsupported</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">);</span></span>
<span class="line"><span style="color: #F6F6F4">        }</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:16px">FC type:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package org.tmsblog.enums;

public enum FCType {
    BECAMEX_BD, HAGL, HANOI
}
" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">org.tmsblog.enums</span><span style="color: #F6F6F4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">enum</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">FCType</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #BF9EEE">BECAMEX_BD</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">HAGL</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">HANOI</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:16px">Client:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package org.tmsblog.patterns.creational.factorymethod;

import org.tmsblog.enums.FCType;

public class Client {
    public static void main(String[] args) {
        FC fc = FCFactory.getFC(FCType.HANOI);
        System.out.println(fc.getFCName());
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">org.tmsblog.patterns.creational.factorymethod</span><span style="color: #F6F6F4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> org.tmsblog.enums.FCType;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Client</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">void</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">main</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1; font-style: italic">String</span><span style="color: #F6F6F4">[] </span><span style="color: #FFB86C; font-style: italic">args</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1; font-style: italic">FC</span><span style="color: #F6F6F4"> fc </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> FCFactory.</span><span style="color: #62E884">getFC</span><span style="color: #F6F6F4">(FCType.HANOI);</span></span>
<span class="line"><span style="color: #F6F6F4">        System.out.</span><span style="color: #62E884">println</span><span style="color: #F6F6F4">(fc.</span><span style="color: #62E884">getFCName</span><span style="color: #F6F6F4">());</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:16px">Như anh em thấy, phía client chỉ cần gọi duy nhất một phương thức&nbsp;<strong>FCFactory.getFC()</strong>&nbsp;là có thể sử dụng được dịch vụ của một CLB bất kỳ.</p>



<p class="wp-block-paragraph" style="font-size:16px">Khi hệ thống muốn cung cấp thêm dịch vụ của một CLB khác, chẳng hạn Viettel, thì cần tạo thêm một class mới implement từ interface FC, và thêm vào logic khởi tạo FC trong Factory là xong. Nó không làm ảnh hưởng đến code ở phía Client.</p>



<h2 id="3-su-dung-factory-pattern-khi-nao" class="wp-block-heading" style="font-size:30px"><strong>3. Sử dụng Factory Pattern khi nào?</strong></h2>



<p class="wp-block-paragraph" style="font-size:16px">Factory Pattern được sử dụng khi:</p>



<ul class="wp-block-list">
<li style="font-size:16px">Chúng ta có một super class với nhiều class con và dựa trên đầu vào, chúng ta cần trả về một class con. Mô hình này giúp chúng ta đưa trách nhiệm của việc khởi tạo một lớp từ phía người dùng (client) sang lớp Factory.</li>



<li style="font-size:16px">Chúng ta không biết sau này sẽ cần đến những lớp con nào nữa. Khi cần mở rộng, hãy tạo ra sub class và implement thêm vào factory method cho việc khởi tạo sub class này.</li>
</ul>



<h2 id="4-loi-ich-cua-factory-pattern-la-gi" class="wp-block-heading" style="font-size:30px"><strong>4. Lợi ích của Factory Pattern là gì?</strong></h2>



<p class="wp-block-paragraph" style="font-size:16px">Lợi ích của Factory Pattern:</p>



<ul class="wp-block-list">
<li style="font-size:16px">Factory Pattern giúp giảm sự phụ thuộc giữa các module (loose coupling): cung cấp 1 hướng tiếp cận với Interface thay vì các implement. Giúp chuơng trình độc lập với những lớp cụ thể mà chúng ta cần tạo 1 đối tượng, code ở phía client không bị ảnh hưởng khi thay đổi logic ở factory hay sub class.</li>



<li style="font-size:16px">Mở rộng code dễ dàng hơn: khi cần mở rộng, chỉ việc tạo ra sub class và implement thêm vào factory method.</li>



<li style="font-size:16px">Khởi tạo các Objects mà che giấu đi xử lí logic của việc khởi tạo đấy. Người dùng không biết logic thực sực được khởi tạo bên dưới phương thức factory.</li>



<li style="font-size:16px">Dễ dàng quản lý life cycle của các Object được tạo bởi Factory Pattern.</li>



<li style="font-size:16px">Thống nhất về&nbsp;naming convention: giúp cho các&nbsp;developer có thể hiểu về cấu trúc source code.</li>
</ul>



<p class="wp-block-paragraph" style="font-size:16px">Anh em có thể thấy Factory Pattern được áp dụng trong:</p>



<ul class="wp-block-list">
<li style="font-size:16px">JDK:&nbsp;java.util.Calendar, ResourceBundle, NumberFormat, …</li>



<li style="font-size:16px">BeanFactory trong Spring Framework.</li>



<li style="font-size:16px">SessionFactory trong Hibernate Framework.</li>
</ul>



<p class="wp-block-paragraph" style="font-size:16px">Như vậy tôi đã giới thiệu xong về Factory Method Pattern, hy vọng bài viết sẽ giúp ích cho anh em trong quá trình xây dựng và phát triển ứng dụng. Hẹn gặp lại ở các bài viết tiếp theo.</p>



<p class="wp-block-paragraph" style="font-size:16px"><strong>Tài liệu tham khảo:</strong></p>



<ul class="wp-block-list">
<li style="font-size:16px"><a href="https://refactoring.guru/design-patterns/factory-method">https://refactoring.guru/design-patterns/factory-method</a></li>



<li style="font-size:16px"><a href="https://sourcemaking.com/design_patterns/factory_method">https://sourcemaking.com/design_patterns/factory_method</a></li>



<li style="font-size:16px"><a href="https://www.tutorialspoint.com/design_pattern/factory_pattern.htm">https://www.tutorialspoint.com/design_pattern/factory_pattern.htm</a></li>
</ul>
<p>The post <a href="https://blog.tomosia.com.vn/huong-dan-java-design-pattern-factory-method/">Hướng dẫn Java Design Pattern – Factory Method</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/huong-dan-java-design-pattern-factory-method/feed/</wfw:commentRss>
			<slash:comments>557</slash:comments>
		
		
			</item>
		<item>
		<title>Hướng dẫn Java Design Pattern – Singleton</title>
		<link>https://blog.tomosia.com.vn/huong-dan-java-design-pattern-singleton/</link>
					<comments>https://blog.tomosia.com.vn/huong-dan-java-design-pattern-singleton/#comments</comments>
		
		<dc:creator><![CDATA[admin_tomosia]]></dc:creator>
		<pubDate>Tue, 20 Feb 2024 08:21:39 +0000</pubDate>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Solution]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[Design Patterns]]></category>
		<category><![CDATA[Singleton]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=3157</guid>

					<description><![CDATA[<p>Hi các đồng dâm, tôi lại quay trở lại rồi đây, trong bài trước tôi đã giới thiệu&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/huong-dan-java-design-pattern-singleton/">Hướng dẫn Java Design Pattern – Singleton</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph" style="font-size:16px">Hi các đồng dâm, tôi lại quay trở lại rồi đây, trong bài trước tôi đã giới thiệu với anh em kiến thức cơ bản của <a href="http://blog.tomosia.com.vn/gioi-thieu-design-patterns/">Design Pattern</a>. Tiếp tục với series Design Patterns, đây sẽ là &#8220;<em>màn dạo đầu</em>&#8221; nhé =))))</p>



<p class="wp-block-paragraph" style="font-size:16px">Đôi khi, trong quá trình phân tích thiết kế một hệ thống, chúng ta mong muốn có những đối tượng cần tồn tại duy nhất và có thể truy xuất mọi lúc mọi nơi. Làm thế nào để hiện thực được một đối tượng như thế khi xây dựng mã nguồn? Chúng ta có thể nghĩ tới việc sử dụng một biến toàn cục (global variable : public static final). Tuy nhiên, việc sử dụng biến toàn cục nó phá vỡ quy tắc của&nbsp;<strong>OOP (encapsulation)</strong>. Để giải bài toán trên, người ta hướng đến một giải pháp là sử dụng&nbsp;<strong>Singleton pattern</strong>.</p>



<h2 id="1-singleton-pattern-la-gi" class="wp-block-heading" style="font-size:30px"><strong>1. Singleton Pattern là gì?</strong></h2>



<p class="wp-block-paragraph" style="font-size:16px"><strong>Singleton</strong>&nbsp;là 1 trong 5 design pattern của nhóm&nbsp;<strong>Creational Design Pattern</strong>.</p>



<p class="wp-block-paragraph" style="font-size:16px"><strong>Singleton</strong>&nbsp;đảm bảo chỉ duy nhất&nbsp;<strong>một thể hiện (instance)</strong>&nbsp;được tạo ra và nó sẽ cung cấp cho anh em một method để có thể truy xuất được thể hiện duy nhất đó mọi lúc mọi nơi trong chương trình.</p>



<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-2 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="640" data-id="3162" src="http://blog.tomosia.com.vn/wp-content/uploads/2024/02/image-9-1024x640.png" alt="" class="wp-image-3162" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2024/02/image-9-1024x640.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/image-9-300x188.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/image-9-768x480.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/image-9-1536x960.png 1536w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/image-9-380x238.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/image-9-800x500.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/image-9-1160x725.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2024/02/image-9.png 1920w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</figure>



<p class="wp-block-paragraph" style="font-size:16px">Sử dụng Singleton khi chúng ta muốn:</p>



<ul class="wp-block-list" style="font-size:16px">
<li>Đảm bảo rằng chỉ có một instance của lớp.</li>



<li>Việc quản lý việc truy cập tốt hơn vì chỉ có một thể hiện duy nhất.</li>



<li>Có thể quản lý số lượng thể hiện của một lớp trong giớn hạn chỉ định.</li>
</ul>



<h2 id="2-implement-singleton-pattern-nhu-the-nao" class="wp-block-heading" style="font-size:30px"><strong>2. Implement Singleton Pattern như thế nào?</strong></h2>



<p class="wp-block-paragraph" style="font-size:16px">Có rất nhiều cách để implement Singleton Pattern. Nhưng dù cho việc implement bằng cách nào đi nữa cũng dựa vào nguyên tắc dưới đây cơ bản dưới đây:</p>



<ul class="wp-block-list">
<li style="font-size:16px"><strong>private constructor</strong>&nbsp;để hạn chế truy cập từ class bên ngoài.</li>



<li style="font-size:16px">Đặt&nbsp;<strong>private static final variable</strong>&nbsp;đảm bảo biến chỉ được khởi tạo trong class.</li>



<li style="font-size:16px">Có một method&nbsp;<strong>public static</strong>&nbsp;để&nbsp;<strong>return instance</strong>&nbsp;được khởi tạo ở trên.</li>
</ul>



<h2 id="3-nhung-cach-nao-de-implement-singleton-pattern" class="wp-block-heading" style="font-size:30px"><strong>3. Những cách nào để implement Singleton Pattern</strong></h2>



<p class="wp-block-paragraph" style="font-size:16px">Dựa trên những nguyên tắc thiết kế Singleton ở trên, chúng ta có các cách implement singleton như sau:</p>



<p class="wp-block-paragraph" style="font-size:23px"><strong>3.1. Eager initialization</strong></p>



<p class="wp-block-paragraph" style="font-size:16px"><strong>Singleton</strong>&nbsp;Class được khởi tạo ngay khi được gọi đến.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package com.tmsblog.patterns.creational.singleton;
 
public class EagerInitializedSingleton {
 
    private static final EagerInitializedSingleton INSTANCE = new EagerInitializedSingleton();
 
    private EagerInitializedSingleton() {}
 
    public static EagerInitializedSingleton getInstance() {
        return INSTANCE;
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">com.tmsblog.patterns.creational.singleton</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">EagerInitializedSingleton</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">final</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">EagerInitializedSingleton</span><span style="color: #F6F6F4"> INSTANCE </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">EagerInitializedSingleton</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">EagerInitializedSingleton</span><span style="color: #F6F6F4">() {}</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">EagerInitializedSingleton</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">getInstance</span><span style="color: #F6F6F4">() {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> INSTANCE;</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:16px">Eager initialization là cách tiếp cận tốt, dễ cài đặt, tuy nhiên nó có một nhược điểm mặc dù instance đã được khởi tạo nhưng có thể sẽ không dùng tới và nó dễ dàng bị phá vỡ bởi Reflection.</p>



<p class="wp-block-paragraph" style="font-size:23px"><strong>3.2. Static block initialization</strong></p>



<p class="wp-block-paragraph" style="font-size:16px">Cách làm tương tự như&nbsp;<strong>Eager initialization</strong>&nbsp;chỉ khác phần&nbsp;<strong>static block</strong>&nbsp;cung cấp thêm lựa chọn cho việc handle exception hay các xử lý khác.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package com.tmsblog.patterns.creational.singleton;
 
public class StaticBlockSingleton {
 
    private static final StaticBlockSingleton INSTANCE;
 
    private StaticBlockSingleton() {}
 
    // Static block initialization for exception handling
    static {
        try {
            INSTANCE = new StaticBlockSingleton();
        } catch (Exception e) {
            throw new RuntimeException(&quot;Exception occured in creating singleton instance&quot;);
        }
    }
 
    public static StaticBlockSingleton getInstance() {
        return INSTANCE;
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">com.tmsblog.patterns.creational.singleton</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">StaticBlockSingleton</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">final</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">StaticBlockSingleton</span><span style="color: #F6F6F4"> INSTANCE;</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">StaticBlockSingleton</span><span style="color: #F6F6F4">() {}</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">// Static block initialization for exception handling</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">try</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">            INSTANCE </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">StaticBlockSingleton</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">        } </span><span style="color: #F286C4">catch</span><span style="color: #F6F6F4"> (</span><span style="color: #97E1F1; font-style: italic">Exception</span><span style="color: #F6F6F4"> </span><span style="color: #FFB86C; font-style: italic">e</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">throw</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">RuntimeException</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">Exception occured in creating singleton instance</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">);</span></span>
<span class="line"><span style="color: #F6F6F4">        }</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">StaticBlockSingleton</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">getInstance</span><span style="color: #F6F6F4">() {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> INSTANCE;</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:23px"><strong>3.3. Lazy Initialization</strong></p>



<p class="wp-block-paragraph" style="font-size:16px">Là một cách làm mang tính mở rộng hơn so với 2 cách làm trên và hoạt động tốt trong môi trường đơn luồng (single-thread).</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package com.tmsblog.patterns.creational.singleton;
 
public class LazyInitializedSingleton {
 
    private static LazyInitializedSingleton instance;
 
    private LazyInitializedSingleton() {}
 
    public static LazyInitializedSingleton getInstance() {
        if (instance == null) {
            instance = new LazyInitializedSingleton();
        }
        return instance;
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">com.tmsblog.patterns.creational.singleton</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">LazyInitializedSingleton</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">LazyInitializedSingleton</span><span style="color: #F6F6F4"> instance;</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">LazyInitializedSingleton</span><span style="color: #F6F6F4">() {}</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">LazyInitializedSingleton</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">getInstance</span><span style="color: #F6F6F4">() {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> (instance </span><span style="color: #F286C4">==</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">null</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">            instance </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">LazyInitializedSingleton</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">        }</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> instance;</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:16px">Cách này đã khắc phục được nhược điểm của cách&nbsp;<strong>Eager initialization</strong>, chỉ khi nào&nbsp;<strong>getInstance()</strong>&nbsp;được gọi thì instance mới được khởi tạo. Tuy nhiên, cách này chỉ sử dụng tốt trong trường hợp đơn luồng (single-thread), trường hợp nếu có nhiều luồng (multi-thread) cùng chạy và cùng gọi hàm getInstance() tại cùng một thời điểm thì có thể có nhiều hơn 1 thể hiện của instance. Để khắc phục nhược điểm này chúng ta sử dụng&nbsp;<strong>Thread Safe Singleton</strong>.</p>



<p class="wp-block-paragraph" style="font-size:16px">Một nhược điểm nữa của&nbsp;<strong>Lazy Initialization</strong>&nbsp;cần quan tâm là: đối với thao tác create instance quá chậm thì người dùng có phải chờ lâu cho lần sử dụng đầu tiên.</p>



<p class="wp-block-paragraph" style="font-size:23px"><strong>3.4. Thread Safe Singleton</strong></p>



<p class="wp-block-paragraph" style="font-size:16px">Cách đơn giản nhất là chúng ta gọi phương thức&nbsp;<strong>synchronized</strong>&nbsp;của hàm&nbsp;<strong>getInstance()</strong>&nbsp;và như vậy hệ thống đảm bảo rằng tại cùng một thời điểm chỉ có thể có 1 luồng có thể truy cập vào hàm getInstance() và đảm bảo rằng chỉ có duy nhất 1 thể hiện của class.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package com.tmsblog.patterns.creational.singleton;
 
public class ThreadSafeLazyInitializedSingleton {
 
    private static volatile ThreadSafeLazyInitializedSingleton instance;
 
    private ThreadSafeLazyInitializedSingleton() {}
 
    public static synchronized ThreadSafeLazyInitializedSingleton getInstance() {
        if (instance == null) {
            instance = new ThreadSafeLazyInitializedSingleton();
        }
        return instance;
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">com.tmsblog.patterns.creational.singleton</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">ThreadSafeLazyInitializedSingleton</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">volatile</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">ThreadSafeLazyInitializedSingleton</span><span style="color: #F6F6F4"> instance;</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">ThreadSafeLazyInitializedSingleton</span><span style="color: #F6F6F4">() {}</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">synchronized</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">ThreadSafeLazyInitializedSingleton</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">getInstance</span><span style="color: #F6F6F4">() {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> (instance </span><span style="color: #F286C4">==</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">null</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">            instance </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">ThreadSafeLazyInitializedSingleton</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">        }</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> instance;</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<blockquote class="wp-block-quote is-style-default is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"></p>
<cite>Biến&nbsp;<strong>volatile</strong>&nbsp;trong Java có tác dụng thông báo sự thay đổi giá trị của biến tới các thread khác&nbsp;nhau nếu biến này đang được sử dụng trong nhiều thread.</cite></blockquote>



<p class="wp-block-paragraph" style="font-size:16px">Cách này có nhược điểm là một phương thức&nbsp;<strong>synchronized</strong>&nbsp;sẽ chạy rất chậm và tốn hiệu năng, bất kỳ Thread nào gọi đến đều phải chờ nếu có một Thread khác đang sử dụng. Có những tác vụ xử lý trước và sau khi tạo thể hiện không cần thiết phải block. Vì vậy chúng ta cần cải tiến nó đi 1 chút với&nbsp;<strong>Double Check Locking Singleton</strong>.</p>



<p class="wp-block-paragraph" style="font-size:23px"><strong>3.5. Double Check Locking Singleton</strong></p>



<p class="wp-block-paragraph" style="font-size:16px">Để implement theo cách này, chúng ta sẽ kiểm tra sự tồn tại thể hiện của lớp, với sự hỗ trợ của đồng bộ hóa, hai lần trước khi khởi tạo. Phải khai báo&nbsp;<strong>volatile</strong>&nbsp;cho instance để tránh lớp làm việc không chính xác do quá trình tối ưu hóa của trình biên dịch.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package com.tmsblog.patterns.creational.singleton;
 
public class DoubleCheckLockingSingleton {
 
    private static volatile DoubleCheckLockingSingleton instance;
 
    private DoubleCheckLockingSingleton() {}
 
    public static DoubleCheckLockingSingleton getInstance() {
        // Do something before get instance ...
        if (instance == null) {
            // Do the task too long before create instance ...
            // Block so other threads cannot come into while initialize
            synchronized (DoubleCheckLockingSingleton.class) {
                // Re-check again. Maybe another thread has initialized before
                if (instance == null) {
                    instance = new DoubleCheckLockingSingleton();
                }
            }
        }
        // Do something after get instance ...
        return instance;
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">com.tmsblog.patterns.creational.singleton</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">DoubleCheckLockingSingleton</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">volatile</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">DoubleCheckLockingSingleton</span><span style="color: #F6F6F4"> instance;</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">DoubleCheckLockingSingleton</span><span style="color: #F6F6F4">() {}</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">DoubleCheckLockingSingleton</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">getInstance</span><span style="color: #F6F6F4">() {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #7B7F8B">// Do something before get instance ...</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> (instance </span><span style="color: #F286C4">==</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">null</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #7B7F8B">// Do the task too long before create instance ...</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #7B7F8B">// Block so other threads cannot come into while initialize</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">synchronized</span><span style="color: #F6F6F4"> (DoubleCheckLockingSingleton.class) {</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #7B7F8B">// Re-check again. Maybe another thread has initialized before</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> (instance </span><span style="color: #F286C4">==</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">null</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">                    instance </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">DoubleCheckLockingSingleton</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">                }</span></span>
<span class="line"><span style="color: #F6F6F4">            }</span></span>
<span class="line"><span style="color: #F6F6F4">        }</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #7B7F8B">// Do something after get instance ...</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> instance;</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:23px"><strong>3.6. Bill Pugh Singleton Implementation</strong></p>



<p class="wp-block-paragraph" style="font-size:16px">Với cách làm này bạn sẽ tạo ra&nbsp;<strong>static nested class</strong>&nbsp;với vai trò 1 Helper khi muốn tách biệt chức năng cho 1 class function rõ ràng hơn. Đây là cách thường hay được sử dụng và có hiệu suất tốt (theo các chuyên gia đánh giá :v).</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package com.tmsblog.patterns.creational.singleton;
 
public class BillPughSingleton {
 
    private BillPughSingleton() {}
 
    public static BillPughSingleton getInstance() {
        return SingletonHelper.INSTANCE;
    }
 
    private static class SingletonHelper {
        private static final BillPughSingleton INSTANCE = new BillPughSingleton();
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">com.tmsblog.patterns.creational.singleton</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">BillPughSingleton</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">BillPughSingleton</span><span style="color: #F6F6F4">() {}</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">BillPughSingleton</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">getInstance</span><span style="color: #F6F6F4">() {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> SingletonHelper.INSTANCE;</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">SingletonHelper</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">final</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">BillPughSingleton</span><span style="color: #F6F6F4"> INSTANCE </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">BillPughSingleton</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:16px">Khi Singleton được tải vào bộ nhớ thì SingletonHelper chưa được tải vào. Nó chỉ được tải khi và chỉ khi phương thức getInstance() được gọi. Với cách này tránh được lỗi cơ chế khởi tạo instance của Singleton trong Multi-Thread, performance cao do tách biệt được quá trình xử lý. Do đó, cách làm này được đánh giá là cách triển khai Singleton nhanh và hiệu quả nhất.</p>



<p class="wp-block-paragraph" style="font-size:23px"><strong>3.7. Phá vỡ cấu trúc Singleton Pattern bằng Reflection</strong></p>



<p class="wp-block-paragraph" style="font-size:16px"><strong>Reflection</strong>&nbsp;có thể được dùng để phá vỡ Pattern của&nbsp;<strong>Eager Initialization</strong>&nbsp;ở trên.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package com.tmsblog.patterns.creational.singleton;
 
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
 
public class ReflectionBreakSingleton {
 
    public static void main(String[] args)
            throws InstantiationException, IllegalAccessException, InvocationTargetException {
         
        EagerInitializedSingleton instanceOne = EagerInitializedSingleton.getInstance();
        EagerInitializedSingleton instanceTwo = null;
 
        Constructor&lt;?&gt;[] constructors = EagerInitializedSingleton.class.getDeclaredConstructors();
        for (Constructor&lt;?&gt; constructor : constructors) {
            constructor.setAccessible(true);
            instanceTwo = (EagerInitializedSingleton) constructor.newInstance();
        }
 
        System.out.println(instanceOne.hashCode());
        System.out.println(instanceTwo.hashCode());
    }
}
" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">com.tmsblog.patterns.creational.singleton</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> java.lang.reflect.Constructor;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> java.lang.reflect.InvocationTargetException;</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">ReflectionBreakSingleton</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">void</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">main</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1; font-style: italic">String</span><span style="color: #F6F6F4">[] </span><span style="color: #FFB86C; font-style: italic">args</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">throws</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">InstantiationException</span><span style="color: #F6F6F4">, </span><span style="color: #97E1F1; font-style: italic">IllegalAccessException</span><span style="color: #F6F6F4">, </span><span style="color: #97E1F1; font-style: italic">InvocationTargetException</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">         </span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1; font-style: italic">EagerInitializedSingleton</span><span style="color: #F6F6F4"> instanceOne </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> EagerInitializedSingleton.</span><span style="color: #62E884">getInstance</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1; font-style: italic">EagerInitializedSingleton</span><span style="color: #F6F6F4"> instanceTwo </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">null</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1; font-style: italic">Constructor</span><span style="color: #F6F6F4">&lt;</span><span style="color: #97E1F1; font-style: italic">?</span><span style="color: #F6F6F4">&gt;[] constructors </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> EagerInitializedSingleton.class.</span><span style="color: #62E884">getDeclaredConstructors</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">for</span><span style="color: #F6F6F4"> (</span><span style="color: #97E1F1; font-style: italic">Constructor</span><span style="color: #F6F6F4">&lt;</span><span style="color: #97E1F1; font-style: italic">?</span><span style="color: #F6F6F4">&gt; constructor </span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> constructors) {</span></span>
<span class="line"><span style="color: #F6F6F4">            constructor.</span><span style="color: #62E884">setAccessible</span><span style="color: #F6F6F4">(</span><span style="color: #BF9EEE">true</span><span style="color: #F6F6F4">);</span></span>
<span class="line"><span style="color: #F6F6F4">            instanceTwo </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> (EagerInitializedSingleton) constructor.</span><span style="color: #62E884">newInstance</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">        }</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">        System.out.</span><span style="color: #62E884">println</span><span style="color: #F6F6F4">(instanceOne.</span><span style="color: #62E884">hashCode</span><span style="color: #F6F6F4">());</span></span>
<span class="line"><span style="color: #F6F6F4">        System.out.</span><span style="color: #62E884">println</span><span style="color: #F6F6F4">(instanceTwo.</span><span style="color: #62E884">hashCode</span><span style="color: #F6F6F4">());</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">Output của chương trình:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="2018699554
1311053135" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #BF9EEE">2018699554</span></span>
<span class="line"><span style="color: #BF9EEE">1311053135</span></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:16px">Tương tự&nbsp;<strong>Eager Initialization</strong>, implement theo&nbsp;<strong>Bill Pugh Singleton</strong>&nbsp;cũng bị break bởi Reflection.</p>



<p class="wp-block-paragraph" style="font-size:23px"><strong>3.8. Enum Singleton</strong></p>



<p class="wp-block-paragraph" style="font-size:16px">Khi dùng&nbsp;<strong>enum</strong>&nbsp;thì các params chỉ được khởi tạo 1 lần duy nhất, đây cũng là cách giúp anh em tạo ra Singleton instance.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package com.tmsblog.patterns.creational.singleton;
 
/**
 * Singleton implementation using enum initialization
 */
public enum EnumSingleton {
 
    INSTANCE;
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">com.tmsblog.patterns.creational.singleton</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #7B7F8B">/**</span></span>
<span class="line"><span style="color: #7B7F8B"> * Singleton implementation using enum initialization</span></span>
<span class="line"><span style="color: #7B7F8B"> */</span></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">enum</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">EnumSingleton</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #BF9EEE">INSTANCE</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:16px">Lưu ý:</p>



<ul class="wp-block-list">
<li style="font-size:16px">Enum có thể sử dụng như một Singleton, nhưng nó có nhược điểm là không thể extends từ một lớp được, nên khi sử dụng cần xem xét vấn đề này.</li>



<li style="font-size:16px">Hàm&nbsp;<strong>constructor</strong>&nbsp;của&nbsp;<strong>enum</strong>&nbsp;là&nbsp;<strong>lazy</strong>, nghĩa là khi được sử dụng mới chạy hàm khởi tạo và nó chỉ chạy duy nhất một lần. Nếu muốn sử dụng như một eager singleton thì cần gọi thực thi trong một&nbsp;<strong>static block</strong>&nbsp;khi start chương trình.</li>
</ul>



<p class="wp-block-paragraph" style="font-size:16px">So sánh giữa 2 cách sử dụng&nbsp;<strong>enum initialization</strong>&nbsp;và&nbsp;<strong>static block initialization</strong>&nbsp;<strong>method</strong>, enum có một điểm rất mạnh khi giải quyết về vấn đề&nbsp;<strong>Serialization/ Deserialization</strong>.</p>



<p class="wp-block-paragraph" style="font-size:23px"><strong>3.9. Serialization and Singleton</strong></p>



<p class="wp-block-paragraph" style="font-size:16px">Đôi khi trong các hệ thống phân tán (distributed system), chúng ta cần implement interface&nbsp;<strong>Serializable</strong>&nbsp;trong lớp Singleton để chúng ta có thể lưu trữ trạng thái của nó trong file hệ thống và truy xuất lại nó sau.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package com.tmsblog.patterns.creational.singleton;
 
import java.io.ObjectStreamException;
import java.io.Serializable;
 
public class SerializedSingleton implements Serializable {
 
    private static final long serialVersionUID = 1741825395699241705L;
 
    private SerializedSingleton() {}
 
    private static class SingletonHelper {
        private static final SerializedSingleton instance = new SerializedSingleton();
    }
 
    public static SerializedSingleton getInstance() {
        return SingletonHelper.instance;
    }
     
    /**
     * Special hook provided by serialization where developer can control what object needs to sent.
     * However this method is invoked on the new object instance created by de serialization process.
     *
     * @return
     * @throws ObjectStreamException
     */
//    private Object readResolve() throws ObjectStreamException {
//        return SingletonHelper.instance;
//    }
 
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">com.tmsblog.patterns.creational.singleton</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> java.io.ObjectStreamException;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> java.io.Serializable;</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">SerializedSingleton</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">implements</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Serializable</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">final</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">long</span><span style="color: #F6F6F4"> serialVersionUID </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">1741825395699241705L</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">SerializedSingleton</span><span style="color: #F6F6F4">() {}</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">SingletonHelper</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">final</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">SerializedSingleton</span><span style="color: #F6F6F4"> instance </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">SerializedSingleton</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">SerializedSingleton</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">getInstance</span><span style="color: #F6F6F4">() {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> SingletonHelper.instance;</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">     </span></span>
<span class="line"><span style="color: #7B7F8B">    /**</span></span>
<span class="line"><span style="color: #7B7F8B">     * Special hook provided by serialization where developer can control what object needs to sent.</span></span>
<span class="line"><span style="color: #7B7F8B">     * However this method is invoked on the new object instance created by de serialization process.</span></span>
<span class="line"><span style="color: #7B7F8B">     *</span></span>
<span class="line"><span style="color: #7B7F8B">     * </span><span style="color: #F286C4">@return</span></span>
<span class="line"><span style="color: #7B7F8B">     * </span><span style="color: #F286C4">@throws</span><span style="color: #7B7F8B"> </span><span style="color: #97E1F1">ObjectStreamException</span></span>
<span class="line"><span style="color: #7B7F8B">     */</span></span>
<span class="line"><span style="color: #7B7F8B">//    private Object readResolve() throws ObjectStreamException {</span></span>
<span class="line"><span style="color: #7B7F8B">//        return SingletonHelper.instance;</span></span>
<span class="line"><span style="color: #7B7F8B">//    }</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:16px">Đoạn code test quá trình Serialize/ Deserialize:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="package com.tmsblog.patterns.creational.singleton;
 
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
 
public class SingletonSerializedTest {
 
    public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
 
        SerializedSingleton serializedSingleton1 = SerializedSingleton.getInstance();
        EnumSingleton enumSingleton1 = EnumSingleton.INSTANCE;
 
        ObjectOutput out = new ObjectOutputStream(new FileOutputStream(&quot;SingletonSerializedTest.txt&quot;));
        out.writeObject(serializedSingleton1);
        out.writeObject(enumSingleton1);
        out.close();
 
        // De-serialize from file to object
        ObjectInput in = new ObjectInputStream(new FileInputStream(&quot;SingletonSerializedTest.txt&quot;));
        SerializedSingleton serializedSingleton2 = (SerializedSingleton) in.readObject();
        EnumSingleton enumSingleton2 = (EnumSingleton) in.readObject();
        in.close();
 
        System.out.println(&quot;serializedSingleton1 hashCode=&quot; + serializedSingleton1.hashCode());
        System.out.println(&quot;serializedSingleton2 hashCode=&quot; + serializedSingleton2.hashCode());
        System.out.println(&quot;enumSingleton1 hashCode=&quot; + enumSingleton1.hashCode());
        System.out.println(&quot;enumSingleton2 hashCode=&quot; + enumSingleton2.hashCode());
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">package</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">com.tmsblog.patterns.creational.singleton</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> java.io.FileInputStream;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> java.io.FileNotFoundException;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> java.io.FileOutputStream;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> java.io.IOException;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> java.io.ObjectInput;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> java.io.ObjectInputStream;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> java.io.ObjectOutput;</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> java.io.ObjectOutputStream;</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">SingletonSerializedTest</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">static</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">void</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">main</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1; font-style: italic">String</span><span style="color: #F6F6F4">[] </span><span style="color: #FFB86C; font-style: italic">args</span><span style="color: #F6F6F4">) </span><span style="color: #F286C4">throws</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">FileNotFoundException</span><span style="color: #F6F6F4">, </span><span style="color: #97E1F1; font-style: italic">IOException</span><span style="color: #F6F6F4">, </span><span style="color: #97E1F1; font-style: italic">ClassNotFoundException</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1; font-style: italic">SerializedSingleton</span><span style="color: #F6F6F4"> serializedSingleton1 </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> SerializedSingleton.</span><span style="color: #62E884">getInstance</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1; font-style: italic">EnumSingleton</span><span style="color: #F6F6F4"> enumSingleton1 </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> EnumSingleton.INSTANCE;</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1; font-style: italic">ObjectOutput</span><span style="color: #F6F6F4"> out </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">ObjectOutputStream</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">FileOutputStream</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">SingletonSerializedTest.txt</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">));</span></span>
<span class="line"><span style="color: #F6F6F4">        out.</span><span style="color: #62E884">writeObject</span><span style="color: #F6F6F4">(serializedSingleton1);</span></span>
<span class="line"><span style="color: #F6F6F4">        out.</span><span style="color: #62E884">writeObject</span><span style="color: #F6F6F4">(enumSingleton1);</span></span>
<span class="line"><span style="color: #F6F6F4">        out.</span><span style="color: #62E884">close</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #7B7F8B">// De-serialize from file to object</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1; font-style: italic">ObjectInput</span><span style="color: #F6F6F4"> in </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">ObjectInputStream</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">FileInputStream</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">SingletonSerializedTest.txt</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">));</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1; font-style: italic">SerializedSingleton</span><span style="color: #F6F6F4"> serializedSingleton2 </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> (SerializedSingleton) in.</span><span style="color: #62E884">readObject</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1; font-style: italic">EnumSingleton</span><span style="color: #F6F6F4"> enumSingleton2 </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> (EnumSingleton) in.</span><span style="color: #62E884">readObject</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">        in.</span><span style="color: #62E884">close</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #F6F6F4">        System.out.</span><span style="color: #62E884">println</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">serializedSingleton1 hashCode=</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">+</span><span style="color: #F6F6F4"> serializedSingleton1.</span><span style="color: #62E884">hashCode</span><span style="color: #F6F6F4">());</span></span>
<span class="line"><span style="color: #F6F6F4">        System.out.</span><span style="color: #62E884">println</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">serializedSingleton2 hashCode=</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">+</span><span style="color: #F6F6F4"> serializedSingleton2.</span><span style="color: #62E884">hashCode</span><span style="color: #F6F6F4">());</span></span>
<span class="line"><span style="color: #F6F6F4">        System.out.</span><span style="color: #62E884">println</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">enumSingleton1 hashCode=</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">+</span><span style="color: #F6F6F4"> enumSingleton1.</span><span style="color: #62E884">hashCode</span><span style="color: #F6F6F4">());</span></span>
<span class="line"><span style="color: #F6F6F4">        System.out.</span><span style="color: #62E884">println</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">enumSingleton2 hashCode=</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">+</span><span style="color: #F6F6F4"> enumSingleton2.</span><span style="color: #62E884">hashCode</span><span style="color: #F6F6F4">());</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:16px">Output của chương trình:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="serializedSingleton1 hashCode=1028566121
serializedSingleton2 hashCode=1747585824
enumSingleton1 hashCode=1118140819
enumSingleton2 hashCode=1118140819" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">serializedSingleton1 hashCode</span><span style="color: #F286C4">=</span><span style="color: #BF9EEE">1028566121</span></span>
<span class="line"><span style="color: #F6F6F4">serializedSingleton2 hashCode</span><span style="color: #F286C4">=</span><span style="color: #BF9EEE">1747585824</span></span>
<span class="line"><span style="color: #F6F6F4">enumSingleton1 hashCode</span><span style="color: #F286C4">=</span><span style="color: #BF9EEE">1118140819</span></span>
<span class="line"><span style="color: #F6F6F4">enumSingleton2 hashCode</span><span style="color: #F286C4">=</span><span style="color: #BF9EEE">1118140819</span></span></code></pre></div>



<p class="wp-block-paragraph" style="font-size:16px">Như trong ví dụ trên,&nbsp;<strong>Deserialize</strong>&nbsp;đối tượng của&nbsp;<strong>SerializedSingleton</strong>&nbsp;khác với đối tượng gốc. Tuy nhiên vấn đề này không xảy ra khi sử dụng&nbsp;<strong>enum</strong>.</p>



<p class="wp-block-paragraph" style="font-size:16px">Thực tế thì vẫn có cách khắc phục khi sử dụng class SerializedSingleton là implement một phương thức&nbsp;<strong>readResolve()</strong>. Nhưng khi chúng ta thật sự gặp vấn đề và cần sử dụng Serialize/ Deserialize, thì nên sử dụng&nbsp;<strong>enum</strong>&nbsp;sẽ đơn giản hơn.</p>



<h2 id="4-su-dung-singleton-pattern-khi-nao" class="wp-block-heading" style="font-size:30px"><strong>4. Sử dụng Singleton Pattern khi nào?</strong></h2>



<p class="wp-block-paragraph" style="font-size:16px">Dưới đây là một số trường hợp sử dụng của Singleton Pattern thường gặp:</p>



<ul class="wp-block-list">
<li style="font-size:16px">Vì class dùng Singleton chỉ tồn tại 1 Instance (thể hiện) nên nó thường được dùng cho các trường hợp giải quyết các bài toán cần truy cập vào các ứng dụng như: Shared resource, Logger, Configuration, Caching, Thread pool, …</li>



<li style="font-size:16px">Một số design pattern khác cũng sử dụng Singleton để triển khai: Abstract Factory, Builder, Prototype, Facade,…</li>



<li style="font-size:16px">Đã được sử dụng trong một số class của core java như:&nbsp;java.lang.Runtime, java.awt.Desktop.</li>
</ul>



<h2 id="5-tong-ket" class="wp-block-heading" style="font-size:30px"><strong>5. Tổng kết</strong></h2>



<p class="wp-block-paragraph" style="font-size:16px">Có rất nhiều cách implement cho Singleton, anh em có thể sử dụng&nbsp;<strong>BillPughSingleton</strong>&nbsp;vì có hiệu suất cao, sử dụng&nbsp;<strong>LazyInitializedSingleton</strong>&nbsp;cho những ứng dụng chỉ làm việc với ứng dụng&nbsp;<strong>single-thread</strong>&nbsp;và sử dụng&nbsp;<strong>DoubleCheckLockingSingleton</strong>&nbsp;khi làm việc với ứng dụng&nbsp;<strong>multi-thread</strong>. Tùy theo trường hợp cụ thể, anh em hãy chọn cho mình cách implement phù hợp nhé.</p>



<p class="wp-block-paragraph" style="font-size:16px"><strong>Tài liệu tham khảo:</strong></p>



<ul class="wp-block-list">
<li style="font-size:16px"><a href="https://community.oracle.com/docs/DOC-918906">https://community.oracle.com/docs/DOC-918906</a></li>



<li style="font-size:16px"><a href="https://www.journaldev.com/1377/java-singleton-design-pattern-best-practices-examples">https://www.journaldev.com/1377/java-singleton-design-pattern-best-practices-examples</a></li>



<li style="font-size:16px"><a href="http://www.javacreed.com/the-broken-singleton/">http://www.javacreed.com/the-broken-singleton/</a></li>



<li style="font-size:16px"><a href="https://www.geeksforgeeks.org/prevent-singleton-pattern-reflection-serialization-cloning/">https://www.geeksforgeeks.org/prevent-singleton-pattern-reflection-serialization-cloning/</a></li>



<li style="font-size:16px"><a href="http://www.java67.com/2015/09/thread-safe-singleton-in-java-using-double-checked-locking-pattern.html">http://www.java67.com/2015/09/thread-safe-singleton-in-java-using-double-checked-locking-pattern.html</a></li>
</ul>
<p>The post <a href="https://blog.tomosia.com.vn/huong-dan-java-design-pattern-singleton/">Hướng dẫn Java Design Pattern – Singleton</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/huong-dan-java-design-pattern-singleton/feed/</wfw:commentRss>
			<slash:comments>8</slash:comments>
		
		
			</item>
		<item>
		<title>Sử dụng trigger trong SQL Generrate mã code  kết hợp một String + id Auto Increament.</title>
		<link>https://blog.tomosia.com.vn/su-dung-trigger-trong-sql-generrate-ma-code-ket-hop-mot-string-id-auto-increament/</link>
					<comments>https://blog.tomosia.com.vn/su-dung-trigger-trong-sql-generrate-ma-code-ket-hop-mot-string-id-auto-increament/#comments</comments>
		
		<dc:creator><![CDATA[admin_tomosia]]></dc:creator>
		<pubDate>Tue, 19 Dec 2023 06:20:41 +0000</pubDate>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Sql]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[trigger]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=2582</guid>

					<description><![CDATA[<p>Thông tin chi tiết về bài toán: Ở đây tôi có một bài toán từ khách hàng như&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/su-dung-trigger-trong-sql-generrate-ma-code-ket-hop-mot-string-id-auto-increament/">Sử dụng trigger trong SQL Generrate mã code  kết hợp một String + id Auto Increament.</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 id="thong-tin-chi-tiet-ve-bai-toan" class="wp-block-heading"><strong>Thông tin chi tiết về bài toán:</strong></h2>



<p class="wp-block-paragraph">Ở đây tôi có một bài toán từ khách hàng như sau: Hãy tạo cho tao một mã đơn hàng (<strong>trans_confirm_no</strong>) để tao có thể phân biệt được các đơn hàng với nhau với định dạng như này &#8220;<strong>TMSxxxxx</strong>&#8221; và nó sẽ phải bắt đầu từ &#8220;<strong><strong>TMS</strong>00001</strong>&#8220;. </p>



<p class="wp-block-paragraph"><br>Ngay khi nhận được yêu cầu thì tôi đã bắt tay ngay vào việc code và ngôn ngữ tôi sử dụng cho dự án này là Java spring boot và MSQL.</p>



<h3 id="cach-su-dung-code-de-generate-ma-trans_confirm_no" class="wp-block-heading">Cách sử dụng code để generate mã trans_confirm_no</h3>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.854167938232422px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="// hàm tạo ra mã trans_confirm_no mới tăng dần
private synchronized String getTransConfirmNoDiorTest(){
        String maxTransConfirmNo = transactionDiorMapper.getTransConfirmNo();
        log.info(&quot;maxTransConfirmNo: {}&quot;, maxTransConfirmNo);
        StringBuilder result = new StringBuilder();
        if (StringUtils.isBlank(maxTransConfirmNo)){
            logger.warn(&quot;Order transConfirmNo failed, transConfirmNo={}&quot;, maxTransConfirmNo);
            result.append(&quot;00001&quot;);
        } else {
            int maxTransConfirmNoIntValue = Integer.parseInt(maxTransConfirmNo.substring(3)) + 1;
            if (maxTransConfirmNoIntValue &gt; 0 &amp;&amp; maxTransConfirmNoIntValue &lt; 100000 ) {
                int len = 5 - ((Integer)maxTransConfirmNoIntValue).toString().length();
                if ( len &gt; 0 ) {
                    for (int i = 0; i &lt; len ; i++) {
                        result.append(&quot;0&quot;);
                    }
                    result.append(maxTransConfirmNoIntValue);
                } else {
                    result.append(maxTransConfirmNoIntValue);
                }
            } else {
                result.append(&quot;00001&quot;);
            }
        }
        log.info(&quot;result: {}&quot;, result);
        return &quot;TMS&quot; + result;
    }" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #7B7F8B">// hàm tạo ra mã trans_confirm_no mới tăng dần</span></span>
<span class="line"><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">synchronized</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">String</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">getTransConfirmNoDiorTest</span><span style="color: #F6F6F4">(){</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1; font-style: italic">String</span><span style="color: #F6F6F4"> maxTransConfirmNo </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> transactionDiorMapper.</span><span style="color: #62E884">getTransConfirmNo</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">        log.</span><span style="color: #62E884">info</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">maxTransConfirmNo: {}</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, maxTransConfirmNo);</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1; font-style: italic">StringBuilder</span><span style="color: #F6F6F4"> result </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">StringBuilder</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> (StringUtils.</span><span style="color: #62E884">isBlank</span><span style="color: #F6F6F4">(maxTransConfirmNo)){</span></span>
<span class="line"><span style="color: #F6F6F4">            logger.</span><span style="color: #62E884">warn</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">Order transConfirmNo failed, transConfirmNo={}</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, maxTransConfirmNo);</span></span>
<span class="line"><span style="color: #F6F6F4">            result.</span><span style="color: #62E884">append</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">00001</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">);</span></span>
<span class="line"><span style="color: #F6F6F4">        } </span><span style="color: #F286C4">else</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #97E1F1; font-style: italic">int</span><span style="color: #F6F6F4"> maxTransConfirmNoIntValue </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> Integer.</span><span style="color: #62E884">parseInt</span><span style="color: #F6F6F4">(maxTransConfirmNo.</span><span style="color: #62E884">substring</span><span style="color: #F6F6F4">(</span><span style="color: #BF9EEE">3</span><span style="color: #F6F6F4">)) </span><span style="color: #F286C4">+</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">1</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> (maxTransConfirmNoIntValue </span><span style="color: #F286C4">&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">0</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">&amp;&amp;</span><span style="color: #F6F6F4"> maxTransConfirmNoIntValue </span><span style="color: #F286C4">&lt;</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">100000</span><span style="color: #F6F6F4"> ) {</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #97E1F1; font-style: italic">int</span><span style="color: #F6F6F4"> len </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">5</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">-</span><span style="color: #F6F6F4"> ((Integer)maxTransConfirmNoIntValue).</span><span style="color: #62E884">toString</span><span style="color: #F6F6F4">().</span><span style="color: #62E884">length</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> ( len </span><span style="color: #F286C4">&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">0</span><span style="color: #F6F6F4"> ) {</span></span>
<span class="line"><span style="color: #F6F6F4">                    </span><span style="color: #F286C4">for</span><span style="color: #F6F6F4"> (</span><span style="color: #97E1F1; font-style: italic">int</span><span style="color: #F6F6F4"> i </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">0</span><span style="color: #F6F6F4">; i </span><span style="color: #F286C4">&lt;</span><span style="color: #F6F6F4"> len ; i</span><span style="color: #F286C4">++</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">                        result.</span><span style="color: #62E884">append</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">0</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">);</span></span>
<span class="line"><span style="color: #F6F6F4">                    }</span></span>
<span class="line"><span style="color: #F6F6F4">                    result.</span><span style="color: #62E884">append</span><span style="color: #F6F6F4">(maxTransConfirmNoIntValue);</span></span>
<span class="line"><span style="color: #F6F6F4">                } </span><span style="color: #F286C4">else</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">                    result.</span><span style="color: #62E884">append</span><span style="color: #F6F6F4">(maxTransConfirmNoIntValue);</span></span>
<span class="line"><span style="color: #F6F6F4">                }</span></span>
<span class="line"><span style="color: #F6F6F4">            } </span><span style="color: #F286C4">else</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">                result.</span><span style="color: #62E884">append</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">00001</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">);</span></span>
<span class="line"><span style="color: #F6F6F4">            }</span></span>
<span class="line"><span style="color: #F6F6F4">        }</span></span>
<span class="line"><span style="color: #F6F6F4">        log.</span><span style="color: #62E884">info</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">result: {}</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, result);</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">TMS</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">+</span><span style="color: #F6F6F4"> result;</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span></code></pre></div>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.854171752929688px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code=" // hàm lấy ra mã trans_confirm_no lớn nhất đang có
 SELECT
            ttd.trans_confirm_no
        FROM
            t_transaction_dior ttd
        WHERE
            ttd.delete_flag = 0
            AND ttd.trans_confirm_no LIKE 'JPD%'
            AND ttd.trans_status IS NOT NULL
        ORDER BY ttd.trans_confirm_no DESC
        LIMIT 1;" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4"> </span><span style="color: #7B7F8B">// hàm lấy ra mã trans_confirm_no lớn nhất đang có</span></span>
<span class="line"><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">SELECT</span></span>
<span class="line"><span style="color: #F6F6F4">            ttd.trans_confirm_no</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1; font-style: italic">FROM</span></span>
<span class="line"><span style="color: #F6F6F4">            t_transaction_dior ttd</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1; font-style: italic">WHERE</span></span>
<span class="line"><span style="color: #F6F6F4">            ttd.delete_flag </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">0</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #97E1F1; font-style: italic">AND</span><span style="color: #F6F6F4"> ttd.trans_confirm_no LIKE </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">JPD%</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #97E1F1; font-style: italic">AND</span><span style="color: #F6F6F4"> ttd.trans_status </span><span style="color: #97E1F1; font-style: italic">IS</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">NOT</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">NULL</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1; font-style: italic">ORDER</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">BY</span><span style="color: #F6F6F4"> ttd.trans_confirm_no </span><span style="color: #97E1F1; font-style: italic">DESC</span></span>
<span class="line"><span style="color: #F6F6F4">        LIMIT </span><span style="color: #BF9EEE">1</span><span style="color: #F6F6F4">;</span></span></code></pre></div>



<p class="wp-block-paragraph">Vấn đề gặp phải ở đây là khi chúng tôi có nhiều đầu api call đến hàm này cùng thời điểm thì việc tạo ra một mã <strong><strong>TMS</strong>xxxxx</strong> tăng dần gặp vấn đề và nó Generate không đúng với mong muốn.</p>



<p class="wp-block-paragraph">Khi hệ thống có lượng truy cập lớn, số lượng user mua hàng dồn dập, hàm bên trên sẽ xảy ra trường hợp sinh ra các mã <strong>trans_confirm_no</strong> trùng nhau.</p>



<h3 id="su-dung-trigger-generate-ma-trans_confirm_no" class="wp-block-heading">Sử dụng trigger generate mã trans_confirm_no</h3>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.854156494140625px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Java</span><span role="button" tabindex="0" data-code="// Đầu tiên chúng ta tạo ra 1 bảng để sử cơ chế AUTO_INCREMENT trong mysql để  lấy được phần xxxxx nằm trong fomat &quot;TMSxxxxx&quot;
CREATE TABLE transaction_seq
(
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
);

// Mình tạo ra 1 trigger sử dụng concat để nối prefix TMS + xxxxx đã được gen ở bảng transaction_seq phía trên.
DELIMITER $$
CREATE TRIGGER tg_t_transaction_dior_insert
    BEFORE INSERT ON t_transaction_dior
    FOR EACH ROW
BEGIN
    INSERT INTO transaction_seq VALUES (NULL);
    SET NEW.trans_confirm_no = CONCAT('TMS', LPAD(LAST_INSERT_ID(), 5, '0'));
END$$
    DELIMITER ;

// Đặt giá trị bắt đầu AUTO_INCREMENT trong table transaction_seq
ALTER TABLE transaction_seq AUTO_INCREMENT=00001;" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #7B7F8B">// Đầu tiên chúng ta tạo ra 1 bảng để sử cơ chế AUTO_INCREMENT trong mysql để  lấy được phần xxxxx nằm trong fomat &quot;TMSxxxxx&quot;</span></span>
<span class="line"><span style="color: #97E1F1; font-style: italic">CREATE</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">TABLE</span><span style="color: #F6F6F4"> transaction_seq</span></span>
<span class="line"><span style="color: #F6F6F4">(</span></span>
<span class="line"><span style="color: #F6F6F4">    id </span><span style="color: #97E1F1; font-style: italic">INT</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">NOT</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">NULL</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">AUTO_INCREMENT</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">PRIMARY</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">KEY</span></span>
<span class="line"><span style="color: #F6F6F4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #7B7F8B">// Mình tạo ra 1 trigger sử dụng concat để nối prefix TMS + xxxxx đã được gen ở bảng transaction_seq phía trên.</span></span>
<span class="line"><span style="color: #97E1F1; font-style: italic">DELIMITER</span><span style="color: #F6F6F4"> $$</span></span>
<span class="line"><span style="color: #97E1F1; font-style: italic">CREATE</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">TRIGGER</span><span style="color: #F6F6F4"> tg_t_transaction_dior_insert</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #97E1F1; font-style: italic">BEFORE</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">INSERT</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">ON</span><span style="color: #F6F6F4"> t_transaction_dior</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #97E1F1; font-style: italic">FOR</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">EACH</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">ROW</span></span>
<span class="line"><span style="color: #97E1F1; font-style: italic">BEGIN</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #97E1F1; font-style: italic">INSERT</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">INTO</span><span style="color: #F6F6F4"> transaction_seq </span><span style="color: #62E884">VALUES</span><span style="color: #F6F6F4"> (NULL);</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #97E1F1; font-style: italic">SET</span><span style="color: #F6F6F4"> NEW.trans_confirm_no </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">CONCAT</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">TMS</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #62E884">LPAD</span><span style="color: #F6F6F4">(</span><span style="color: #62E884">LAST_INSERT_ID</span><span style="color: #F6F6F4">(), </span><span style="color: #BF9EEE">5</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">0</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">));</span></span>
<span class="line"><span style="color: #97E1F1; font-style: italic">END</span><span style="color: #F6F6F4">$$</span></span>
<span class="line"><span style="color: #F6F6F4">    DELIMITER ;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #7B7F8B">// Đặt giá trị bắt đầu AUTO_INCREMENT trong table transaction_seq</span></span>
<span class="line"><span style="color: #97E1F1; font-style: italic">ALTER</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">TABLE</span><span style="color: #F6F6F4"> transaction_seq AUTO_INCREMENT</span><span style="color: #F286C4">=</span><span style="color: #BF9EEE">00001</span><span style="color: #F6F6F4">;</span></span></code></pre></div>



<p class="wp-block-paragraph">Khi sử dụng trigger đã giải quyết được vấn đề của tôi trong bài toán này.</p>
<p>The post <a href="https://blog.tomosia.com.vn/su-dung-trigger-trong-sql-generrate-ma-code-ket-hop-mot-string-id-auto-increament/">Sử dụng trigger trong SQL Generrate mã code  kết hợp một String + id Auto Increament.</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/su-dung-trigger-trong-sql-generrate-ma-code-ket-hop-mot-string-id-auto-increament/feed/</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		
			</item>
		<item>
		<title>Setup New Relic for monitoring AWS Beanstalk and Java Spring Boot</title>
		<link>https://blog.tomosia.com.vn/setup-new-relic-for-monitoring-aws-beanstalk-and-java-spring-boot/</link>
					<comments>https://blog.tomosia.com.vn/setup-new-relic-for-monitoring-aws-beanstalk-and-java-spring-boot/#comments</comments>
		
		<dc:creator><![CDATA[Việt Nguyễn]]></dc:creator>
		<pubDate>Tue, 31 Oct 2023 10:10:56 +0000</pubDate>
				<category><![CDATA[AWS]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[NewRelic]]></category>
		<category><![CDATA[monitoring]]></category>
		<category><![CDATA[performance]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=1655</guid>

					<description><![CDATA[<p>New Relic là một dịch vụ giúp bạn theo giõi tần suất hoạt động của máy chủ, cũng&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/setup-new-relic-for-monitoring-aws-beanstalk-and-java-spring-boot/">Setup New Relic for monitoring AWS Beanstalk and Java Spring Boot</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">New Relic là một dịch vụ giúp bạn theo giõi tần suất hoạt động của máy chủ, cũng như theo giõi các ứng dụng được chạy trên máy chủ như Java, PHP, Ruby,&#8230; Chúng ta có thể theo giõi tổng quan mà không cần phải truy cập vào máy chủ để kiểm tra. </p>



<p class="wp-block-paragraph">Ở bài viết này, mình sẽ hướng dẫn các bạn setup New Relic với máy chủ sử dụng dịch vụ AWS Beanstalk và ứng dụng đc viết bằng Java Spring Boot. Bắt đầu thôiiii.</p>



<h2 id="1-dang-ki-tai-khoan-new-relic" class="wp-block-heading">1. Đăng kí tài khoản New Relic</h2>



<p class="wp-block-paragraph">Đăng kí tài khoản New Relic rất dễ dàng tại link sau: <a href="https://newrelic.com/signup">https://newrelic.com/signup</a></p>



<p class="wp-block-paragraph">Sau khi đăng kí thằng công, màn hình hiển thị trang dashboard của hệ thống và chúng ta có thể bắt đầu setup</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="510" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-65-1024x510.png" alt="" class="wp-image-1661" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-65-1024x510.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-65-300x149.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-65-768x383.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-65-1536x765.png 1536w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-65-2048x1020.png 2048w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-65-380x189.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-65-800x399.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-65-1160x578.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-65.png 3356w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h2 id="2-tao-ung-dung-java-va-cai-dat" class="wp-block-heading">2. Tạo ứng dụng Java và cài đặt</h2>



<h3 id="2-1-thong-tin-ung-dung" class="wp-block-heading">2.1. Thông tin ứng dụng</h3>



<p class="wp-block-paragraph">Trên bảng stack, chọn ứng dụng Java và tiếp tục, sau đó chọn <strong>Don&#8217;t have access</strong> the host và chọn Java tại mục <strong>Connect your applications</strong>.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="508" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-66-1024x508.png" alt="" class="wp-image-1662" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-66-1024x508.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-66-300x149.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-66-768x381.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-66-1536x762.png 1536w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-66-2048x1015.png 2048w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-66-380x188.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-66-800x397.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-66-1160x575.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-66.png 3356w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Ở bài viết này chúng ta đang thực hiện trên máy chủ AWS Beanstalk nên tôi sẽ chọn <strong>On a host</strong>. Sau đó nhập tên ứng dụng và làm các bước theo hướng dẫn.</p>



<p class="wp-block-paragraph">Ở bước <strong>Tell us about your environment</strong>, tôi chọn <strong>running on Linux</strong> và framework là Spring Boot.</p>



<h3 id="2-2-cai-dat-goi-thu-vien" class="wp-block-heading">2.2. Cài đặt gói thư viện</h3>



<p class="wp-block-paragraph">Bước tiếp theo chúng ta cần tiến hành cài đặt gói thư viện. Bởi vì dùng AWS Beanstalk nên tôi sẽ thực hiện việc cài đặt trong folder <strong><em>.ebextension</em></strong></p>



<p class="wp-block-paragraph">Tạo file <strong><em>newrelic.config</em></strong> trong thư mục <strong><em>.ebextensions</em></strong></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#F8F8F2;--cbp-line-number-width:16.854156494140625px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#efefe1">ShellScript</span><span role="button" tabindex="0" data-code="commands:
  01-download-java-agent:
    command: &quot;curl -O https://download.newrelic.com/newrelic/java-agent/newrelic-agent/current/newrelic-java.zip &amp;&amp; mv newrelic-java.zip /var/newrelic-java.zip&quot;
  02-unzip-java-agent:
    command: &quot;unzip -o /var/newrelic-java.zip -d /var/&quot;
  03-give-permissions-to-newrelic:
    command: &quot;chmod a+rwx /var/newrelic&quot;
  04-replace-license-key:
    command: &quot;sed -i 's/&lt;%= license_key %&gt;/your_application_license_key/g' /var/newrelic/newrelic.yml&quot;
  05-replace-application-name:
    command: &quot;sed -i 's/My Application/your_application_license_key/g' /var/newrelic/newrelic.yml&quot;
  06-enable-infras-and-logs-metric:
       command: &quot;your_application_cmd_curl_infra&quot;" style="color:#F8F8F2;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #50FA7B">commands:</span></span>
<span class="line"><span style="color: #F8F8F2">  </span><span style="color: #50FA7B">01-download-java-agent:</span></span>
<span class="line"><span style="color: #F8F8F2">    </span><span style="color: #8BE9FD">command</span><span style="color: #F8F8F2">: </span><span style="color: #E9F284">&quot;</span><span style="color: #F1FA8C">curl -O https://download.newrelic.com/newrelic/java-agent/newrelic-agent/current/newrelic-java.zip &amp;&amp; mv newrelic-java.zip /var/newrelic-java.zip</span><span style="color: #E9F284">&quot;</span></span>
<span class="line"><span style="color: #F8F8F2">  </span><span style="color: #50FA7B">02-unzip-java-agent:</span></span>
<span class="line"><span style="color: #F8F8F2">    </span><span style="color: #8BE9FD">command</span><span style="color: #F8F8F2">: </span><span style="color: #E9F284">&quot;</span><span style="color: #F1FA8C">unzip -o /var/newrelic-java.zip -d /var/</span><span style="color: #E9F284">&quot;</span></span>
<span class="line"><span style="color: #F8F8F2">  </span><span style="color: #50FA7B">03-give-permissions-to-newrelic:</span></span>
<span class="line"><span style="color: #F8F8F2">    </span><span style="color: #8BE9FD">command</span><span style="color: #F8F8F2">: </span><span style="color: #E9F284">&quot;</span><span style="color: #F1FA8C">chmod a+rwx /var/newrelic</span><span style="color: #E9F284">&quot;</span></span>
<span class="line"><span style="color: #F8F8F2">  </span><span style="color: #50FA7B">04-replace-license-key:</span></span>
<span class="line"><span style="color: #F8F8F2">    </span><span style="color: #8BE9FD">command</span><span style="color: #F8F8F2">: </span><span style="color: #E9F284">&quot;</span><span style="color: #F1FA8C">sed -i &#39;s/&lt;%= license_key %&gt;/your_application_license_key/g&#39; /var/newrelic/newrelic.yml</span><span style="color: #E9F284">&quot;</span></span>
<span class="line"><span style="color: #F8F8F2">  </span><span style="color: #50FA7B">05-replace-application-name:</span></span>
<span class="line"><span style="color: #F8F8F2">    </span><span style="color: #8BE9FD">command</span><span style="color: #F8F8F2">: </span><span style="color: #E9F284">&quot;</span><span style="color: #F1FA8C">sed -i &#39;s/My Application/your_application_license_key/g&#39; /var/newrelic/newrelic.yml</span><span style="color: #E9F284">&quot;</span></span>
<span class="line"><span style="color: #F8F8F2">  </span><span style="color: #50FA7B">06-enable-infras-and-logs-metric:</span></span>
<span class="line"><span style="color: #F8F8F2">       </span><span style="color: #8BE9FD">command</span><span style="color: #F8F8F2">: </span><span style="color: #E9F284">&quot;</span><span style="color: #F1FA8C">your_application_cmd_curl_infra</span><span style="color: #E9F284">&quot;</span></span></code></pre></div>



<p class="wp-block-paragraph">Trong đó:</p>



<ul class="wp-block-list">
<li><strong>your_application_license_key</strong>: license_key của bạn.</li>



<li><strong>your_application_license_key</strong>: Application name mà bạn đã đăng kí vơi new relic.</li>



<li><strong>your_application_cmd_curl_infra</strong>: Command bạn nhận đc ở bước <strong>Connect your logs and infrastructure</strong>.</li>
</ul>



<p class="wp-block-paragraph">Nếu bạn chỉ muốn giám sát ứng dụng mà ko muốn giám sát Infras, bạn có thể bỏ qua bước <strong>06-enable-infras-and-logs-metric</strong>. Nếu cài đặt hãy disable option <strong>Automatically answer &#8220;yes&#8221; to all install prompts. We&#8217;ll stop the installer if there&#8217;s an error.</strong> trên dashboard NewRelic</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="464" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-67-1024x464.png" alt="" class="wp-image-1665" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-67-1024x464.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-67-300x136.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-67-768x348.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-67-1536x695.png 1536w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-67-2048x927.png 2048w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-67-380x172.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-67-800x362.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-67-1160x525.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-67.png 2730w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h2 id="3-deploy-voi-codebuild-va-codepipeline" class="wp-block-heading">3. Deploy với CodeBuild và CodePipeline</h2>



<p class="wp-block-paragraph">Chúng ta cần tạo file Procfile trong folder root của source code để định nghĩa câu lệnh chạy ứng dụng java kèm theo NewRelic</p>



<p class="wp-block-paragraph"><strong>Procfile</strong>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#F8F8F2;--cbp-line-number-width:8.42535400390625px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#efefe1">ShellScript</span><span role="button" tabindex="0" data-code="web: java -javaagent:/var/newrelic/newrelic.jar -jar app.jar" style="color:#F8F8F2;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #50FA7B">web:</span><span style="color: #F8F8F2"> </span><span style="color: #F1FA8C">java</span><span style="color: #F8F8F2"> </span><span style="color: #BD93F9">-javaagent:/var/newrelic/newrelic.jar</span><span style="color: #F8F8F2"> </span><span style="color: #BD93F9">-jar</span><span style="color: #F8F8F2"> </span><span style="color: #F1FA8C">app.jar</span></span></code></pre></div>



<p class="wp-block-paragraph">Config sẽ không chạy nếu folder .ebextensions không được deploy lên Beanstalk. Dưới đây là file <strong>Buildspec</strong> của tôi</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#F8F8F2;--cbp-line-number-width:16.854171752929688px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#efefe1">ShellScript</span><span role="button" tabindex="0" data-code="version: 0.2

phases:
  install:
    runtime-versions:
      java: corretto11
  build:
    commands:
      - mvn install
      - mv target/*.jar app.jar
artifacts:
  files:
    - Procfile
    - app.jar
    - .ebextensions/**/*" style="color:#F8F8F2;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #50FA7B">version:</span><span style="color: #F8F8F2"> </span><span style="color: #BD93F9">0.2</span></span>
<span class="line"></span>
<span class="line"><span style="color: #50FA7B">phases:</span></span>
<span class="line"><span style="color: #F8F8F2">  </span><span style="color: #50FA7B">install:</span></span>
<span class="line"><span style="color: #F8F8F2">    </span><span style="color: #50FA7B">runtime-versions:</span></span>
<span class="line"><span style="color: #F8F8F2">      </span><span style="color: #50FA7B">java:</span><span style="color: #F8F8F2"> </span><span style="color: #F1FA8C">corretto11</span></span>
<span class="line"><span style="color: #F8F8F2">  </span><span style="color: #50FA7B">build:</span></span>
<span class="line"><span style="color: #F8F8F2">    </span><span style="color: #50FA7B">commands:</span></span>
<span class="line"><span style="color: #F8F8F2">      </span><span style="color: #50FA7B">-</span><span style="color: #F8F8F2"> </span><span style="color: #F1FA8C">mvn</span><span style="color: #F8F8F2"> </span><span style="color: #F1FA8C">install</span></span>
<span class="line"><span style="color: #F8F8F2">      </span><span style="color: #50FA7B">-</span><span style="color: #F8F8F2"> </span><span style="color: #F1FA8C">mv</span><span style="color: #F8F8F2"> </span><span style="color: #F1FA8C">target/</span><span style="color: #BD93F9; font-style: italic">*</span><span style="color: #F1FA8C">.jar</span><span style="color: #F8F8F2"> </span><span style="color: #F1FA8C">app.jar</span></span>
<span class="line"><span style="color: #50FA7B">artifacts:</span></span>
<span class="line"><span style="color: #F8F8F2">  </span><span style="color: #50FA7B">files:</span></span>
<span class="line"><span style="color: #F8F8F2">    </span><span style="color: #50FA7B">-</span><span style="color: #F8F8F2"> </span><span style="color: #F1FA8C">Procfile</span></span>
<span class="line"><span style="color: #F8F8F2">    </span><span style="color: #50FA7B">-</span><span style="color: #F8F8F2"> </span><span style="color: #F1FA8C">app.jar</span></span>
<span class="line"><span style="color: #F8F8F2">    </span><span style="color: #50FA7B">-</span><span style="color: #F8F8F2"> </span><span style="color: #F1FA8C">.ebextensions/</span><span style="color: #BD93F9; font-style: italic">**</span><span style="color: #F1FA8C">/</span><span style="color: #BD93F9; font-style: italic">*</span></span></code></pre></div>



<p class="wp-block-paragraph">Cuối cùng sau khi deploy thành công, thực hiện Test Connection trên NewRelic để tạo ứng dụng:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="351" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-68-1024x351.png" alt="" class="wp-image-1666" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-68-1024x351.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-68-300x103.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-68-768x263.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-68-1536x527.png 1536w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-68-2048x702.png 2048w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-68-380x130.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-68-800x274.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-68-1160x398.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-68.png 2258w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h2 id="4-tong-ket" class="wp-block-heading">4. Tổng kết</h2>



<p class="wp-block-paragraph">Còn 1 phần quan trọng mình chưa đến đó là chi phí, theo mình tìm hiểu NewRelic sẽ tính phía dựa trên dung lượng sử dụng trên hệ thống, chúng ta sẽ được miễn phí với 100GB/tháng</p>



<p class="wp-block-paragraph">Trên đây là tổng hợp các bước setup NewRelic với ứng dụng Java chạy trên AWS Beanstalk của mình, hi vọng giúp ích mọi người.</p>



<p class="wp-block-paragraph">Về những thông số và giá trị đem lại anh em hãy tự trải nghiệm và tận hưởng nheeeeeeeeé</p>



<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://blog.tomosia.com.vn/setup-new-relic-for-monitoring-aws-beanstalk-and-java-spring-boot/">Setup New Relic for monitoring AWS Beanstalk and Java Spring Boot</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/setup-new-relic-for-monitoring-aws-beanstalk-and-java-spring-boot/feed/</wfw:commentRss>
			<slash:comments>6</slash:comments>
		
		
			</item>
	</channel>
</rss>
