{"id":92439,"date":"2025-11-30T22:10:27","date_gmt":"2025-11-30T15:10:27","guid":{"rendered":"https:\/\/itviec.com\/blog\/?p=92439"},"modified":"2025-11-30T22:39:18","modified_gmt":"2025-11-30T15:39:18","slug":"cau-hoi-phong-van-java-spring-boot","status":"publish","type":"post","link":"https:\/\/itviec.com\/blog\/cau-hoi-phong-van-java-spring-boot\/","title":{"rendered":"Top 45+ c\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n Java Spring Boot th\u01b0\u1eddng g\u1eb7p\u00a0"},"content":{"rendered":"<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_85 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">N\u1ed9i dung b\u00e0i vi\u1ebft<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/itviec.com\/blog\/cau-hoi-phong-van-java-spring-boot\/#Cau_hoi_phong_van_ve_Spring_Core\" >C\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n v\u1ec1 Spring Core&nbsp;<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/itviec.com\/blog\/cau-hoi-phong-van-java-spring-boot\/#Cau_hoi_phong_van_ve_Spring_MVC_RESTful_APIs\" >C\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n v\u1ec1 Spring MVC &amp; RESTful APIs<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/itviec.com\/blog\/cau-hoi-phong-van-java-spring-boot\/#Cau_hoi_phong_van_ve_Spring_Data_JPA_Database\" >C\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n v\u1ec1 Spring Data JPA &amp; Database<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/itviec.com\/blog\/cau-hoi-phong-van-java-spring-boot\/#Cau_hoi_phong_van_ve_Spring_Security\" >C\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n v\u1ec1 Spring Security<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/itviec.com\/blog\/cau-hoi-phong-van-java-spring-boot\/#Cau_hoi_phong_van_ve_microservices_testing_kien_thuc_nang_cao\" >C\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n v\u1ec1 microservices, testing &amp; ki\u1ebfn th\u1ee9c n\u00e2ng cao<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/itviec.com\/blog\/cau-hoi-phong-van-java-spring-boot\/#Tong_ket\" >T\u1ed5ng k\u1ebft<\/a><\/li><\/ul><\/nav><\/div>\n\n<p><strong><em>Hi\u1ec7n nay, Java Spring Boot \u0111\u00e3 tr\u1edf th\u00e0nh m\u1ed9t framework kh\u00f4ng th\u1ec3 thi\u1ebfu cho c\u00e1c nh\u00e0 ph\u00e1t tri\u1ec3n \u1ee9ng d\u1ee5ng web hi\u1ec7n \u0111\u1ea1i. T\u1eeb nh\u1eefng kh\u00e1i ni\u1ec7m c\u01a1 b\u1ea3n v\u1ec1 Spring Core \u0111\u1ebfn c\u00e1c ki\u1ebfn tr\u00fac Microservices ph\u1ee9c t\u1ea1p, vi\u1ec7c n\u1eafm v\u1eefng Spring Boot l\u00e0 ch\u00eca kh\u00f3a \u0111\u1ec3 b\u1ea1n t\u1ef1 tin chinh ph\u1ee5c m\u1ecdi bu\u1ed5i ph\u1ecfng v\u1ea5n. B\u00e0i vi\u1ebft n\u00e0y s\u1ebd t\u1ed5ng h\u1ee3p nh\u1eefng c\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n Java Spring Boot th\u01b0\u1eddng g\u1eb7p nh\u1ea5t, k\u00e8m theo gi\u1ea3i th\u00edch v\u00e0 v\u00ed d\u1ee5 th\u1ef1c t\u1ebf, gi\u00fap b\u1ea1n c\u1ee7ng c\u1ed1 ki\u1ebfn th\u1ee9c v\u00e0 chu\u1ea9n b\u1ecb t\u1ed1t nh\u1ea5t cho h\u00e0nh tr\u00ecnh s\u1ef1 nghi\u1ec7p c\u1ee7a m\u00ecnh.<\/em><\/strong><\/p>\n\n\n\n<p>\u0110\u1ecdc b\u00e0i vi\u1ebft \u0111\u1ec3 \u0111\u01b0\u1ee3c h\u01b0\u1edbng d\u1eabn tr\u1ea3 l\u1eddi:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>C\u00e1c c\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n v\u1ec1 Spring Core<\/li>\n\n\n\n<li>C\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n v\u1ec1 Spring MVC &amp; RESTful APIs<\/li>\n\n\n\n<li>C\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n v\u1ec1 Spring Data JPA &amp; Database<\/li>\n\n\n\n<li>C\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n v\u1ec1 Spring Security<\/li>\n\n\n\n<li>C\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n v\u1ec1 microservices, testing &amp; ki\u1ebfn th\u1ee9c n\u00e2ng cao<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-cau-h\u1ecfi-ph\u1ecfng-v\u1ea5n-v\u1ec1-spring-core\"><span class=\"ez-toc-section\" id=\"Cau_hoi_phong_van_ve_Spring_Core\"><\/span><strong>C\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n v\u1ec1 Spring Core&nbsp;<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-1-spring-boot-la-gi-va-gi\u1ea3i-quy\u1ebft-v\u1ea5n-d\u1ec1-gi-c\u1ee7a-spring-framework\"><strong>1. Spring Boot l\u00e0 g\u00ec v\u00e0 gi\u1ea3i quy\u1ebft v\u1ea5n \u0111\u1ec1 g\u00ec c\u1ee7a Spring Framework?<\/strong><\/h3>\n\n\n\n<p>Spring Boot l\u00e0 m\u1ed9t d\u1ef1 \u00e1n con c\u1ee7a Spring Framework, \u0111\u01b0\u1ee3c thi\u1ebft k\u1ebf \u0111\u1ec3 \u0111\u01a1n gi\u1ea3n h\u00f3a qu\u00e1 tr\u00ecnh kh\u1edfi t\u1ea1o v\u00e0 ph\u00e1t tri\u1ec3n c\u00e1c \u1ee9ng d\u1ee5ng Spring \u0111\u1ed9c l\u1eadp, s\u1eb5n s\u00e0ng cho m\u00f4i tr\u01b0\u1eddng s\u1ea3n xu\u1ea5t.&nbsp;<\/p>\n\n\n\n<p>N\u00f3 gi\u1ea3i quy\u1ebft c\u00e1c v\u1ea5n \u0111\u1ec1 ch\u00ednh c\u1ee7a Spring Framework nh\u01b0:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>C\u1ea5u h\u00ecnh ph\u1ee9c t\u1ea1p:<\/strong> Spring truy\u1ec1n th\u1ed1ng y\u00eau c\u1ea7u r\u1ea5t nhi\u1ec1u c\u1ea5u h\u00ecnh XML ho\u1eb7c Java Config. Spring Boot gi\u1ea3m \u0111\u00e1ng k\u1ec3 \u0111i\u1ec1u n\u00e0y th\u00f4ng qua c\u01a1 ch\u1ebf Auto-Configuration.<\/li>\n\n\n\n<li><strong>Qu\u1ea3n l\u00fd Dependency:<\/strong> Vi\u1ec7c qu\u1ea3n l\u00fd c\u00e1c phi\u00ean b\u1ea3n dependency trong Spring c\u00f3 th\u1ec3 ph\u1ee9c t\u1ea1p. Spring Boot Starters gi\u00fap \u0111\u01a1n gi\u1ea3n h\u00f3a vi\u1ec7c n\u00e0y b\u1eb1ng c\u00e1ch nh\u00f3m c\u00e1c dependency li\u00ean quan l\u1ea1i v\u1edbi nhau.<\/li>\n\n\n\n<li><strong>Tri\u1ec3n khai:<\/strong> Spring Boot cho ph\u00e9p t\u1ea1o c\u00e1c \u1ee9ng d\u1ee5ng \u0111\u1ed9c l\u1eadp, ch\u1ea1y \u0111\u01b0\u1ee3c (executable JARs), bao g\u1ed3m c\u1ea3 m\u00e1y ch\u1ee7 nh\u00fang (nh\u01b0 Tomcat, Jetty), gi\u00fap vi\u1ec7c tri\u1ec3n khai tr\u1edf n\u00ean d\u1ec5 d\u00e0ng h\u01a1n r\u1ea5t nhi\u1ec1u.<\/li>\n\n\n\n<li><strong>Th\u1eddi gian ph\u00e1t tri\u1ec3n:<\/strong> Nh\u1edd c\u00e1c t\u00ednh n\u0103ng tr\u00ean, Spring Boot gi\u00fap t\u0103ng t\u1ed1c \u0111\u1ed9 ph\u00e1t tri\u1ec3n \u1ee9ng d\u1ee5ng l\u00ean \u0111\u00e1ng k\u1ec3.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-2-dependency-injection-di-va-inversion-of-control-ioc-co-l\u1ee3i-ich-gi\"><strong>2. Dependency Injection (DI) v\u00e0 Inversion of Control (IoC) c\u00f3 l\u1ee3i \u00edch g\u00ec?<\/strong><\/h3>\n\n\n\n<p><strong>Inversion of Control (IoC):<\/strong> IoC l\u00e0 m\u1ed9t nguy\u00ean l\u00fd thi\u1ebft k\u1ebf trong ph\u00e1t tri\u1ec3n ph\u1ea7n m\u1ec1m, trong \u0111\u00f3 lu\u1ed3ng \u0111i\u1ec1u khi\u1ec3n c\u1ee7a ch\u01b0\u01a1ng tr\u00ecnh \u0111\u01b0\u1ee3c \u0111\u1ea3o ng\u01b0\u1ee3c. Thay v\u00ec c\u00e1c \u0111\u1ed1i t\u01b0\u1ee3ng t\u1ef1 t\u1ea1o ho\u1eb7c t\u00ecm ki\u1ebfm c\u00e1c dependency c\u1ee7a ch\u00fang, m\u1ed9t IoC Container (trong Spring l\u00e0 ApplicationContext) s\u1ebd ch\u1ecbu tr\u00e1ch nhi\u1ec7m t\u1ea1o v\u00e0 qu\u1ea3n l\u00fd c\u00e1c \u0111\u1ed1i t\u01b0\u1ee3ng, sau \u0111\u00f3 &#8220;ti\u00eam&#8221; c\u00e1c dependency \u0111\u00f3 v\u00e0o. \u0110i\u1ec1u n\u00e0y gi\u00fap gi\u1ea3m s\u1ef1 ph\u1ee5 thu\u1ed9c ch\u1eb7t ch\u1ebd gi\u1eefa c\u00e1c th\u00e0nh ph\u1ea7n.<\/p>\n\n\n\n<p><strong>Dependency Injection (DI):<\/strong> DI l\u00e0 m\u1ed9t m\u1eabu thi\u1ebft k\u1ebf c\u1ee5 th\u1ec3 \u0111\u1ec3 tri\u1ec3n khai nguy\u00ean l\u00fd IoC. N\u00f3 l\u00e0 qu\u00e1 tr\u00ecnh m\u1ed9t \u0111\u1ed1i t\u01b0\u1ee3ng nh\u1eadn c\u00e1c dependency c\u1ee7a n\u00f3 t\u1eeb b\u00ean ngo\u00e0i, thay v\u00ec t\u1ef1 t\u1ea1o ra ch\u00fang. Trong Spring, DI c\u00f3 th\u1ec3 \u0111\u01b0\u1ee3c th\u1ef1c hi\u1ec7n th\u00f4ng qua Constructor Injection, Setter Injection ho\u1eb7c Field Injection.<\/p>\n\n\n\n<p><strong>L\u1ee3i \u00edch:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Gi\u1ea3m s\u1ef1 ph\u1ee5 thu\u1ed9c (Loose Coupling):<\/strong> C\u00e1c th\u00e0nh ph\u1ea7n kh\u00f4ng c\u1ea7n bi\u1ebft c\u00e1ch t\u1ea1o ra c\u00e1c dependency c\u1ee7a ch\u00fang, ch\u1ec9 c\u1ea7n bi\u1ebft c\u00e1ch s\u1eed d\u1ee5ng ch\u00fang.<\/li>\n\n\n\n<li><strong>T\u0103ng kh\u1ea3 n\u0103ng ki\u1ec3m th\u1eed (Testability):<\/strong> D\u1ec5 d\u00e0ng thay th\u1ebf c\u00e1c dependency b\u1eb1ng c\u00e1c mock object trong qu\u00e1 tr\u00ecnh ki\u1ec3m th\u1eed \u0111\u01a1n v\u1ecb.<\/li>\n\n\n\n<li><strong>T\u0103ng kh\u1ea3 n\u0103ng t\u00e1i s\u1eed d\u1ee5ng (Reusability):<\/strong> C\u00e1c th\u00e0nh ph\u1ea7n c\u00f3 th\u1ec3 \u0111\u01b0\u1ee3c t\u00e1i s\u1eed d\u1ee5ng trong c\u00e1c ng\u1eef c\u1ea3nh kh\u00e1c nhau v\u1edbi c\u00e1c dependency kh\u00e1c nhau.<\/li>\n\n\n\n<li><strong>D\u1ec5 d\u00e0ng qu\u1ea3n l\u00fd v\u00e0 b\u1ea3o tr\u00ec:<\/strong> Thay \u0111\u1ed5i m\u1ed9t dependency kh\u00f4ng \u1ea3nh h\u01b0\u1edfng tr\u1ef1c ti\u1ebfp \u0111\u1ebfn c\u00e1c th\u00e0nh ph\u1ea7n s\u1eed d\u1ee5ng n\u00f3.<\/li>\n\n\n\n<li><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-3-bean-trong-spring-la-gi-trinh-bay-cac-cach-d\u1ec3-d\u1ecbnh-nghia-m\u1ed9t-bean\"><strong>3. Bean trong Spring l\u00e0 g\u00ec? Tr\u00ecnh b\u00e0y c\u00e1c c\u00e1ch \u0111\u1ec3 \u0111\u1ecbnh ngh\u0129a m\u1ed9t Bean.<\/strong><\/h3>\n\n\n\n<p>Trong Spring, m\u1ed9t &#8220;Bean&#8221; l\u00e0 m\u1ed9t \u0111\u1ed1i t\u01b0\u1ee3ng \u0111\u01b0\u1ee3c kh\u1edfi t\u1ea1o, qu\u1ea3n l\u00fd v\u00e0 c\u1ea5u h\u00ecnh b\u1edfi Spring IoC Container. C\u00e1c Bean n\u00e0y l\u00e0 x\u01b0\u01a1ng s\u1ed1ng c\u1ee7a \u1ee9ng d\u1ee5ng Spring, \u0111\u1ea1i di\u1ec7n cho c\u00e1c th\u00e0nh ph\u1ea7n ch\u00ednh c\u1ee7a \u1ee9ng d\u1ee5ng.<\/p>\n\n\n\n<p><strong>C\u00e1c c\u00e1ch \u0111\u1ec3 \u0111\u1ecbnh ngh\u0129a m\u1ed9t Bean:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>S\u1eed d\u1ee5ng @Component v\u00e0 c\u00e1c Annotation ph\u00e1i sinh (@Service, @Repository, @Controller): <\/strong>\u0110\u00e2y l\u00e0 c\u00e1ch ph\u1ed5 bi\u1ebfn nh\u1ea5t trong Spring Boot. B\u1ea1n ch\u1ec9 c\u1ea7n \u0111\u00e1nh d\u1ea5u l\u1edbp b\u1eb1ng m\u1ed9t trong c\u00e1c annotation n\u00e0y, v\u00e0 Spring Boot s\u1ebd t\u1ef1 \u0111\u1ed9ng qu\u00e9t (component scanning) v\u00e0 \u0111\u0103ng k\u00fd ch\u00fang nh\u01b0 c\u00e1c Bean.<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>@Service\npublic class MyServiceImpl implements MyService {\n    \/\/ ...}<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>S\u1eed d\u1ee5n<\/strong><strong>g @Bean <\/strong><strong>Annotation trong l\u1edbp c\u1ea5u h\u00ecnh (<\/strong><strong>@Configuration<\/strong><strong>)<\/strong>: B\u1ea1n c\u00f3 th\u1ec3 \u0111\u1ecbnh ngh\u0129a m\u1ed9t ph\u01b0\u01a1ng th\u1ee9c trong m\u1ed9t l\u1edbp \u0111\u01b0\u1ee3c \u0111\u00e1nh d\u1ea5u <strong>@Configuration<\/strong>, v\u00e0 ph\u01b0\u01a1ng th\u1ee9c \u0111\u00f3 s\u1ebd tr\u1ea3 v\u1ec1 m\u1ed9t \u0111\u1ed1i t\u01b0\u1ee3ng. Spring s\u1ebd g\u1ecdi ph\u01b0\u01a1ng th\u1ee9c n\u00e0y v\u00e0 \u0111\u0103ng k\u00fd \u0111\u1ed1i t\u01b0\u1ee3ng tr\u1ea3 v\u1ec1 nh\u01b0 m\u1ed9t Bean.<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>@Configuration\npublic class AppConfig {\n    @Bean\n    public MyService myService() {\n        return new MyServiceImpl();\n    }\n}<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>S\u1eed d\u1ee5ng XML Configuration (\u00edt d\u00f9ng trong Spring Boot):<\/strong> Trong c\u00e1c d\u1ef1 \u00e1n Spring truy\u1ec1n th\u1ed1ng, Bean c\u00f3 th\u1ec3 \u0111\u01b0\u1ee3c \u0111\u1ecbnh ngh\u0129a trong c\u00e1c t\u1ec7p XML.<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;bean id=\"myService\" class=\"com.example.MyServiceImpl\"\/&gt;&nbsp;<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-4-s\u1ef1-khac-bi\u1ec7t-gi\u1eefa-component-service-repository-va-controller-la-gi\"><strong>4. S\u1ef1 kh\u00e1c bi\u1ec7t gi\u1eefa @Component, @Service, @Repository v\u00e0 @Controller l\u00e0 g\u00ec?<\/strong><\/h3>\n\n\n\n<p>T\u1ea5t c\u1ea3 c\u00e1c annotation n\u00e0y \u0111\u1ec1u l\u00e0 c\u00e1c chuy\u00ean bi\u1ec7t h\u00f3a c\u1ee7a @Component, c\u00f3 ngh\u0129a l\u00e0 ch\u00fang \u0111\u1ec1u \u0111\u01b0\u1ee3c s\u1eed d\u1ee5ng \u0111\u1ec3 \u0111\u00e1nh d\u1ea5u m\u1ed9t l\u1edbp l\u00e0 m\u1ed9t Spring Bean v\u00e0 \u0111\u01b0\u1ee3c Spring IoC Container qu\u1ea3n l\u00fd.&nbsp;<\/p>\n\n\n\n<p>S\u1ef1 kh\u00e1c bi\u1ec7t ch\u00ednh n\u1eb1m \u1edf ng\u1eef ngh\u0129a v\u00e0 m\u1ee5c \u0111\u00edch s\u1eed d\u1ee5ng, gi\u00fap t\u0103ng t\u00ednh r\u00f5 r\u00e0ng v\u00e0 kh\u1ea3 n\u0103ng \u0111\u1ecdc m\u00e3:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>@Component:<\/strong> L\u00e0 m\u1ed9t annotation chung, d\u00f9ng \u0111\u1ec3 \u0111\u00e1nh d\u1ea5u m\u1ed9t l\u1edbp l\u00e0 m\u1ed9t th\u00e0nh ph\u1ea7n \u0111\u01b0\u1ee3c Spring qu\u1ea3n l\u00fd. N\u00f3 l\u00e0 annotation c\u01a1 s\u1edf cho c\u00e1c annotation kh\u00e1c. B\u1ea1n c\u00f3 th\u1ec3 s\u1eed d\u1ee5ng n\u00f3 cho c\u00e1c th\u00e0nh ph\u1ea7n chung kh\u00f4ng thu\u1ed9c m\u1ed9t t\u1ea7ng c\u1ee5 th\u1ec3 n\u00e0o.<\/li>\n\n\n\n<li><strong>@Service:<\/strong> D\u00f9ng \u0111\u1ec3 \u0111\u00e1nh d\u1ea5u c\u00e1c l\u1edbp trong t\u1ea7ng d\u1ecbch v\u1ee5 (Service Layer). T\u1ea7ng n\u00e0y th\u01b0\u1eddng ch\u1ee9a logic nghi\u1ec7p v\u1ee5 ch\u00ednh c\u1ee7a \u1ee9ng d\u1ee5ng. @Service kh\u00f4ng th\u00eam b\u1ea5t k\u1ef3 h\u00e0nh vi k\u1ef9 thu\u1eadt n\u00e0o \u0111\u1eb7c bi\u1ec7t so v\u1edbi @Component, nh\u01b0ng n\u00f3 gi\u00fap r\u00f5 r\u00e0ng h\u01a1n v\u1ec1 vai tr\u00f2 c\u1ee7a l\u1edbp trong ki\u1ebfn tr\u00fac.<\/li>\n\n\n\n<li><strong>@Repository<\/strong>: D\u00f9ng \u0111\u1ec3 \u0111\u00e1nh d\u1ea5u c\u00e1c l\u1edbp trong t\u1ea7ng truy c\u1eadp d\u1eef li\u1ec7u (Data Access Layer), th\u01b0\u1eddng l\u00e0 c\u00e1c DAO (Data Access Object). Annotation n\u00e0y c\u00f3 th\u00eam m\u1ed9t t\u00ednh n\u0103ng \u0111\u1eb7c bi\u1ec7t: n\u00f3 t\u1ef1 \u0111\u1ed9ng chuy\u1ec3n \u0111\u1ed5i c\u00e1c ngo\u1ea1i l\u1ec7 c\u1ee5 th\u1ec3 c\u1ee7a c\u01a1 s\u1edf d\u1eef li\u1ec7u th\u00e0nh c\u00e1c ngo\u1ea1i l\u1ec7 kh\u00f4ng \u0111\u01b0\u1ee3c ki\u1ec3m tra (unchecked exceptions) c\u1ee7a Spring (DataAccessException), gi\u00fap x\u1eed l\u00fd l\u1ed7i nh\u1ea5t qu\u00e1n h\u01a1n.<\/li>\n\n\n\n<li><strong>@Controller: <\/strong>D\u00f9ng \u0111\u1ec3 \u0111\u00e1nh d\u1ea5u c\u00e1c l\u1edbp trong t\u1ea7ng tr\u00ecnh b\u00e0y (Presentation Layer) trong ki\u1ebfn tr\u00fac MVC truy\u1ec1n th\u1ed1ng. C\u00e1c l\u1edbp n\u00e0y x\u1eed l\u00fd c\u00e1c y\u00eau c\u1ea7u HTTP \u0111\u1ebfn, t\u01b0\u01a1ng t\u00e1c v\u1edbi t\u1ea7ng d\u1ecbch v\u1ee5 v\u00e0 tr\u1ea3 v\u1ec1 c\u00e1c View.<\/li>\n\n\n\n<li><strong>@RestController (th\u00eam):<\/strong> L\u00e0 s\u1ef1 k\u1ebft h\u1ee3p c\u1ee7a @Controller v\u00e0 @ResponseBody. N\u00f3 \u0111\u01b0\u1ee3c s\u1eed d\u1ee5ng cho vi\u1ec7c x\u00e2y d\u1ef1ng c\u00e1c RESTful API, n\u01a1i c\u00e1c ph\u01b0\u01a1ng th\u1ee9c c\u1ee7a controller tr\u1ef1c ti\u1ebfp tr\u1ea3 v\u1ec1 d\u1eef li\u1ec7u (th\u01b0\u1eddng l\u00e0 JSON ho\u1eb7c XML) thay v\u00ec t\u00ean View.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-5-spring-boot-starters-la-gi-k\u1ec3-ten-m\u1ed9t-vai-starters-b\u1ea1n-da-s\u1eed-d\u1ee5ng-vi-d\u1ee5-spring-boot-starter-web\"><strong>5. Spring Boot Starters l\u00e0 g\u00ec? K\u1ec3 t\u00ean m\u1ed9t v\u00e0i starters b\u1ea1n \u0111\u00e3 s\u1eed d\u1ee5ng (v\u00ed d\u1ee5: spring-boot-starter-web).<\/strong><\/h3>\n\n\n\n<p><strong>Spring Boot Starters<\/strong> l\u00e0 m\u1ed9t t\u1eadp h\u1ee3p c\u00e1c dependency \u0111\u01b0\u1ee3c c\u1ea5u h\u00ecnh s\u1eb5n, gi\u00fap \u0111\u01a1n gi\u1ea3n h\u00f3a vi\u1ec7c th\u00eam c\u00e1c ch\u1ee9c n\u0103ng ph\u1ed5 bi\u1ebfn v\u00e0o \u1ee9ng d\u1ee5ng Spring Boot. M\u1ed7i Starter l\u00e0 m\u1ed9t b\u1ed9 m\u00f4 t\u1ea3 dependency m\u00e0 b\u1ea1n c\u00f3 th\u1ec3 th\u00eam v\u00e0o pom.xml (Maven) ho\u1eb7c build.gradle (Gradle) c\u1ee7a m\u00ecnh. Khi b\u1ea1n th\u00eam m\u1ed9t Starter, n\u00f3 s\u1ebd t\u1ef1 \u0111\u1ed9ng k\u00e9o v\u1ec1 t\u1ea5t c\u1ea3 c\u00e1c th\u01b0 vi\u1ec7n c\u1ea7n thi\u1ebft v\u00e0 c\u1ea5u h\u00ecnh ch\u00fang m\u1ed9t c\u00e1ch h\u1ee3p l\u00fd, lo\u1ea1i b\u1ecf nhu c\u1ea7u ph\u1ea3i t\u1ef1 qu\u1ea3n l\u00fd t\u1eebng dependency v\u00e0 phi\u00ean b\u1ea3n c\u1ee7a ch\u00fang.<\/p>\n\n\n\n<p>M\u1ed9t v\u00e0i starters th\u01b0\u1eddng d\u00f9ng:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>spring-boot-starter-web:<\/strong> \u0110\u1ec3 x\u00e2y d\u1ef1ng c\u00e1c \u1ee9ng d\u1ee5ng web, bao g\u1ed3m RESTful API. N\u00f3 bao g\u1ed3m Tomcat nh\u00fang, Spring MVC, Jackson (\u0111\u1ec3 x\u1eed l\u00fd JSON), v.v.<\/li>\n\n\n\n<li><strong>spring-boot-starter-data-jpa:<\/strong> \u0110\u1ec3 l\u00e0m vi\u1ec7c v\u1edbi c\u01a1 s\u1edf d\u1eef li\u1ec7u quan h\u1ec7 b\u1eb1ng Spring Data JPA v\u00e0 Hibernate.<\/li>\n\n\n\n<li><strong>spring-boot-starter-security:<\/strong> \u0110\u1ec3 th\u00eam c\u00e1c t\u00ednh n\u0103ng b\u1ea3o m\u1eadt v\u00e0o \u1ee9ng d\u1ee5ng, bao g\u1ed3m x\u00e1c th\u1ef1c v\u00e0 \u1ee7y quy\u1ec1n.<\/li>\n\n\n\n<li><strong>spring-boot-starter-test:<\/strong> Cung c\u1ea5p c\u00e1c th\u01b0 vi\u1ec7n c\u1ea7n thi\u1ebft cho vi\u1ec7c ki\u1ec3m th\u1eed \u1ee9ng d\u1ee5ng Spring Boot, bao g\u1ed3m JUnit, Mockito, Spring Test, v.v.<\/li>\n\n\n\n<li><strong>spring-boot-starter-actuator:<\/strong> \u0110\u1ec3 gi\u00e1m s\u00e1t v\u00e0 qu\u1ea3n l\u00fd \u1ee9ng d\u1ee5ng trong m\u00f4i tr\u01b0\u1eddng s\u1ea3n xu\u1ea5t.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-6-gi\u1ea3i-thich-c\u01a1-ch\u1ebf-ho\u1ea1t-d\u1ed9ng-auto-configuration-c\u1ee7a-spring-boot\"><strong>6. Gi\u1ea3i th\u00edch c\u01a1 ch\u1ebf ho\u1ea1t \u0111\u1ed9ng Auto-Configuration c\u1ee7a Spring Boot.<\/strong><\/h3>\n\n\n\n<p>Auto-Configuration l\u00e0 m\u1ed9t trong nh\u1eefng t\u00ednh n\u0103ng c\u1ed1t l\u00f5i c\u1ee7a Spring Boot, gi\u00fap t\u1ef1 \u0111\u1ed9ng c\u1ea5u h\u00ecnh \u1ee9ng d\u1ee5ng Spring d\u1ef1a tr\u00ean c\u00e1c dependency c\u00f3 trong classpath, c\u00e1c Bean \u0111\u00e3 \u0111\u01b0\u1ee3c \u0111\u1ecbnh ngh\u0129a v\u00e0 c\u00e1c thu\u1ed9c t\u00ednh c\u1ea5u h\u00ecnh. M\u1ee5c ti\u00eau l\u00e0 gi\u1ea3m thi\u1ec3u l\u01b0\u1ee3ng c\u1ea5u h\u00ecnh th\u1ee7 c\u00f4ng m\u00e0 nh\u00e0 ph\u00e1t tri\u1ec3n ph\u1ea3i vi\u1ebft.<\/p>\n\n\n\n<p>C\u00e1ch ho\u1ea1t \u0111\u1ed9ng:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>@SpringBootApplication: <\/strong>Annotation n\u00e0y (th\u01b0\u1eddng \u0111\u1eb7t tr\u00ean l\u1edbp ch\u00ednh c\u1ee7a \u1ee9ng d\u1ee5ng) bao g\u1ed3m @EnableAutoConfiguration.<\/li>\n\n\n\n<li><strong>@EnableAutoConfiguration:<\/strong> Annotation n\u00e0y k\u00edch ho\u1ea1t c\u01a1 ch\u1ebf Auto-Configuration. Khi \u1ee9ng d\u1ee5ng kh\u1edfi \u0111\u1ed9ng, Spring Boot s\u1ebd t\u00ecm ki\u1ebfm c\u00e1c l\u1edbp Auto-Configuration trong classpath.<\/li>\n\n\n\n<li><strong>C\u00e1c l\u1edbp Auto-Configuration:<\/strong> C\u00e1c l\u1edbp n\u00e0y th\u01b0\u1eddng n\u1eb1m trong c\u00e1c th\u01b0 vi\u1ec7n Starter (v\u00ed d\u1ee5: spring-boot-autoconfigure). M\u1ed7i l\u1edbp Auto-Configuration ch\u1ee9a logic \u0111\u1ec3 c\u1ea5u h\u00ecnh m\u1ed9t t\u00ednh n\u0103ng c\u1ee5 th\u1ec3 (v\u00ed d\u1ee5: c\u1ea5u h\u00ecnh DataSource, Tomcat, Spring MVC).<\/li>\n\n\n\n<li><strong>@ConditionalOn&#8230; Annotations:<\/strong> \u0110\u00e2y l\u00e0 ch\u00eca kh\u00f3a c\u1ee7a Auto-Configuration. C\u00e1c l\u1edbp Auto-Configuration s\u1eed d\u1ee5ng m\u1ed9t lo\u1ea1t c\u00e1c annotation @ConditionalOn&#8230; \u0111\u1ec3 quy\u1ebft \u0111\u1ecbnh xem li\u1ec7u m\u1ed9t c\u1ea5u h\u00ecnh c\u1ee5 th\u1ec3 c\u00f3 n\u00ean \u0111\u01b0\u1ee3c \u00e1p d\u1ee5ng hay kh\u00f4ng. V\u00ed d\u1ee5:\n<ul class=\"wp-block-list\">\n<li>@ConditionalOnClass: Ch\u1ec9 c\u1ea5u h\u00ecnh n\u1ebfu m\u1ed9t l\u1edbp c\u1ee5 th\u1ec3 c\u00f3 trong classpath.<\/li>\n\n\n\n<li>@ConditionalOnMissingBean: Ch\u1ec9 c\u1ea5u h\u00ecnh n\u1ebfu m\u1ed9t Bean c\u00f3 ki\u1ec3u c\u1ee5 th\u1ec3 ch\u01b0a \u0111\u01b0\u1ee3c \u0111\u1ecbnh ngh\u0129a.<\/li>\n\n\n\n<li>@ConditionalOnProperty: Ch\u1ec9 c\u1ea5u h\u00ecnh n\u1ebfu m\u1ed9t thu\u1ed9c t\u00ednh c\u1ea5u h\u00ecnh c\u1ee5 th\u1ec3 c\u00f3 gi\u00e1 tr\u1ecb.<\/li>\n\n\n\n<li>@ConditionalOnWebApplication: Ch\u1ec9 c\u1ea5u h\u00ecnh n\u1ebfu \u0111\u00e2y l\u00e0 m\u1ed9t \u1ee9ng d\u1ee5ng web.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>C\u01a1 ch\u1ebf \u01b0u ti\u00ean:<\/strong> Spring Boot s\u1ebd \u00e1p d\u1ee5ng c\u00e1c c\u1ea5u h\u00ecnh m\u1eb7c \u0111\u1ecbnh n\u00e0y, nh\u01b0ng n\u1ebfu nh\u00e0 ph\u00e1t tri\u1ec3n cung c\u1ea5p c\u1ea5u h\u00ecnh t\u00f9y ch\u1ec9nh (v\u00ed d\u1ee5: \u0111\u1ecbnh ngh\u0129a m\u1ed9t Bean v\u1edbi c\u00f9ng ki\u1ec3u), c\u1ea5u h\u00ecnh t\u00f9y ch\u1ec9nh \u0111\u00f3 s\u1ebd \u0111\u01b0\u1ee3c \u01b0u ti\u00ean v\u00e0 ghi \u0111\u00e8 l\u00ean c\u1ea5u h\u00ecnh t\u1ef1 \u0111\u1ed9ng.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-7-vong-d\u1eddi-c\u1ee7a-m\u1ed9t-bean-bean-lifecycle-trong-spring-di\u1ec5n-ra-nh\u01b0-th\u1ebf-nao\"><strong>7. V\u00f2ng \u0111\u1eddi c\u1ee7a m\u1ed9t Bean (Bean Lifecycle) trong Spring di\u1ec5n ra nh\u01b0 th\u1ebf n\u00e0o?<\/strong><\/h3>\n\n\n\n<p>V\u00f2ng \u0111\u1eddi c\u1ee7a m\u1ed9t Bean trong Spring l\u00e0 chu\u1ed7i c\u00e1c b\u01b0\u1edbc m\u00e0 Spring IoC Container th\u1ef1c hi\u1ec7n t\u1eeb khi kh\u1edfi t\u1ea1o Bean cho \u0111\u1ebfn khi n\u00f3 b\u1ecb h\u1ee7y. C\u00e1c b\u01b0\u1edbc ch\u00ednh bao g\u1ed3m:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Kh\u1edfi t\u1ea1o (Instantiation):<\/strong> Spring IoC Container t\u1ea1o m\u1ed9t instance c\u1ee7a Bean (g\u1ecdi constructor).<\/li>\n\n\n\n<li><strong>\u0110i\u1ec1n thu\u1ed9c t\u00ednh (Populate Properties \/ Dependency Injection):<\/strong> Spring ti\u00eam c\u00e1c dependency (c\u00e1c Bean kh\u00e1c) v\u00e0o instance v\u1eeba t\u1ea1o th\u00f4ng qua setter, constructor ho\u1eb7c field injection.<\/li>\n\n\n\n<li><strong>BeanNameAware (n\u1ebfu c\u00f3):<\/strong> N\u1ebfu Bean implement BeanNameAware, ph\u01b0\u01a1ng th\u1ee9c setBeanName() s\u1ebd \u0111\u01b0\u1ee3c g\u1ecdi, cung c\u1ea5p t\u00ean c\u1ee7a Bean.<\/li>\n\n\n\n<li><strong>BeanFactoryAware (n\u1ebfu c\u00f3): <\/strong>N\u1ebfu Bean implement BeanFactoryAware, ph\u01b0\u01a1ng th\u1ee9c setBeanFactory() s\u1ebd \u0111\u01b0\u1ee3c g\u1ecdi, cung c\u1ea5p tham chi\u1ebfu \u0111\u1ebfn BeanFactory (ho\u1eb7c ApplicationContext).<\/li>\n\n\n\n<li><strong>ApplicationContextAware (n\u1ebfu c\u00f3): <\/strong>N\u1ebfu Bean implement ApplicationContextAware, ph\u01b0\u01a1ng th\u1ee9c setApplicationContext() s\u1ebd \u0111\u01b0\u1ee3c g\u1ecdi, cung c\u1ea5p tham chi\u1ebfu \u0111\u1ebfn ApplicationContext.<\/li>\n\n\n\n<li><strong>BeanPostProcessors &#8211; postProcessBeforeInitialization():<\/strong> C\u00e1c BeanPostProcessors \u0111\u00e3 \u0111\u0103ng k\u00fd s\u1ebd \u0111\u01b0\u1ee3c g\u1ecdi. Ph\u01b0\u01a1ng th\u1ee9c postProcessBeforeInitialization() c\u1ee7a ch\u00fang s\u1ebd \u0111\u01b0\u1ee3c th\u1ef1c thi.<\/li>\n\n\n\n<li><strong>InitializingBean &#8211; afterPropertiesSet() (n\u1ebfu c\u00f3):<\/strong> N\u1ebfu Bean implement InitializingBean, ph\u01b0\u01a1ng th\u1ee9c afterPropertiesSet() s\u1ebd \u0111\u01b0\u1ee3c g\u1ecdi. \u0110\u00e2y l\u00e0 n\u01a1i b\u1ea1n c\u00f3 th\u1ec3 th\u1ef1c hi\u1ec7n c\u00e1c kh\u1edfi t\u1ea1o t\u00f9y ch\u1ec9nh sau khi t\u1ea5t c\u1ea3 c\u00e1c thu\u1ed9c t\u00ednh \u0111\u00e3 \u0111\u01b0\u1ee3c thi\u1ebft l\u1eadp.<\/li>\n\n\n\n<li><strong>Custom init-method (n\u1ebfu c\u00f3):<\/strong> N\u1ebfu b\u1ea1n \u0111\u00e3 ch\u1ec9 \u0111\u1ecbnh m\u1ed9t ph\u01b0\u01a1ng th\u1ee9c init-method trong c\u1ea5u h\u00ecnh Bean (v\u00ed d\u1ee5: th\u00f4ng qua @Bean(initMethod = &#8220;myInitMethod&#8221;) ho\u1eb7c XML), ph\u01b0\u01a1ng th\u1ee9c \u0111\u00f3 s\u1ebd \u0111\u01b0\u1ee3c g\u1ecdi.<\/li>\n\n\n\n<li><strong>BeanPostProcessors &#8211; postProcessAfterInitialization():<\/strong> C\u00e1c BeanPostProcessors l\u1ea1i \u0111\u01b0\u1ee3c g\u1ecdi. Ph\u01b0\u01a1ng th\u1ee9c postProcessAfterInitialization() c\u1ee7a ch\u00fang s\u1ebd \u0111\u01b0\u1ee3c th\u1ef1c thi. T\u1ea1i th\u1eddi \u0111i\u1ec3m n\u00e0y, Bean \u0111\u00e3 s\u1eb5n s\u00e0ng \u0111\u1ec3 s\u1eed d\u1ee5ng.<\/li>\n\n\n\n<li><strong>S\u1eed d\u1ee5ng Bean:<\/strong> Bean \u0111\u01b0\u1ee3c s\u1eed d\u1ee5ng trong \u1ee9ng d\u1ee5ng.<\/li>\n\n\n\n<li><strong>DisposableBean &#8211; destroy() (n\u1ebfu c\u00f3):<\/strong> Khi Container b\u1ecb \u0111\u00f3ng, n\u1ebfu Bean implement DisposableBean, ph\u01b0\u01a1ng th\u1ee9c destroy() s\u1ebd \u0111\u01b0\u1ee3c g\u1ecdi.<\/li>\n\n\n\n<li><strong>Custom destroy-method (n\u1ebfu c\u00f3): <\/strong>N\u1ebfu b\u1ea1n \u0111\u00e3 ch\u1ec9 \u0111\u1ecbnh m\u1ed9t ph\u01b0\u01a1ng th\u1ee9c destroy-method trong c\u1ea5u h\u00ecnh Bean (v\u00ed d\u1ee5: th\u00f4ng qua @Bean(destroyMethod = &#8220;myDestroyMethod&#8221;) ho\u1eb7c XML), ph\u01b0\u01a1ng th\u1ee9c \u0111\u00f3 s\u1ebd \u0111\u01b0\u1ee3c g\u1ecdi.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-8-s\u1ef1-khac-bi\u1ec7t-gi\u1eefa-cac-scope-c\u1ee7a-bean-singleton-prototype-request-session-scope-m\u1eb7c-d\u1ecbnh-la-gi\"><strong>8. S\u1ef1 kh\u00e1c bi\u1ec7t gi\u1eefa c\u00e1c scope c\u1ee7a Bean (Singleton, Prototype, Request, Session)? Scope m\u1eb7c \u0111\u1ecbnh l\u00e0 g\u00ec?<\/strong><\/h3>\n\n\n\n<p>Scope c\u1ee7a Bean x\u00e1c \u0111\u1ecbnh c\u00e1ch m\u00e0 Spring IoC Container qu\u1ea3n l\u00fd c\u00e1c instance c\u1ee7a Bean. Scope m\u1eb7c \u0111\u1ecbnh l\u00e0 Singleton.<\/p>\n\n\n\n<p>S\u1ef1 kh\u00e1c bi\u1ec7t gi\u1eefa c\u00e1c scope:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>Scope<\/td><td>M\u00f4 t\u1ea3<\/td><td>Tr\u01b0\u1eddng h\u1ee3p s\u1eed d\u1ee5ng<\/td><\/tr><tr><td>Singleton <em>(m\u1eb7c \u0111\u1ecbnh)<\/em><\/td><td>Ch\u1ec9 c\u00f3 m\u1ed9t instance duy nh\u1ea5t c\u1ee7a Bean \u0111\u01b0\u1ee3c t\u1ea1o cho m\u1ed7i Spring IoC Container. M\u1ecdi y\u00eau c\u1ea7u \u0111\u1ebfn Bean n\u00e0y \u0111\u1ec1u nh\u1eadn c\u00f9ng m\u1ed9t instance.<\/td><td>D\u00f9ng cho c\u00e1c Bean kh\u00f4ng c\u00f3 tr\u1ea1ng th\u00e1i (stateless) ho\u1eb7c c\u1ea7n chia s\u1ebb tr\u1ea1ng th\u00e1i chung to\u00e0n \u1ee9ng d\u1ee5ng, nh\u01b0 @Service, @Repository, @Controller.<\/td><\/tr><tr><td>Prototype<\/td><td>M\u1ed7i khi Bean \u0111\u01b0\u1ee3c y\u00eau c\u1ea7u, Spring t\u1ea1o m\u1ed9t instance m\u1edbi ho\u00e0n to\u00e0n.<\/td><td>D\u00f9ng cho c\u00e1c Bean c\u00f3 tr\u1ea1ng th\u00e1i (stateful), n\u01a1i m\u1ed7i client ho\u1eb7c lu\u1ed3ng c\u1ea7n m\u1ed9t b\u1ea3n sao ri\u00eang bi\u1ec7t.<\/td><\/tr><tr><td>Request <em>(Web-only)<\/em><\/td><td>M\u1ed9t instance m\u1edbi \u0111\u01b0\u1ee3c t\u1ea1o cho m\u1ed7i HTTP request. Bean t\u1ed3n t\u1ea1i trong su\u1ed1t v\u00f2ng \u0111\u1eddi c\u1ee7a request \u0111\u00f3.<\/td><td>D\u00f9ng cho c\u00e1c Bean c\u1ea7n l\u01b0u d\u1eef li\u1ec7u t\u1ea1m trong m\u1ed9t request (v\u00ed d\u1ee5: th\u00f4ng tin form).<\/td><\/tr><tr><td>Session <em>(Web-only)<\/em><\/td><td>M\u1ed7i HTTP Session s\u1ebd c\u00f3 m\u1ed9t instance ri\u00eang bi\u1ec7t c\u1ee7a Bean. Bean t\u1ed3n t\u1ea1i su\u1ed1t v\u00f2ng \u0111\u1eddi phi\u00ean l\u00e0m vi\u1ec7c.<\/td><td>D\u00f9ng cho c\u00e1c Bean c\u1ea7n l\u01b0u th\u00f4ng tin li\u00ean quan \u0111\u1ebfn m\u1ed9t phi\u00ean l\u00e0m vi\u1ec7c c\u1ee7a ng\u01b0\u1eddi d\u00f9ng (v\u00ed d\u1ee5: gi\u1ecf h\u00e0ng).<\/td><\/tr><tr><td>Application <em>(Web-only)<\/em><\/td><td>T\u01b0\u01a1ng t\u1ef1 Singleton nh\u01b0ng ph\u1ea1m vi \u1edf ServletContext, m\u1ed9t instance duy nh\u1ea5t cho to\u00e0n b\u1ed9 \u1ee9ng d\u1ee5ng web.<\/td><td>D\u00f9ng khi c\u1ea7n chia s\u1ebb d\u1eef li\u1ec7u ho\u1eb7c c\u1ea5u h\u00ecnh tr\u00ean ph\u1ea1m vi to\u00e0n b\u1ed9 web app.<\/td><\/tr><tr><td>WebSocket <em>(Web-only)<\/em><\/td><td>T\u1ea1o m\u1ed9t instance duy nh\u1ea5t cho m\u1ed7i phi\u00ean WebSocket.<\/td><td>D\u00f9ng cho c\u00e1c Bean c\u1ea7n l\u01b0u tr\u1ea1ng th\u00e1i xuy\u00ean su\u1ed1t phi\u00ean giao ti\u1ebfp WebSocket.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-9-qualifier-va-primary-d\u01b0\u1ee3c-s\u1eed-d\u1ee5ng-d\u1ec3-lam-gi-trong-tr\u01b0\u1eddng-h\u1ee3p-co-nhi\u1ec1u-bean-cung-m\u1ed9t-ki\u1ec3u\"><strong>9. @Qualifier v\u00e0 @Primary \u0111\u01b0\u1ee3c s\u1eed d\u1ee5ng \u0111\u1ec3 l\u00e0m g\u00ec trong tr\u01b0\u1eddng h\u1ee3p c\u00f3 nhi\u1ec1u Bean c\u00f9ng m\u1ed9t ki\u1ec3u?<\/strong><\/h3>\n\n\n\n<p>Khi c\u00f3 nhi\u1ec1u Bean c\u00f9ng m\u1ed9t ki\u1ec3u trong Spring IoC Container, Spring s\u1ebd kh\u00f4ng bi\u1ebft Bean n\u00e0o c\u1ea7n \u0111\u01b0\u1ee3c ti\u00eam v\u00e0o m\u1ed9t dependency c\u1ee5 th\u1ec3, d\u1eabn \u0111\u1ebfn l\u1ed7i NoUniqueBeanDefinitionException. @Qualifier v\u00e0 @Primary \u0111\u01b0\u1ee3c s\u1eed d\u1ee5ng \u0111\u1ec3 gi\u1ea3i quy\u1ebft v\u1ea5n \u0111\u1ec1 n\u00e0y.<\/p>\n\n\n\n<p><strong>@Primary: <\/strong>Annotation @Primary \u0111\u01b0\u1ee3c \u0111\u1eb7t tr\u00ean m\u1ed9t Bean \u0111\u1ec3 ch\u1ec9 \u0111\u1ecbnh r\u1eb1ng n\u00f3 l\u00e0 Bean \u0111\u01b0\u1ee3c \u01b0u ti\u00ean (primary) khi c\u00f3 nhi\u1ec1u Bean c\u00f9ng ki\u1ec3u. N\u1ebfu kh\u00f4ng c\u00f3 @Qualifier c\u1ee5 th\u1ec3 n\u00e0o \u0111\u01b0\u1ee3c ch\u1ec9 \u0111\u1ecbnh, Spring s\u1ebd t\u1ef1 \u0111\u1ed9ng ch\u1ecdn Bean \u0111\u01b0\u1ee3c \u0111\u00e1nh d\u1ea5u @Primary.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>S\u1eed d\u1ee5ng khi:<\/strong> B\u1ea1n mu\u1ed1n c\u00f3 m\u1ed9t Bean m\u1eb7c \u0111\u1ecbnh \u0111\u01b0\u1ee3c ch\u1ecdn trong s\u1ed1 nhi\u1ec1u Bean c\u00f9ng ki\u1ec3u.<\/li>\n\n\n\n<li><strong>V\u00ed d\u1ee5:<\/strong><\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>@Service\n@Primary\npublic class SmsNotificationService implements NotificationService { ... }\n\n@Service\npublic class EmailNotificationService implements NotificationService { ... }\n\n@Component\npublic class MyClient {\n    @Autowired\n    private NotificationService notificationService; \/\/ S\u1ebd ti\u00eam SmsNotificationService\n}<\/code><\/pre>\n\n\n\n<p><strong>@Qualifier: <\/strong>Annotation @Qualifier \u0111\u01b0\u1ee3c s\u1eed d\u1ee5ng \u0111\u1ec3 ch\u1ec9 \u0111\u1ecbnh t\u00ean c\u1ee5 th\u1ec3 c\u1ee7a Bean m\u00e0 b\u1ea1n mu\u1ed1n ti\u00eam. B\u1ea1n \u0111\u1eb7t @Qualifier tr\u00ean c\u1ea3 Bean khi \u0111\u1ecbnh ngh\u0129a v\u00e0 tr\u00ean \u0111i\u1ec3m ti\u00eam (autowired field\/constructor\/setter).<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>S\u1eed d\u1ee5ng khi: <\/strong>B\u1ea1n c\u1ea7n ki\u1ec3m so\u00e1t ch\u00ednh x\u00e1c Bean n\u00e0o \u0111\u01b0\u1ee3c ti\u00eam, \u0111\u1eb7c bi\u1ec7t khi c\u00f3 nhi\u1ec1u Bean c\u00f9ng ki\u1ec3u v\u00e0 kh\u00f4ng c\u00f3 m\u1ed9t Bean &#8220;m\u1eb7c \u0111\u1ecbnh&#8221; r\u00f5 r\u00e0ng.<\/li>\n\n\n\n<li><strong>V\u00ed d\u1ee5:<\/strong><\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>@Service(\"smsService\")\npublic class SmsNotificationService implements NotificationService { ... }\n\n@Service(\"emailService\")\npublic class EmailNotificationService implements NotificationService { ... }\n\n@Component\npublic class MyClient {\n    @Autowired\n    @Qualifier(\"emailService\")\n    private NotificationService notificationService; \/\/ S\u1ebd ti\u00eam EmailNotificationService\n}<\/code><\/pre>\n\n\n\n<p>@Qualifier c\u00f3 \u0111\u1ed9 \u01b0u ti\u00ean cao h\u01a1n @Primary. N\u1ebfu c\u1ea3 hai \u0111\u1ec1u \u0111\u01b0\u1ee3c s\u1eed d\u1ee5ng, @Qualifier s\u1ebd \u0111\u01b0\u1ee3c \u01b0u ti\u00ean.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-10-co-cac-lo\u1ea1i-conditionalon-annotation-nao-chung-ho\u1ea1t-d\u1ed9ng-nh\u01b0-th\u1ebf-nao\"><strong>10. C\u00f3 c\u00e1c lo\u1ea1i @ConditionalOn&#8230; annotation n\u00e0o? Ch\u00fang ho\u1ea1t \u0111\u1ed9ng nh\u01b0 th\u1ebf n\u00e0o?\u00a0<\/strong><\/h3>\n\n\n\n<p>Nh\u01b0 \u0111\u00e3 \u0111\u1ec1 c\u1eadp \u1edf c\u00e2u 6, c\u00e1c annotation @ConditionalOn&#8230; l\u00e0 m\u1ed9t ph\u1ea7n quan tr\u1ecdng c\u1ee7a c\u01a1 ch\u1ebf Auto-Configuration trong Spring Boot. Ch\u00fang \u0111\u01b0\u1ee3c s\u1eed d\u1ee5ng \u0111\u1ec3 ki\u1ec3m so\u00e1t c\u00f3 \u0111i\u1ec1u ki\u1ec7n vi\u1ec7c \u0111\u0103ng k\u00fd Bean ho\u1eb7c c\u1ea5u h\u00ecnh. Spring Boot s\u1ebd ki\u1ec3m tra c\u00e1c \u0111i\u1ec1u ki\u1ec7n n\u00e0y trong qu\u00e1 tr\u00ecnh kh\u1edfi \u0111\u1ed9ng \u1ee9ng d\u1ee5ng. N\u1ebfu t\u1ea5t c\u1ea3 c\u00e1c \u0111i\u1ec1u ki\u1ec7n \u0111\u01b0\u1ee3c \u0111\u00e1p \u1ee9ng, Bean ho\u1eb7c c\u1ea5u h\u00ecnh t\u01b0\u01a1ng \u1ee9ng s\u1ebd \u0111\u01b0\u1ee3c \u00e1p d\u1ee5ng; n\u1ebfu kh\u00f4ng, ch\u00fang s\u1ebd b\u1ecb b\u1ecf qua.<\/p>\n\n\n\n<p><strong>C\u00e1c lo\u1ea1i ph\u1ed5 bi\u1ebfn:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>@ConditionalOnClass: Bean\/c\u1ea5u h\u00ecnh ch\u1ec9 \u0111\u01b0\u1ee3c t\u1ea1o n\u1ebfu l\u1edbp \u0111\u01b0\u1ee3c ch\u1ec9 \u0111\u1ecbnh c\u00f3 trong classpath.<\/li>\n\n\n\n<li>@ConditionalOnMissingClass: Bean\/c\u1ea5u h\u00ecnh ch\u1ec9 \u0111\u01b0\u1ee3c t\u1ea1o n\u1ebfu l\u1edbp \u0111\u01b0\u1ee3c ch\u1ec9 \u0111\u1ecbnh kh\u00f4ng c\u00f3 trong classpath.<\/li>\n\n\n\n<li>@ConditionalOnBean: Bean\/c\u1ea5u h\u00ecnh ch\u1ec9 \u0111\u01b0\u1ee3c t\u1ea1o n\u1ebfu Bean c\u00f3 ki\u1ec3u\/t\u00ean \u0111\u01b0\u1ee3c ch\u1ec9 \u0111\u1ecbnh \u0111\u00e3 t\u1ed3n t\u1ea1i trong IoC Container.<\/li>\n\n\n\n<li>@ConditionalOnMissingBean: Bean\/c\u1ea5u h\u00ecnh ch\u1ec9 \u0111\u01b0\u1ee3c t\u1ea1o n\u1ebfu Bean c\u00f3 ki\u1ec3u\/t\u00ean \u0111\u01b0\u1ee3c ch\u1ec9 \u0111\u1ecbnh kh\u00f4ng t\u1ed3n t\u1ea1i trong IoC Container.<\/li>\n\n\n\n<li>@ConditionalOnProperty: Bean\/c\u1ea5u h\u00ecnh ch\u1ec9 \u0111\u01b0\u1ee3c t\u1ea1o n\u1ebfu m\u1ed9t thu\u1ed9c t\u00ednh c\u1ea5u h\u00ecnh c\u1ee5 th\u1ec3 c\u00f3 gi\u00e1 tr\u1ecb mong mu\u1ed1n (ho\u1eb7c t\u1ed3n t\u1ea1i).<\/li>\n\n\n\n<li>@ConditionalOnExpression: Bean\/c\u1ea5u h\u00ecnh ch\u1ec9 \u0111\u01b0\u1ee3c t\u1ea1o n\u1ebfu m\u1ed9t bi\u1ec3u th\u1ee9c SpEL (Spring Expression Language) \u0111\u00e1nh gi\u00e1 l\u00e0 true.<\/li>\n\n\n\n<li>@ConditionalOnResource: Bean\/c\u1ea5u h\u00ecnh ch\u1ec9 \u0111\u01b0\u1ee3c t\u1ea1o n\u1ebfu m\u1ed9t t\u00e0i nguy\u00ean c\u1ee5 th\u1ec3 (v\u00ed d\u1ee5: t\u1ec7p c\u1ea5u h\u00ecnh) t\u1ed3n t\u1ea1i.<\/li>\n\n\n\n<li>@ConditionalOnWebApplication: Bean\/c\u1ea5u h\u00ecnh ch\u1ec9 \u0111\u01b0\u1ee3c t\u1ea1o n\u1ebfu \u1ee9ng d\u1ee5ng l\u00e0 m\u1ed9t \u1ee9ng d\u1ee5ng web.<\/li>\n\n\n\n<li>@ConditionalOnNotWebApplication: Bean\/c\u1ea5u h\u00ecnh ch\u1ec9 \u0111\u01b0\u1ee3c t\u1ea1o n\u1ebfu \u1ee9ng d\u1ee5ng kh\u00f4ng ph\u1ea3i l\u00e0 m\u1ed9t \u1ee9ng d\u1ee5ng web.<\/li>\n<\/ul>\n\n\n\n<p><strong>C\u01a1 ch\u1ebf ho\u1ea1t \u0111\u1ed9ng:<\/strong> Spring Boot s\u1eed d\u1ee5ng SpringFactoriesLoader \u0111\u1ec3 t\u00ecm c\u00e1c l\u1edbp c\u1ea5u h\u00ecnh t\u1ef1 \u0111\u1ed9ng \u0111\u01b0\u1ee3c li\u1ec7t k\u00ea trong t\u1ec7p META-INF\/spring.factories trong classpath. Sau \u0111\u00f3, n\u00f3 duy\u1ec7t qua t\u1eebng l\u1edbp c\u1ea5u h\u00ecnh n\u00e0y v\u00e0 ki\u1ec3m tra c\u00e1c \u0111i\u1ec1u ki\u1ec7n @ConditionalOn&#8230; tr\u01b0\u1edbc khi quy\u1ebft \u0111\u1ecbnh c\u00f3 t\u1ea1o c\u00e1c Bean b\u00ean trong l\u1edbp c\u1ea5u h\u00ecnh \u0111\u00f3 hay kh\u00f4ng.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-11-lam-th\u1ebf-nao-d\u1ec3-t\u1ea1o-m\u1ed9t-custom-auto-configuration-cho-rieng-minh\"><strong>11. L\u00e0m th\u1ebf n\u00e0o \u0111\u1ec3 t\u1ea1o m\u1ed9t custom auto-configuration cho ri\u00eang m\u00ecnh?<\/strong><\/h3>\n\n\n\n<p>\u0110\u1ec3 t\u1ea1o m\u1ed9t custom auto-configuration, b\u1ea1n c\u1ea7n th\u1ef1c hi\u1ec7n c\u00e1c b\u01b0\u1edbc sau:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>T\u1ea1o m\u1ed9t l\u1edbp c\u1ea5u h\u00ecnh (@Configuration class):<\/strong><strong><br><\/strong><strong><br><\/strong>L\u1edbp n\u00e0y s\u1ebd ch\u1ee9a c\u00e1c @Bean definitions m\u00e0 b\u1ea1n mu\u1ed1n t\u1ef1 \u0111\u1ed9ng c\u1ea5u h\u00ecnh. S\u1eed d\u1ee5ng @ConditionalOn&#8230; annotations \u0111\u1ec3 ki\u1ec3m so\u00e1t khi n\u00e0o c\u00e1c Bean n\u00e0y n\u00ean \u0111\u01b0\u1ee3c t\u1ea1o.<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ MyServiceAutoConfiguration.java\n@Configuration\n@ConditionalOnClass(MyService.class) \/\/ Ch\u1ec9 c\u1ea5u h\u00ecnh n\u1ebfu MyService c\u00f3 trong classpath\n@ConditionalOnMissingBean(MyService.class) \/\/ Ch\u1ec9 c\u1ea5u h\u00ecnh n\u1ebfu ch\u01b0a c\u00f3 Bean MyService n\u00e0o \u0111\u01b0\u1ee3c \u0111\u1ecbnh ngh\u0129a\n@ConditionalOnProperty(prefix = \"my.service\", name = \"enabled\", havingValue = \"true\", matchIfMissing = true)\npublic class MyServiceAutoConfiguration {\n\n    @Bean\n    public MyService myService() {\n        return new MyServiceImpl();\n    }\n}<\/code><\/pre>\n\n\n\n<p>Trong v\u00ed d\u1ee5 n\u00e0y:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>@ConditionalOnClass(MyService.class):<\/strong> \u0110\u1ea3m b\u1ea3o r\u1eb1ng MyService t\u1ed3n t\u1ea1i trong classpath.<\/li>\n\n\n\n<li><strong>@ConditionalOnMissingBean(MyService.class):<\/strong> \u0110\u1ea3m b\u1ea3o r\u1eb1ng n\u1ebfu ng\u01b0\u1eddi d\u00f9ng \u0111\u00e3 t\u1ef1 \u0111\u1ecbnh ngh\u0129a m\u1ed9t MyService Bean, c\u1ea5u h\u00ecnh n\u00e0y s\u1ebd kh\u00f4ng ghi \u0111\u00e8.<\/li>\n\n\n\n<li><strong>@ConditionalOnProperty:<\/strong> Cho ph\u00e9p ng\u01b0\u1eddi d\u00f9ng b\u1eadt\/t\u1eaft c\u1ea5u h\u00ecnh n\u00e0y th\u00f4ng qua thu\u1ed9c t\u00ednh <strong>my.service.enabled=true<\/strong> (m\u1eb7c \u0111\u1ecbnh l\u00e0 true n\u1ebfu kh\u00f4ng c\u00f3).<\/li>\n\n\n\n<li><strong>T\u1ea1o m\u1ed9t t\u1ec7p<\/strong><strong> spring.factories:<\/strong><\/li>\n<\/ul>\n\n\n\n<p>Trong th\u01b0 m\u1ee5c src\/main\/resources\/META-INF\/, t\u1ea1o m\u1ed9t t\u1ec7p c\u00f3 t\u00ean spring.factories. Trong t\u1ec7p n\u00e0y, b\u1ea1n s\u1ebd li\u1ec7t k\u00ea l\u1edbp c\u1ea5u h\u00ecnh t\u1ef1 \u0111\u1ed9ng c\u1ee7a m\u00ecnh d\u01b0\u1edbi kh\u00f3a.&nbsp;<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>org.springframework.boot.autoconfigure.EnableAutoConfiguration.\n\n# src\/main\/resources\/META-INF\/spring.factories\norg.springframework.boot.autoconfigure.EnableAutoConfiguration=\\\ncom.example.autoconfiguration.MyServiceAutoConfiguration<\/code><\/pre>\n\n\n\n<p>(L\u01b0u \u00fd: \\ \u0111\u01b0\u1ee3c s\u1eed d\u1ee5ng \u0111\u1ec3 ng\u1eaft d\u00f2ng n\u1ebfu b\u1ea1n c\u00f3 nhi\u1ec1u l\u1edbp auto-configuration.)<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u0110\u00f3ng g\u00f3i v\u00e0 s\u1eed d\u1ee5ng:<\/strong><\/li>\n<\/ul>\n\n\n\n<p>\u0110\u00f3ng g\u00f3i module ch\u1ee9a custom auto-configuration n\u00e0y th\u00e0nh m\u1ed9t JAR. Khi b\u1ea1n th\u00eam JAR n\u00e0y v\u00e0o classpath c\u1ee7a m\u1ed9t \u1ee9ng d\u1ee5ng Spring Boot kh\u00e1c, Spring Boot s\u1ebd t\u1ef1 \u0111\u1ed9ng ph\u00e1t hi\u1ec7n v\u00e0 \u00e1p d\u1ee5ng c\u1ea5u h\u00ecnh c\u1ee7a b\u1ea1n d\u1ef1a tr\u00ean c\u00e1c \u0111i\u1ec1u ki\u1ec7n \u0111\u00e3 \u0111\u1ecbnh.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-cau-h\u1ecfi-ph\u1ecfng-v\u1ea5n-v\u1ec1-spring-mvc-amp-restful-apis\"><span class=\"ez-toc-section\" id=\"Cau_hoi_phong_van_ve_Spring_MVC_RESTful_APIs\"><\/span><strong>C\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n v\u1ec1 Spring MVC &amp; RESTful APIs<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-12-restcontroller-khac-gi-so-v\u1edbi-controller\"><strong>12. @RestController kh\u00e1c g\u00ec so v\u1edbi @Controller?<\/strong><\/h3>\n\n\n\n<p>S\u1ef1 kh\u00e1c bi\u1ec7t ch\u00ednh gi\u1eefa <strong>@RestController<\/strong> v\u00e0 <strong>@Controller<\/strong> n\u1eb1m \u1edf <strong>c\u00e1ch ch\u00fang x\u1eed l\u00fd k\u1ebft qu\u1ea3 tr\u1ea3 v\u1ec1 t\u1eeb c\u00e1c method<\/strong>.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>@Controller<\/strong> l\u00e0 m\u1ed9t annotation c\u01a1 b\u1ea3n c\u1ee7a Spring MVC, d\u00f9ng \u0111\u1ec3 \u0111\u00e1nh d\u1ea5u m\u1ed9t class l\u00e0 Controller.&nbsp;<\/li>\n<\/ul>\n\n\n\n<p>Khi d\u00f9ng @Controller, c\u00e1c method th\u01b0\u1eddng tr\u1ea3 v\u1ec1 m\u1ed9t String l\u00e0 t\u00ean c\u1ee7a View (v\u00ed d\u1ee5: t\u00ean file JSP, Thymeleaf). Spring s\u1ebd d\u1ef1a v\u00e0o ViewResolver \u0111\u1ec3 t\u00ecm v\u00e0 render ra giao di\u1ec7n HTML cho ng\u01b0\u1eddi d\u00f9ng. N\u1ebfu mu\u1ed1n tr\u1ea3 v\u1ec1 d\u1eef li\u1ec7u d\u1ea1ng JSON\/XML, ta ph\u1ea3i k\u1ebft h\u1ee3p th\u00eam annotation <strong>@ResponseBody<\/strong> tr\u00ean t\u1eebng method.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@Controller\npublic class UserController {\n    @RequestMapping(\"\/user-ui\")\n    public String getUserPage() {\n        return \"user-view\"; \/\/ Tr\u1ea3 v\u1ec1 file user-view.html\n    }\n\n    @RequestMapping(\"\/api\/user\")\n    @ResponseBody \/\/ C\u1ea7n th\u00eam @ResponseBody \u0111\u1ec3 tr\u1ea3 v\u1ec1 JSON\n    public User getUserData() {\n        return new User(\"john\", \"john@example.com\"); \n    }\n}<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>@RestController<\/strong> l\u00e0 m\u1ed9t annotation ti\u1ec7n l\u1ee3i \u0111\u01b0\u1ee3c gi\u1edbi thi\u1ec7u t\u1eeb Spring 4.0. N\u00f3 th\u1ef1c ch\u1ea5t l\u00e0 s\u1ef1 k\u1ebft h\u1ee3p c\u1ee7a <strong>@Controller v\u00e0 @ResponseBody<\/strong>.&nbsp;<\/li>\n<\/ul>\n\n\n\n<p>Khi d\u00f9ng @RestController, t\u1ea5t c\u1ea3 c\u00e1c method trong class \u0111\u00f3 s\u1ebd m\u1eb7c \u0111\u1ecbnh c\u00f3 @ResponseBody, ngh\u0129a l\u00e0 k\u1ebft qu\u1ea3 tr\u1ea3 v\u1ec1 (th\u01b0\u1eddng l\u00e0 Object) s\u1ebd \u0111\u01b0\u1ee3c t\u1ef1 \u0111\u1ed9ng chuy\u1ec3n \u0111\u1ed5i th\u00e0nh JSON ho\u1eb7c XML b\u1edfi HttpMessageConverter v\u00e0 g\u1eedi tr\u1ef1c ti\u1ebfp v\u1ec1 cho client. Annotation n\u00e0y \u0111\u01b0\u1ee3c sinh ra chuy\u00ean \u0111\u1ec3 x\u00e2y d\u1ef1ng RESTful APIs.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@RestController\n@RequestMapping(\"\/api\")\npublic class UserRestController {\n    @GetMapping(\"\/user\") \/\/ Kh\u00f4ng c\u1ea7n @ResponseBody\n    public User getUserData() {\n        return new User(\"john\", \"john@example.com\"); \/\/ T\u1ef1 \u0111\u1ed9ng chuy\u1ec3n th\u00e0nh JSON\n    }\n}<\/code><\/pre>\n\n\n\n<p>T\u00f3m l\u1ea1i: D\u00f9ng @Controller khi x\u00e2y d\u1ef1ng web application truy\u1ec1n th\u1ed1ng tr\u1ea3 v\u1ec1 view, v\u00e0 d\u00f9ng @RestController khi x\u00e2y d\u1ef1ng RESTful API ch\u1ec9 tr\u1ea3 v\u1ec1 d\u1eef li\u1ec7u.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-13-cac-annotation-requestmapping-getmapping-postmapping-dung-d\u1ec3-lam-gi\"><strong>13. C\u00e1c annotation @RequestMapping, @GetMapping, @PostMapping d\u00f9ng \u0111\u1ec3 l\u00e0m g\u00ec?<\/strong><\/h3>\n\n\n\n<p>C\u00e1c annotation n\u00e0y d\u00f9ng \u0111\u1ec3 <strong>\u00e1nh x\u1ea1 (map) c\u00e1c HTTP request t\u1eeb client t\u1edbi c\u00e1c method x\u1eed l\u00fd (handler method) trong Controller<\/strong>.<\/p>\n\n\n\n<p><strong>@RequestMapping<\/strong>: \u0110\u00e2y l\u00e0 annotation t\u1ed5ng qu\u00e1t nh\u1ea5t. N\u00f3 c\u00f3 th\u1ec3 map v\u1edbi b\u1ea5t k\u1ef3 ph\u01b0\u01a1ng th\u1ee9c HTTP n\u00e0o nh\u01b0 GET, POST, PUT, DELETE&#8230; M\u1eb7c \u0111\u1ecbnh n\u1ebfu kh\u00f4ng ch\u1ec9 \u0111\u1ecbnh method, n\u00f3 s\u1ebd ch\u1ea5p nh\u1eadn t\u1ea5t c\u1ea3. ta c\u00f3 th\u1ec3 d\u00f9ng n\u00f3 \u1edf c\u1ea3 c\u1ea5p \u0111\u1ed9 class (\u0111\u1ec3 \u0111\u1ecbnh ngh\u0129a m\u1ed9t \u0111\u01b0\u1eddng d\u1eabn g\u1ed1c cho t\u1ea5t c\u1ea3 c\u00e1c method b\u00ean trong) v\u00e0 c\u1ea5p \u0111\u1ed9 method.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@RestController\n@RequestMapping(\"\/api\/v1\/tasks\") \/\/ \u00c1p d\u1ee5ng cho c\u1ea3 class\npublic class TaskController {\n    \/\/ S\u1ebd map v\u1edbi request t\u1edbi \/api\/v1\/tasks\n    @RequestMapping(method = RequestMethod.GET) \n    public List&lt;Task&gt; getAllTasks() { \/* ... *\/ }\n}<\/code><\/pre>\n\n\n\n<p><strong>@GetMapping<\/strong> v\u00e0 <strong>@PostMapping<\/strong>: \u0110\u00e2y l\u00e0 c\u00e1c annotation r\u00fat g\u1ecdn v\u00e0 chuy\u00ean bi\u1ec7t h\u01a1n cho c\u00e1c ph\u01b0\u01a1ng th\u1ee9c HTTP c\u1ee5 th\u1ec3.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>@GetMapping<\/strong> l\u00e0 vi\u1ebft t\u1eaft c\u1ee7a @RequestMapping(method = RequestMethod.GET). N\u00f3 chuy\u00ean d\u00f9ng \u0111\u1ec3 x\u1eed l\u00fd c\u00e1c request <strong>GET<\/strong>, th\u01b0\u1eddng d\u00f9ng cho c\u00e1c t\u00e1c v\u1ee5 l\u1ea5y d\u1eef li\u1ec7u.<\/li>\n\n\n\n<li><strong>@PostMapping<\/strong> l\u00e0 vi\u1ebft t\u1eaft c\u1ee7a @RequestMapping(method = RequestMethod.POST). N\u00f3 chuy\u00ean d\u00f9ng \u0111\u1ec3 x\u1eed l\u00fd c\u00e1c request <strong>POST<\/strong>, th\u01b0\u1eddng d\u00f9ng cho c\u00e1c t\u00e1c v\u1ee5 t\u1ea1o m\u1edbi d\u1eef li\u1ec7u.<\/li>\n<\/ul>\n\n\n\n<p>Vi\u1ec7c s\u1eed d\u1ee5ng c\u00e1c annotation chuy\u00ean bi\u1ec7t n\u00e0y gi\u00fap code c\u1ee7a m\u00ecnh tr\u1edf n\u00ean r\u00f5 r\u00e0ng, d\u1ec5 \u0111\u1ecdc v\u00e0 ng\u1eafn g\u1ecdn h\u01a1n r\u1ea5t nhi\u1ec1u.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@RestController\n@RequestMapping(\"\/api\/v1\/tasks\")\npublic class TaskController {\n    @GetMapping\n    public List&lt;Task&gt; getAllTasks() { \/* L\u1ea5y danh s\u00e1ch task *\/ }\n\n    @PostMapping\n    public Task createTask(@RequestBody Task task) { \/* T\u1ea1o m\u1edbi task *\/ }\n}<\/code><\/pre>\n\n\n\n<p>Ngo\u00e0i ra c\u00f2n c\u00f3 <strong>@PutMapping, @DeleteMapping, @PatchMapping<\/strong> v\u1edbi c\u00f4ng d\u1ee5ng t\u01b0\u01a1ng t\u1ef1 cho c\u00e1c ph\u01b0\u01a1ng th\u1ee9c HTTP t\u01b0\u01a1ng \u1ee9ng.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-14-pathvariable-va-requestparam-khac-nhau-nh\u01b0-th\u1ebf-nao\"><strong>14. @PathVariable v\u00e0 @RequestParam kh\u00e1c nhau nh\u01b0 th\u1ebf n\u00e0o?<\/strong><\/h3>\n\n\n\n<p>C\u1ea3 hai annotation n\u00e0y \u0111\u1ec1u d\u00f9ng \u0111\u1ec3 l\u1ea5y d\u1eef li\u1ec7u t\u1eeb URL c\u1ee7a request, nh\u01b0ng ch\u00fang l\u1ea5y t\u1eeb hai ph\u1ea7n kh\u00e1c nhau c\u1ee7a URL.<\/p>\n\n\n\n<p><strong>@PathVariable<\/strong> d\u00f9ng \u0111\u1ec3 tr\u00edch xu\u1ea5t gi\u00e1 tr\u1ecb t\u1eeb m\u1ed9t <strong>ph\u1ea7n c\u1ee7a \u0111\u01b0\u1eddng d\u1eabn URL (URI template)<\/strong>. N\u00f3 r\u1ea5t h\u1eefu \u00edch trong RESTful API khi ch\u00fang ta mu\u1ed1n \u0111\u1ecbnh danh m\u1ed9t t\u00e0i nguy\u00ean c\u1ee5 th\u1ec3.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>V\u00ed d\u1ee5 URL:<code> http:\/\/localhost:8080\/users\/123<\/code><\/li>\n\n\n\n<li>Mapping trong code:<\/li>\n\n\n\n<li><\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>@GetMapping(\"\/users\/{id}\")\npublic User getUserById(@PathVariable(\"id\") Long userId) {\n    \/\/ userId s\u1ebd c\u00f3 gi\u00e1 tr\u1ecb l\u00e0 123\n    return userService.findById(userId);\n}<\/code><\/pre>\n\n\n\n<p>\u1ede \u0111\u00e2y, {id} l\u00e0 m\u1ed9t bi\u1ebfn trong \u0111\u01b0\u1eddng d\u1eabn, v\u00e0 @PathVariable s\u1ebd l\u1ea5y gi\u00e1 tr\u1ecb 123 g\u00e1n v\u00e0o bi\u1ebfn userId.<\/p>\n\n\n\n<p><strong>@RequestParam<\/strong> d\u00f9ng \u0111\u1ec3 tr\u00edch xu\u1ea5t gi\u00e1 tr\u1ecb t\u1eeb <strong>query parameter (ph\u1ea7n sau d\u1ea5u ?)<\/strong> c\u1ee7a URL. N\u00f3 th\u01b0\u1eddng \u0111\u01b0\u1ee3c d\u00f9ng cho vi\u1ec7c l\u1ecdc, s\u1eafp x\u1ebfp ho\u1eb7c ph\u00e2n trang.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>V\u00ed d\u1ee5 URL: <code>http:\/\/localhost:8080\/users?page=1&amp;sort=name<\/code><\/li>\n\n\n\n<li>Mapping trong code:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>@GetMapping(\"\/users\")\npublic List&lt;User&gt; getUsers(\n    @RequestParam(value = \"page\", defaultValue = \"0\") int page,\n    @RequestParam(value = \"sort\", required = false) String sort) {\n    \/\/ page s\u1ebd l\u00e0 1, sort s\u1ebd l\u00e0 \"name\"\n    return userService.findAll(page, sort);\n}<\/code><\/pre>\n\n\n\n<p>@RequestParam c\u00f3 c\u00e1c thu\u1ed9c t\u00ednh h\u1eefu \u00edch nh\u01b0 required (m\u1eb7c \u0111\u1ecbnh l\u00e0 true) \u0111\u1ec3 quy \u0111\u1ecbnh parameter c\u00f3 b\u1eaft bu\u1ed9c hay kh\u00f4ng, v\u00e0 defaultValue \u0111\u1ec3 g\u00e1n gi\u00e1 tr\u1ecb m\u1eb7c \u0111\u1ecbnh n\u1ebfu parameter kh\u00f4ng \u0111\u01b0\u1ee3c truy\u1ec1n l\u00ean.<\/p>\n\n\n\n<p><strong>T\u00f3m l\u1ea1i: @PathVariable l\u1ea5y d\u1eef li\u1ec7u t\u1eeb ch\u00ednh \u0111\u01b0\u1eddng d\u1eabn, c\u00f2n @RequestParam l\u1ea5y d\u1eef li\u1ec7u t\u1eeb ph\u1ea7n query string sau d\u1ea5u ?.<\/strong><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-15-trinh-bay-lu\u1ed3ng-x\u1eed-ly-m\u1ed9t-http-request-trong-spring-mvc-t\u1eeb-dispatcherservlet-d\u1ebfn-controller\"><strong>15. Tr\u00ecnh b\u00e0y lu\u1ed3ng x\u1eed l\u00fd m\u1ed9t HTTP Request trong Spring MVC (t\u1eeb DispatcherServlet \u0111\u1ebfn Controller).<\/strong><\/h3>\n\n\n\n<p>Lu\u1ed3ng x\u1eed l\u00fd m\u1ed9t HTTP Request trong Spring MVC c\u00f3 th\u1ec3 \u0111\u01b0\u1ee3c t\u00f3m g\u1ecdn qua c\u00e1c b\u01b0\u1edbc sau, v\u1edbi DispatcherServlet \u0111\u00f3ng vai tr\u00f2 l\u00e0 trung t\u00e2m \u0111i\u1ec1u ph\u1ed1i: <strong>Request -&gt; DispatcherServlet -&gt; HandlerMapping -&gt; Controller -&gt; HandlerAdapter -&gt; ModelAndView\/ResponseData -&gt; ViewResolver\/HttpMessageConverter -&gt; Response<\/strong>.<\/p>\n\n\n\n<p>Gi\u1ea3i th\u00edch c\u1ee5 th\u1ec3:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Request \u0111\u1ebfn DispatcherServlet<\/strong>: Khi m\u1ed9t HTTP Request \u0111\u01b0\u1ee3c g\u1eedi \u0111\u1ebfn \u1ee9ng d\u1ee5ng, DispatcherServlet l\u00e0 th\u00e0nh ph\u1ea7n \u0111\u1ea7u ti\u00ean ti\u1ebfp nh\u1eadn. N\u00f3 ho\u1ea1t \u0111\u1ed9ng nh\u01b0 m\u1ed9t Front Controller, ch\u1ecbu tr\u00e1ch nhi\u1ec7m \u0111i\u1ec1u ph\u1ed1i request n\u00e0y \u0111\u1ebfn c\u00e1c th\u00e0nh ph\u1ea7n x\u1eed l\u00fd ph\u00f9 h\u1ee3p.<\/li>\n\n\n\n<li><strong>DispatcherServlet t\u00ecm Handler (Controller Method)<\/strong>: DispatcherServlet s\u1ebd h\u1ecfi <strong>HandlerMapping<\/strong> \u0111\u1ec3 t\u00ecm ra handler (Controller v\u00e0 method) n\u00e0o s\u1ebd x\u1eed l\u00fd request n\u00e0y. HandlerMapping s\u1ebd d\u1ef1a v\u00e0o th\u00f4ng tin c\u1ee7a request (nh\u01b0 URL, HTTP method) v\u00e0 c\u00e1c annotation (@RequestMapping, @GetMapping&#8230;) trong c\u00e1c Controller \u0111\u1ec3 t\u00ecm ra handler t\u01b0\u01a1ng \u1ee9ng. N\u1ebfu t\u00ecm th\u1ea5y, n\u00f3 s\u1ebd tr\u1ea3 v\u1ec1 m\u1ed9t HandlerExecutionChain (bao g\u1ed3m handler v\u00e0 c\u00e1c interceptor n\u1ebfu c\u00f3) cho DispatcherServlet.<\/li>\n\n\n\n<li><strong>DispatcherServlet g\u1ecdi HandlerAdapter<\/strong>: Sau khi c\u00f3 handler, DispatcherServlet kh\u00f4ng g\u1ecdi tr\u1ef1c ti\u1ebfp method \u0111\u00f3. Thay v\u00e0o \u0111\u00f3, n\u00f3 s\u1ebd chuy\u1ec3n HandlerExecutionChain cho <strong>HandlerAdapter<\/strong>. HandlerAdapter c\u00f3 nhi\u1ec7m v\u1ee5 th\u1ef1c thi method c\u1ee7a handler \u0111\u00f3. L\u00fd do c\u1ea7n HandlerAdapter l\u00e0 \u0111\u1ec3 Spring MVC c\u00f3 th\u1ec3 linh ho\u1ea1t h\u1ed7 tr\u1ee3 nhi\u1ec1u lo\u1ea1i handler kh\u00e1c nhau m\u00e0 kh\u00f4ng c\u1ea7n thay \u0111\u1ed5i DispatcherServlet.<\/li>\n\n\n\n<li><strong>HandlerAdapter th\u1ef1c thi Controller Method<\/strong>: HandlerAdapter s\u1ebd th\u1ef1c thi method trong Controller. T\u1ea1i \u0111\u00e2y, c\u00e1c qu\u00e1 tr\u00ecnh nh\u01b0 binding d\u1eef li\u1ec7u t\u1eeb request v\u00e0o parameter c\u1ee7a method (s\u1eed d\u1ee5ng @PathVariable, @RequestParam, @RequestBody&#8230;) s\u1ebd di\u1ec5n ra. Method n\u00e0y s\u1ebd th\u1ef1c hi\u1ec7n logic business v\u00e0 tr\u1ea3 v\u1ec1 m\u1ed9t \u0111\u1ed1i t\u01b0\u1ee3ng ModelAndView (n\u1ebfu l\u00e0 @Controller) ho\u1eb7c m\u1ed9t \u0111\u1ed1i t\u01b0\u1ee3ng b\u1ea5t k\u1ef3 (n\u1ebfu l\u00e0 @RestController).<\/li>\n\n\n\n<li><strong>X\u1eed l\u00fd k\u1ebft qu\u1ea3 tr\u1ea3 v\u1ec1<\/strong>:\n<ul class=\"wp-block-list\">\n<li>\u0110\u1ed1i v\u1edbi @Controller (tr\u1ea3 v\u1ec1 View): DispatcherServlet nh\u1eadn ModelAndView v\u00e0 chuy\u1ec3n n\u00f3 cho ViewResolver. ViewResolver s\u1ebd ph\u00e2n gi\u1ea3i t\u00ean view logic (v\u00ed d\u1ee5: &#8220;home&#8221;) th\u00e0nh m\u1ed9t \u0111\u1ed1i t\u01b0\u1ee3ng View c\u1ee5 th\u1ec3 (v\u00ed d\u1ee5: home.jsp).<\/li>\n\n\n\n<li>\u0110\u1ed1i v\u1edbi @RestController (tr\u1ea3 v\u1ec1 Data): K\u1ebft qu\u1ea3 tr\u1ea3 v\u1ec1 (v\u00ed d\u1ee5: m\u1ed9t User object) s\u1ebd \u0111\u01b0\u1ee3c x\u1eed l\u00fd b\u1edfi c\u00e1c HttpMessageConverter \u0111\u1ec3 chuy\u1ec3n \u0111\u1ed5i th\u00e0nh \u0111\u1ecbnh d\u1ea1ng d\u1eef li\u1ec7u nh\u01b0 JSON tr\u01b0\u1edbc khi ghi v\u00e0o HttpResponse.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Render View v\u00e0 g\u1eedi Response<\/strong>: Cu\u1ed1i c\u00f9ng, \u0111\u1ed1i t\u01b0\u1ee3ng View s\u1ebd \u0111\u01b0\u1ee3c render (k\u1ebft h\u1ee3p model data v\u00e0o template) \u0111\u1ec3 t\u1ea1o ra HttpResponse, ho\u1eb7c d\u1eef li\u1ec7u JSON\/XML \u0111\u01b0\u1ee3c ghi tr\u1ef1c ti\u1ebfp v\u00e0o HttpResponse v\u00e0 g\u1eedi v\u1ec1 cho client.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-16-controlleradvice-va-exceptionhandler-d\u01b0\u1ee3c-s\u1eed-d\u1ee5ng-d\u1ec3-lam-gi\"><strong>16. @ControllerAdvice v\u00e0 @ExceptionHandler \u0111\u01b0\u1ee3c s\u1eed d\u1ee5ng \u0111\u1ec3 l\u00e0m g\u00ec?\u00a0<\/strong><\/h3>\n\n\n\n<p><strong>@ControllerAdvice<\/strong> v\u00e0 <strong>@ExceptionHandler<\/strong> l\u00e0 hai annotation c\u1ef1c k\u1ef3 m\u1ea1nh m\u1ebd trong Spring, d\u00f9ng \u0111\u1ec3 <strong>x\u1eed l\u00fd exception m\u1ed9t c\u00e1ch t\u1eadp trung v\u00e0 to\u00e0n c\u1ee5c (globally)<\/strong>.<\/p>\n\n\n\n<p><strong>@ExceptionHandler<\/strong>: Annotation n\u00e0y \u0111\u01b0\u1ee3c d\u00f9ng \u0111\u1ec3 \u0111\u00e1nh d\u1ea5u m\u1ed9t method s\u1ebd ch\u1ecbu tr\u00e1ch nhi\u1ec7m x\u1eed l\u00fd m\u1ed9t lo\u1ea1i exception c\u1ee5 th\u1ec3. Ch\u00fang ta c\u00f3 th\u1ec3 \u0111\u1eb7t method n\u00e0y ngay trong m\u1ed9t Controller \u0111\u1ec3 x\u1eed l\u00fd exception ch\u1ec9 ph\u00e1t sinh t\u1eeb Controller \u0111\u00f3.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@RestController<br>public class UserController {<br>    \/\/ ...<br>    @ExceptionHandler(UserNotFoundException.class)<br>    public ResponseEntity&lt;String&gt; handleUserNotFound() {<br>        return new ResponseEntity&lt;&gt;(\"User not found\", HttpStatus.NOT_FOUND);<br>    }<br>}<\/code><\/pre>\n\n\n\n<p>Tuy nhi\u00ean, c\u00e1ch n\u00e0y s\u1ebd d\u1eabn \u0111\u1ebfn vi\u1ec7c l\u1eb7p code n\u1ebfu nhi\u1ec1u Controller c\u1ea7n x\u1eed l\u00fd c\u00f9ng m\u1ed9t lo\u1ea1i exception.<\/p>\n\n\n\n<p><strong>@ControllerAdvice<\/strong>: \u0110\u00e2y l\u00e0 gi\u1ea3i ph\u00e1p cho v\u1ea5n \u0111\u1ec1 tr\u00ean. ControllerAdvice cho ph\u00e9p ta t\u00e1ch bi\u1ec7t logic x\u1eed l\u00fd exception ra m\u1ed9t class ri\u00eang. Class \u0111\u01b0\u1ee3c \u0111\u00e1nh d\u1ea5u @ControllerAdvice s\u1ebd &#8220;l\u1eafng nghe&#8221; c\u00e1c exception x\u1ea3y ra \u1edf t\u1ea5t c\u1ea3 c\u00e1c Controller trong \u1ee9ng d\u1ee5ng. Khi k\u1ebft h\u1ee3p v\u1edbi @ExceptionHandler, n\u00f3 t\u1ea1o th\u00e0nh m\u1ed9t c\u01a1 ch\u1ebf x\u1eed l\u00fd l\u1ed7i t\u1eadp trung, gi\u00fap code s\u1ea1ch s\u1ebd v\u00e0 d\u1ec5 b\u1ea3o tr\u00ec h\u01a1n.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-17-trinh-bay-cach-b\u1ea1n-x\u1eed-ly-exception-trong-m\u1ed9t-rest-api\"><strong>17. Tr\u00ecnh b\u00e0y c\u00e1ch b\u1ea1n x\u1eed l\u00fd Exception trong m\u1ed9t REST API<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>T\u1ea1o c\u00e1c Custom Exception<\/strong>: T\u00f4i s\u1ebd t\u1ea1o c\u00e1c class exception ri\u00eang cho c\u00e1c tr\u01b0\u1eddng h\u1ee3p nghi\u1ec7p v\u1ee5 c\u1ee5 th\u1ec3, v\u00ed d\u1ee5: ResourceNotFoundException, InvalidInputException, DuplicateEmailException. Vi\u1ec7c n\u00e0y gi\u00fap code \u1edf t\u1ea7ng service r\u00f5 r\u00e0ng h\u01a1n, thay v\u00ec ch\u1ec9 throw new Exception(&#8220;&#8230;&#8221;)<\/li>\n<\/ul>\n\n\n\n<p>V\u00ed d\u1ee5:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public class ResourceNotFoundException extends RuntimeException {\n    public ResourceNotFoundException(String message) {\n        super(message);\n    }\n}<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>T\u1ea1o m\u1ed9t Global Exception Handler<\/strong>: ta s\u1ebd t\u1ea1o m\u1ed9t class c\u00f3 t\u00ean nh\u01b0 GlobalExceptionHandler ho\u1eb7c RestResponseEntityExceptionHandler v\u00e0 \u0111\u00e1nh d\u1ea5u n\u00f3 v\u1edbi annotation <strong>@ControllerAdvice<\/strong>.<\/li>\n\n\n\n<li><strong>\u0110\u1ecbnh ngh\u0129a c\u00e1c ph\u01b0\u01a1ng th\u1ee9c x\u1eed l\u00fd Exception<\/strong>: B\u00ean trong class n\u00e0y, ta s\u1ebd vi\u1ebft c\u00e1c method \u0111\u01b0\u1ee3c \u0111\u00e1nh d\u1ea5u v\u1edbi <strong>@ExceptionHandler<\/strong> cho t\u1eebng lo\u1ea1i custom exception.\n<ul class=\"wp-block-list\">\n<li>M\u1ed7i method s\u1ebd nh\u1eadn v\u00e0o exception t\u01b0\u01a1ng \u1ee9ng l\u00e0m tham s\u1ed1.<\/li>\n\n\n\n<li>B\u00ean trong method, ta s\u1ebd t\u1ea1o m\u1ed9t \u0111\u1ed1i t\u01b0\u1ee3ng ErrorResponse (m\u1ed9t POJO class do ta t\u1ef1 \u0111\u1ecbnh ngh\u0129a, th\u01b0\u1eddng ch\u1ee9a c\u00e1c th\u00f4ng tin nh\u01b0 timestamp, status, error, message, path).<\/li>\n\n\n\n<li>Cu\u1ed1i c\u00f9ng, ta tr\u1ea3 v\u1ec1 m\u1ed9t <strong>ResponseEntity&lt;ErrorResponse&gt;<\/strong> v\u1edbi HttpStatus ph\u00f9 h\u1ee3p.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>@ControllerAdvice\npublic class GlobalExceptionHandler {\n\n    @ExceptionHandler(ResourceNotFoundException.class)\n    public ResponseEntity&lt;ErrorResponse&gt; handleResourceNotFoundException(\n        ResourceNotFoundException ex, WebRequest request) {\n\n        ErrorResponse errorDetails = new ErrorResponse(\n            LocalDateTime.now(),\n            HttpStatus.NOT_FOUND.value(),\n            \"Resource Not Found\",\n            ex.getMessage(),\n            request.getDescription(false)\n        );\n        return new ResponseEntity&lt;&gt;(errorDetails, HttpStatus.NOT_FOUND);\n    }\n\n    @ExceptionHandler(MethodArgumentNotValidException.class) \/\/ For @Valid\n    public ResponseEntity&lt;ErrorResponse&gt; handleValidationExceptions(\n        MethodArgumentNotValidException ex, WebRequest request) {\n\n        String errorMessage = ex.getBindingResult().getFieldError().getDefaultMessage();\n        ErrorResponse errorDetails = new ErrorResponse(\n            LocalDateTime.now(),\n            HttpStatus.BAD_REQUEST.value(),\n            \"Validation Failed\",\n            errorMessage,\n            request.getDescription(false)\n        );\n        return new ResponseEntity&lt;&gt;(errorDetails, HttpStatus.BAD_REQUEST);\n    }\n\n    \/\/ M\u1ed9t handler \"b\u1eaft t\u1ea5t c\u1ea3\" cho c\u00e1c exception ch\u01b0a \u0111\u01b0\u1ee3c x\u1eed l\u00fd c\u1ee5 th\u1ec3\n    @ExceptionHandler(Exception.class)\n    public ResponseEntity&lt;ErrorResponse&gt; handleGlobalException(\n        Exception ex, WebRequest request) {\n\n        ErrorResponse errorDetails = new ErrorResponse(\n            LocalDateTime.now(),\n            HttpStatus.INTERNAL_SERVER_ERROR.value(),\n            \"Internal Server Error\",\n            ex.getMessage(),\n            request.getDescription(false)\n        );\n        return new ResponseEntity&lt;&gt;(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);\n    }\n}<\/code><\/pre>\n\n\n\n<p>C\u00e1ch ti\u1ebfp c\u1eadn n\u00e0y gi\u00fap ta chu\u1ea9n h\u00f3a format l\u1ed7i tr\u1ea3 v\u1ec1 cho client, t\u00e1ch bi\u1ec7t logic x\u1eed l\u00fd l\u1ed7i ra kh\u1ecfi logic nghi\u1ec7p v\u1ee5, v\u00e0 l\u00e0m cho code trong Controller g\u1ecdn g\u00e0ng h\u01a1n r\u1ea5t nhi\u1ec1u.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-18-http-status-code-la-gi-neu-y-nghia-c\u1ee7a-cac-code-th\u01b0\u1eddng-dung\"><strong>18. HTTP Status Code l\u00e0 g\u00ec? N\u00eau \u00fd ngh\u0129a c\u1ee7a c\u00e1c code th\u01b0\u1eddng d\u00f9ng.<\/strong><\/h3>\n\n\n\n<p><strong>HTTP Status Code<\/strong> l\u00e0 m\u1ed9t m\u00e3 s\u1ed1 g\u1ed3m 3 ch\u1eef s\u1ed1 \u0111\u01b0\u1ee3c server tr\u1ea3 v\u1ec1 trong HTTP response sau khi nh\u1eadn m\u1ed9t HTTP request t\u1eeb client. M\u00e3 n\u00e0y cung c\u1ea5p cho client th\u00f4ng tin v\u1ec1 k\u1ebft qu\u1ea3 x\u1eed l\u00fd request, v\u00ed d\u1ee5 nh\u01b0 th\u00e0nh c\u00f4ng, th\u1ea5t b\u1ea1i, c\u1ea7n chuy\u1ec3n h\u01b0\u1edbng, hay c\u00f3 l\u1ed7i x\u1ea3y ra.<\/p>\n\n\n\n<p>C\u00e1c status code \u0111\u01b0\u1ee3c chia th\u00e0nh 5 nh\u00f3m d\u1ef1a tr\u00ean ch\u1eef s\u1ed1 \u0111\u1ea7u ti\u00ean:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>1xx<\/strong>: Informational (Th\u00f4ng tin)<\/li>\n\n\n\n<li><strong>2xx<\/strong>: Success (Th\u00e0nh c\u00f4ng)<\/li>\n\n\n\n<li><strong>3xx<\/strong>: Redirection (Chuy\u1ec3n h\u01b0\u1edbng)<\/li>\n\n\n\n<li><strong>4xx<\/strong>: Client Error (L\u1ed7i ph\u00eda Client)<\/li>\n\n\n\n<li><strong>5xx<\/strong>: Server Error (L\u1ed7i ph\u00eda Server)<\/li>\n<\/ul>\n\n\n\n<p>\u00dd ngh\u0129a c\u1ee7a c\u00e1c code ph\u1ed5 bi\u1ebfn m\u00e0 ta hay d\u00f9ng trong ph\u00e1t tri\u1ec3n API l\u00e0:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>200 OK<\/strong>: \u0110\u00e2y l\u00e0 code th\u00e0nh c\u00f4ng ph\u1ed5 bi\u1ebfn nh\u1ea5t. N\u00f3 c\u00f3 ngh\u0129a l\u00e0 request \u0111\u00e3 \u0111\u01b0\u1ee3c <strong>x\u1eed l\u00fd th\u00e0nh c\u00f4ng<\/strong>.<\/li>\n<\/ul>\n\n\n\n<p>V\u00ed d\u1ee5: Client g\u1eedi GET \/users\/123 v\u00e0 server t\u00ecm th\u1ea5y user, tr\u1ea3 v\u1ec1 th\u00f4ng tin user k\u00e8m status 200.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>201 Created<\/strong>: Code n\u00e0y c\u0169ng l\u00e0 th\u00e0nh c\u00f4ng, nh\u01b0ng c\u1ee5 th\u1ec3 h\u01a1n l\u00e0 request \u0111\u00e3 <strong>t\u1ea1o th\u00e0nh c\u00f4ng m\u1ed9t t\u00e0i nguy\u00ean m\u1edbi<\/strong>.<\/li>\n<\/ul>\n\n\n\n<p>V\u00ed d\u1ee5: Client g\u1eedi POST \/users v\u1edbi th\u00f4ng tin user m\u1edbi. Server t\u1ea1o user th\u00e0nh c\u00f4ng trong database v\u00e0 tr\u1ea3 v\u1ec1 status 201. Th\u01b0\u1eddng response s\u1ebd ch\u1ee9a th\u00f4ng tin c\u1ee7a t\u00e0i nguy\u00ean v\u1eeba t\u1ea1o v\u00e0 m\u1ed9t header Location tr\u1ecf \u0111\u1ebfn URL c\u1ee7a t\u00e0i nguy\u00ean \u0111\u00f3.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>400 Bad Request<\/strong>: L\u1ed7i n\u00e0y x\u1ea3y ra khi request c\u1ee7a client <strong>kh\u00f4ng h\u1ee3p l\u1ec7 ho\u1eb7c sai c\u00fa ph\u00e1p<\/strong>. Server kh\u00f4ng th\u1ec3 hi\u1ec3u ho\u1eb7c x\u1eed l\u00fd request.<\/li>\n<\/ul>\n\n\n\n<p>V\u00ed d\u1ee5: Client g\u1eedi POST \/users nh\u01b0ng thi\u1ebfu m\u1ed9t tr\u01b0\u1eddng b\u1eaft bu\u1ed9c trong JSON body, ho\u1eb7c g\u1eedi m\u1ed9t gi\u00e1 tr\u1ecb sai \u0111\u1ecbnh d\u1ea1ng (v\u00ed d\u1ee5: g\u1eedi ch\u1eef cho m\u1ed9t tr\u01b0\u1eddng s\u1ed1).<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>404 Not Found<\/strong>: L\u1ed7i n\u00e0y c\u00f3 ngh\u0129a l\u00e0 server <strong>kh\u00f4ng t\u00ecm th\u1ea5y t\u00e0i nguy\u00ean<\/strong> m\u00e0 client y\u00eau c\u1ea7u. \u0110\u00e2y l\u00e0 m\u1ed9t trong nh\u1eefng l\u1ed7i ph\u1ed5 bi\u1ebfn nh\u1ea5t.<\/li>\n<\/ul>\n\n\n\n<p>V\u00ed d\u1ee5: Client g\u1eedi GET \/users\/999 nh\u01b0ng kh\u00f4ng c\u00f3 user n\u00e0o v\u1edbi ID l\u00e0 999 trong h\u1ec7 th\u1ed1ng.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>500 Internal Server Error<\/strong>: \u0110\u00e2y l\u00e0 m\u1ed9t l\u1ed7i chung chung ph\u00eda server. N\u00f3 ch\u1ec9 ra r\u1eb1ng \u0111\u00e3 c\u00f3 <strong>m\u1ed9t l\u1ed7i b\u1ea5t ng\u1edd x\u1ea3y ra tr\u00ean server<\/strong> khi\u1ebfn n\u00f3 kh\u00f4ng th\u1ec3 ho\u00e0n th\u00e0nh request. L\u1ed7i n\u00e0y kh\u00f4ng ph\u1ea3i do client g\u00e2y ra.<\/li>\n<\/ul>\n\n\n\n<p>V\u00ed d\u1ee5: Trong qu\u00e1 tr\u00ecnh x\u1eed l\u00fd request, code logic g\u1eb7p ph\u1ea3i NullPointerException ho\u1eb7c kh\u00f4ng th\u1ec3 k\u1ebft n\u1ed1i \u0111\u1ebfn database. Client kh\u00f4ng c\u1ea7n bi\u1ebft chi ti\u1ebft l\u1ed7i l\u00e0 g\u00ec, ch\u1ec9 c\u1ea7n bi\u1ebft server \u0111ang g\u1eb7p s\u1ef1 c\u1ed1.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-19-so-sanh-vi\u1ec7c-x\u1eed-ly-b\u1ea5t-d\u1ed3ng-b\u1ed9-trong-spring-mvc-b\u1eb1ng-callable-va-deferredresult\"><strong>19. So s\u00e1nh vi\u1ec7c x\u1eed l\u00fd b\u1ea5t \u0111\u1ed3ng b\u1ed9 trong Spring MVC b\u1eb1ng Callable v\u00e0 DeferredResult.<\/strong><\/h3>\n\n\n\n<p>C\u1ea3 <strong>Callable<\/strong> v\u00e0 <strong>DeferredResult<\/strong> \u0111\u1ec1u l\u00e0 hai c\u01a1 ch\u1ebf m\u00e0 Spring MVC cung c\u1ea5p \u0111\u1ec3 x\u1eed l\u00fd c\u00e1c request m\u1ed9t c\u00e1ch b\u1ea5t \u0111\u1ed3ng b\u1ed9. M\u1ee5c ti\u00eau chung c\u1ee7a ch\u00fang l\u00e0 gi\u1ea3i ph\u00f3ng thread c\u1ee7a web server (v\u00ed d\u1ee5: Tomcat) trong khi ch\u1edd \u0111\u1ee3i m\u1ed9t t\u00e1c v\u1ee5 t\u1ed1n th\u1eddi gian ho\u00e0n th\u00e0nh (nh\u01b0 g\u1ecdi API b\u00ean ngo\u00e0i, truy v\u1ea5n database ph\u1ee9c t\u1ea1p), t\u1eeb \u0111\u00f3 gi\u00fap \u1ee9ng d\u1ee5ng c\u00f3 kh\u1ea3 n\u0103ng ch\u1ecbu t\u1ea3i t\u1ed1t h\u01a1n.<\/p>\n\n\n\n<p>Tuy nhi\u00ean, ch\u00fang kh\u00e1c nhau v\u1ec1 c\u00e1ch th\u1ee9c ho\u1ea1t \u0111\u1ed9ng v\u00e0 ng\u01b0\u1eddi ch\u1ecbu tr\u00e1ch nhi\u1ec7m qu\u1ea3n l\u00fd thread.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Ti\u00eau ch\u00ed<\/strong><\/td><td><strong>Callable&lt;T&gt;<\/strong><\/td><td><strong>DeferredResult&lt;T&gt;<\/strong><\/td><\/tr><tr><td><strong>Qu\u1ea3n l\u00fd Thread<\/strong><\/td><td><strong>Spring qu\u1ea3n l\u00fd ho\u00e0n to\u00e0n<\/strong><\/td><td><strong>L\u1eadp tr\u00ecnh vi\u00ean qu\u1ea3n l\u00fd<\/strong><\/td><\/tr><tr><td><strong>Lu\u1ed3ng ho\u1ea1t \u0111\u1ed9ng<\/strong><\/td><td>Controller tr\u1ea3 v\u1ec1 m\u1ed9t Callable. DispatcherServlet s\u1ebd g\u1ecdi startAsync v\u00e0 chuy\u1ec3n Callable n\u00e0y cho m\u1ed9t TaskExecutor (do Spring qu\u1ea3n l\u00fd) \u0111\u1ec3 th\u1ef1c thi \u1edf m\u1ed9t thread kh\u00e1c. Thread c\u1ee7a Tomcat \u0111\u01b0\u1ee3c gi\u1ea3i ph\u00f3ng ngay l\u1eadp t\u1ee9c. Khi Callable ho\u00e0n th\u00e0nh, Spring s\u1ebd dispatch m\u1ed9t request m\u1edbi \u0111\u1ec3 x\u1eed l\u00fd k\u1ebft qu\u1ea3.<\/td><td>Controller t\u1ea1o m\u1ed9t \u0111\u1ed1i t\u01b0\u1ee3ng DeferredResult v\u00e0 l\u01b0u n\u00f3 \u1edf \u0111\u00e2u \u0111\u00f3 (v\u00ed d\u1ee5: m\u1ed9t h\u00e0ng \u0111\u1ee3i). Sau \u0111\u00f3 tr\u1ea3 v\u1ec1 DeferredResult n\u00e0y. Thread c\u1ee7a Tomcat \u0111\u01b0\u1ee3c gi\u1ea3i ph\u00f3ng. M\u1ed9t thread <strong>do l\u1eadp tr\u00ecnh vi\u00ean t\u1ef1 qu\u1ea3n l\u00fd<\/strong> (v\u00ed d\u1ee5 t\u1eeb m\u1ed9t message queue listener, m\u1ed9t scheduled task) s\u1ebd th\u1ef1c hi\u1ec7n t\u00e1c v\u1ee5 v\u00e0 khi c\u00f3 k\u1ebft qu\u1ea3, n\u00f3 s\u1ebd g\u1ecdi deferredResult.setResult(result). Vi\u1ec7c g\u1ecdi setResult s\u1ebd trigger Spring dispatch m\u1ed9t request m\u1edbi \u0111\u1ec3 x\u1eed l\u00fd k\u1ebft qu\u1ea3.<\/td><\/tr><tr><td><strong>C\u00e1ch s\u1eed d\u1ee5ng<\/strong><\/td><td>\u0110\u01a1n gi\u1ea3n, tr\u1ef1c ti\u1ebfp. Ph\u00f9 h\u1ee3p cho c\u00e1c t\u00e1c v\u1ee5 t\u00ednh to\u00e1n ho\u1eb7c I\/O blocking c\u00f3 th\u1ec3 \u0111\u01b0\u1ee3c g\u00f3i g\u1ecdn trong m\u1ed9t kh\u1ed1i l\u1ec7nh.<\/td><td>Linh ho\u1ea1t h\u01a1n, nh\u01b0ng ph\u1ee9c t\u1ea1p h\u01a1n. Ph\u00f9 h\u1ee3p cho c\u00e1c k\u1ecbch b\u1ea3n d\u1ef1a tr\u00ean s\u1ef1 ki\u1ec7n (event-driven), n\u01a1i m\u00e0 k\u1ebft qu\u1ea3 \u0111\u01b0\u1ee3c t\u1ea1o ra t\u1eeb m\u1ed9t ti\u1ebfn tr\u00ecnh ho\u00e0n to\u00e0n kh\u00e1c, kh\u00f4ng li\u00ean quan tr\u1ef1c ti\u1ebfp \u0111\u1ebfn lu\u1ed3ng request ban \u0111\u1ea7u.<\/td><\/tr><tr><td><strong>V\u00ed d\u1ee5 K\u1ecbch b\u1ea3n<\/strong><\/td><td>G\u1ecdi m\u1ed9t microservice kh\u00e1c v\u00e0 ch\u1edd k\u1ebft qu\u1ea3. &#8211; Th\u1ef1c hi\u1ec7n m\u1ed9t truy v\u1ea5n database ph\u1ee9c t\u1ea1p.<\/td><td>Ch\u1edd m\u1ed9t message t\u1eeb Kafka\/RabbitMQ. &#8211; M\u1ed9t t\u00e1c v\u1ee5 ch\u1ea1y ng\u1ea7m ho\u00e0n th\u00e0nh v\u00e0 \u0111\u1ea9y k\u1ebft qu\u1ea3. &#8211; X\u00e2y d\u1ef1ng \u1ee9ng d\u1ee5ng chat long-polling.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p><strong>T\u00f3m t\u1eaft s\u1ef1 kh\u00e1c bi\u1ec7t c\u1ed1t l\u00f5i:<\/strong><\/p>\n\n\n\n<p>V\u1edbi <strong>Callable<\/strong>, ta n\u00f3i v\u1edbi Spring: <em>&#8220;\u0110\u00e2y l\u00e0 m\u1ed9t kh\u1ed1i c\u00f4ng vi\u1ec7c, h\u00e3y l\u1ea5y n\u00f3 v\u00e0 ch\u1ea1y tr\u00ean m\u1ed9t thread kh\u00e1c do anh qu\u1ea3n l\u00fd. Khi n\u00e0o xong th\u00ec b\u00e1o t\u00f4i.&#8221;<\/em> To\u00e0n b\u1ed9 qu\u00e1 tr\u00ecnh n\u1eb1m trong s\u1ef1 ki\u1ec3m so\u00e1t c\u1ee7a Spring MVC v\u00e0 TaskExecutor c\u1ee7a n\u00f3.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@GetMapping(\"\/callable\")\npublic Callable&lt;String&gt; processCallable() {\n    log.info(\"Controller thread starts\");\n    Callable&lt;String&gt; callable = () -&gt; {\n        Thread.sleep(2000); \/\/ Gi\u1ea3 l\u1eadp t\u00e1c v\u1ee5 d\u00e0i\n        log.info(\"Async task completed\");\n        return \"Callable result\";\n    };\n    log.info(\"Controller thread returns\");\n    return callable;\n}<\/code><\/pre>\n\n\n\n<p>V\u1edbi <strong>DeferredResult<\/strong>, ta n\u00f3i v\u1edbi Spring: <em>&#8220;T\u00f4i s\u1ebd tr\u1ea3 l\u1eddi anh sau. H\u00e3y gi\u1eef request n\u00e0y l\u1ea1i. M\u1ed9t l\u00fac n\u1eefa, m\u1ed9t thread n\u00e0o \u0111\u00f3 \u1edf n\u01a1i kh\u00e1c s\u1ebd cung c\u1ea5p k\u1ebft qu\u1ea3 cho \u0111\u1ed1i t\u01b0\u1ee3ng DeferredResult n\u00e0y.&#8221;<\/em> Em, v\u1edbi t\u01b0 c\u00e1ch l\u00e0 l\u1eadp tr\u00ecnh vi\u00ean, ph\u1ea3i ch\u1ecbu tr\u00e1ch nhi\u1ec7m ho\u00e0n to\u00e0n cho vi\u1ec7c t\u00ednh to\u00e1n v\u00e0 set k\u1ebft qu\u1ea3 t\u1eeb m\u1ed9t thread b\u00ean ngo\u00e0i.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>private final Queue&lt;DeferredResult&lt;String&gt;&gt; resultQueue = new ConcurrentLinkedQueue&lt;&gt;();\n\n@GetMapping(\"\/deferred\")\npublic DeferredResult&lt;String&gt; processDeferred() {\n    log.info(\"Controller thread starts\");\n    DeferredResult&lt;String&gt; deferredResult = new DeferredResult&lt;&gt;(3000L, \"Timeout!\"); \/\/ 3s timeout\n    resultQueue.add(deferredResult);\n    log.info(\"Controller thread returns\");\n    return deferredResult;\n}\n\n\/\/ M\u1ed9t t\u00e1c v\u1ee5 ch\u1ea1y \u0111\u1ecbnh k\u1ef3 ho\u1eb7c m\u1ed9t listener n\u00e0o \u0111\u00f3\n@Scheduled(fixedRate = 2000)\npublic void processQueue() {\n    if (!resultQueue.isEmpty()) {\n        DeferredResult&lt;String&gt; result = resultQueue.poll();\n        result.setResult(\"Deferred result\");\n        log.info(\"Result set for a deferred request\");\n    }\n}<\/code><\/pre>\n\n\n\n<p>L\u1ef1a ch\u1ecdn gi\u1eefa hai c\u01a1 ch\u1ebf n\u00e0y ph\u1ee5 thu\u1ed9c v\u00e0o b\u00e0i to\u00e1n c\u1ee5 th\u1ec3. N\u1ebfu t\u00e1c v\u1ee5 c\u00f3 th\u1ec3 \u0111\u01b0\u1ee3c th\u1ef1c thi ngay l\u1eadp t\u1ee9c trong m\u1ed9t lu\u1ed3ng n\u1ec1n, Callable l\u00e0 l\u1ef1a ch\u1ecdn \u0111\u01a1n gi\u1ea3n v\u00e0 an to\u00e0n h\u01a1n. N\u1ebfu t\u00e1c v\u1ee5 ph\u1ee5 thu\u1ed9c v\u00e0o m\u1ed9t s\u1ef1 ki\u1ec7n b\u00ean ngo\u00e0i v\u00e0 kh\u00f4ng bi\u1ebft khi n\u00e0o s\u1ebd ho\u00e0n th\u00e0nh, DeferredResult mang l\u1ea1i s\u1ef1 linh ho\u1ea1t c\u1ea7n thi\u1ebft.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-cau-h\u1ecfi-ph\u1ecfng-v\u1ea5n-v\u1ec1-spring-data-jpa-amp-database\"><span class=\"ez-toc-section\" id=\"Cau_hoi_phong_van_ve_Spring_Data_JPA_Database\"><\/span><strong>C\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n v\u1ec1 Spring Data JPA &amp; Database<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>C\u00e1c c\u00e2u h\u1ecfi ki\u1ec3m tra k\u1ef9 n\u0103ng l\u00e0m vi\u1ec7c v\u1edbi c\u01a1 s\u1edf d\u1eef li\u1ec7u th\u00f4ng qua Spring Data JPA v\u00e0 Hibernate.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-20-trinh-bay-hi\u1ec3u-bi\u1ebft-c\u1ee7a-b\u1ea1n-v\u1ec1-jparepositor\"><strong>20. Tr\u00ecnh b\u00e0y hi\u1ec3u bi\u1ebft c\u1ee7a b\u1ea1n v\u1ec1 JpaRepositor.<\/strong><\/h3>\n\n\n\n<p><strong>Vai tr\u00f2 &amp; C\u00e1ch ho\u1ea1t \u0111\u1ed9ng<\/strong><\/p>\n\n\n\n<p>JpaRepository l\u00e0 m\u1ed9t interface trong Spring Data JPA. N\u00f3 l\u00e0 m\u1ed9t t\u1ea7ng tr\u1eebu t\u01b0\u1ee3ng c\u1ea5p cao h\u01a1n, k\u1ebf th\u1eeba t\u1eeb PagingAndSortingRepository v\u00e0 CrudRepository. Vai tr\u00f2 ch\u00ednh c\u1ee7a n\u00f3 l\u00e0 gi\u1ea3m thi\u1ec3u \u0111\u00e1ng k\u1ec3 l\u01b0\u1ee3ng code l\u1eb7p \u0111i l\u1eb7p l\u1ea1i (boilerplate code) khi l\u00e0m vi\u1ec7c v\u1edbi c\u00e1c thao t\u00e1c CRUD (Create, Read, Update, Delete) v\u00e0 truy v\u1ea5n d\u1eef li\u1ec7u.<\/p>\n\n\n\n<p>Khi m\u00ecnh t\u1ea1o m\u1ed9t interface v\u00e0 cho n\u00f3 k\u1ebf th\u1eeba JpaRepository, Spring Data JPA s\u1ebd t\u1ef1 \u0111\u1ed9ng cung c\u1ea5p m\u1ed9t implementation cho interface \u0111\u00f3 l\u00fac runtime. M\u00ecnh ch\u1ec9 c\u1ea7n khai b\u00e1o interface l\u00e0 c\u00f3 th\u1ec3 s\u1eed d\u1ee5ng ngay c\u00e1c ph\u01b0\u01a1ng th\u1ee9c c\u00f3 s\u1eb5n m\u00e0 kh\u00f4ng c\u1ea7n vi\u1ebft code tri\u1ec3n khai.<\/p>\n\n\n\n<p><strong>M\u1ed9t s\u1ed1 ph\u01b0\u01a1ng th\u1ee9c c\u01a1 b\u1ea3n m\u00e0 JpaRepository cung c\u1ea5p:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>L\u01b0u v\u00e0 c\u1eadp nh\u1eadt d\u1eef li\u1ec7u:<\/strong>\n<ul class=\"wp-block-list\">\n<li>save(S entity): L\u01b0u m\u1ed9t entity m\u1edbi ho\u1eb7c c\u1eadp nh\u1eadt m\u1ed9t entity \u0111\u00e3 t\u1ed3n t\u1ea1i.<\/li>\n\n\n\n<li>saveAll(Iterable&lt;S&gt; entities): L\u01b0u m\u1ed9t danh s\u00e1ch c\u00e1c entity.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Truy v\u1ea5n d\u1eef li\u1ec7u:<\/strong>\n<ul class=\"wp-block-list\">\n<li>findById(ID id): T\u00ecm m\u1ed9t entity theo kh\u00f3a ch\u00ednh (primary key), tr\u1ea3 v\u1ec1 m\u1ed9t Optional&lt;T&gt;.<\/li>\n\n\n\n<li>findAll(): L\u1ea5y t\u1ea5t c\u1ea3 c\u00e1c entity.<\/li>\n\n\n\n<li>findAllById(Iterable&lt;ID&gt; ids): L\u1ea5y t\u1ea5t c\u1ea3 c\u00e1c entity theo danh s\u00e1ch c\u00e1c kh\u00f3a ch\u00ednh.<\/li>\n\n\n\n<li>existsById(ID id): Ki\u1ec3m tra xem m\u1ed9t entity c\u00f3 t\u1ed3n t\u1ea1i hay kh\u00f4ng.<\/li>\n\n\n\n<li>count(): \u0110\u1ebfm t\u1ed5ng s\u1ed1 entity.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>X\u00f3a d\u1eef li\u1ec7u:<\/strong>\n<ul class=\"wp-block-list\">\n<li>deleteById(ID id): X\u00f3a m\u1ed9t entity theo kh\u00f3a ch\u00ednh.<\/li>\n\n\n\n<li>delete(T entity): X\u00f3a m\u1ed9t entity.<\/li>\n\n\n\n<li>deleteAll(): X\u00f3a t\u1ea5t c\u1ea3 c\u00e1c entity.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p><strong>\u0110i\u1ec3m n\u1ed5i b\u1eadt<\/strong><\/p>\n\n\n\n<p>M\u1ed9t trong nh\u1eefng s\u1ee9c m\u1ea1nh l\u1edbn nh\u1ea5t c\u1ee7a JpaRepository l\u00e0 kh\u1ea3 n\u0103ng <strong>t\u1ef1 \u0111\u1ed9ng sinh c\u00e2u truy v\u1ea5n t\u1eeb t\u00ean ph\u01b0\u01a1ng th\u1ee9c (derived query methods)<\/strong>. V\u00ed d\u1ee5, ta ch\u1ec9 c\u1ea7n \u0111\u1ecbnh ngh\u0129a m\u1ed9t method nh\u01b0 findByEmail(String email) trong repository, Spring Data s\u1ebd t\u1ef1 hi\u1ec3u v\u00e0 t\u1ea1o ra c\u00e2u l\u1ec7nh SELECT * FROM users WHERE email = ? t\u01b0\u01a1ng \u1ee9ng.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-21-s\u1ef1-khac-bi\u1ec7t-gi\u1eefa-save-saveandflush-va-flush-trong-jparepository\"><strong>21. S\u1ef1 kh\u00e1c bi\u1ec7t gi\u1eefa save(), saveAndFlush() v\u00e0 flush() trong JpaRepository?<\/strong><\/h3>\n\n\n\n<p>\u0110\u1ec3 hi\u1ec3u s\u1ef1 kh\u00e1c bi\u1ec7t gi\u1eefa ba ph\u01b0\u01a1ng th\u1ee9c n\u00e0y, tr\u01b0\u1edbc h\u1ebft c\u1ea7n ph\u1ea3i n\u00f3i v\u1ec1 <strong>Persistence Context<\/strong>. Trong Hibernate (implementation m\u1eb7c \u0111\u1ecbnh c\u1ee7a JPA), Persistence Context gi\u1ed1ng nh\u01b0 m\u1ed9t c\u00e1i cache (first-level cache) ch\u1ee9a c\u00e1c entity \u0111ang \u0111\u01b0\u1ee3c qu\u1ea3n l\u00fd trong m\u1ed9t transaction. M\u1ecdi thay \u0111\u1ed5i tr\u00ean entity s\u1ebd \u0111\u01b0\u1ee3c ghi nh\u1eadn v\u00e0o cache n\u00e0y tr\u01b0\u1edbc.<\/p>\n\n\n\n<p>S\u1ef1 kh\u00e1c bi\u1ec7t c\u1ee7a 3 method n\u00e0y n\u1eb1m \u1edf c\u00e1ch ch\u00fang t\u01b0\u01a1ng t\u00e1c v\u1edbi Persistence Context v\u00e0 database:<\/p>\n\n\n\n<p><strong>save(S entity)<\/strong>:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Khi g\u1ecdi save(), entity s\u1ebd \u0111\u01b0\u1ee3c \u0111\u01b0a v\u00e0o Persistence Context. N\u1ebfu l\u00e0 entity m\u1edbi, n\u00f3 s\u1ebd \u0111\u01b0\u1ee3c g\u00e1n m\u1ed9t ID. N\u1ebfu entity \u0111\u00e3 t\u1ed3n t\u1ea1i, tr\u1ea1ng th\u00e1i c\u1ee7a n\u00f3 trong context s\u1ebd \u0111\u01b0\u1ee3c c\u1eadp nh\u1eadt.<\/li>\n\n\n\n<li>Quan tr\u1ecdng l\u00e0 n\u00f3 ch\u01b0a ch\u1eafc \u0111\u00e3 \u0111\u1ea9y ngay l\u1eadp t\u1ee9c c\u00e2u l\u1ec7nh SQL INSERT ho\u1eb7c UPDATE xu\u1ed1ng database. Vi\u1ec7c \u0111\u1ed3ng b\u1ed9 h\u00f3a v\u1edbi database (t\u1ee9c l\u00e0 th\u1ef1c thi c\u00e2u l\u1ec7nh SQL) s\u1ebd ch\u1ec9 x\u1ea3y ra khi transaction \u0111\u01b0\u1ee3c commit, ho\u1eb7c khi Persistence Context \u0111\u1ea7y, ho\u1eb7c khi m\u1ed9t c\u00e2u truy v\u1ea5n c\u1ea7n d\u1eef li\u1ec7u m\u1edbi nh\u1ea5t \u0111\u01b0\u1ee3c th\u1ef1c thi.<\/li>\n<\/ol>\n\n\n\n<p><strong>flush()<\/strong>:<\/p>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li>Method n\u00e0y c\u00f3 nhi\u1ec7m v\u1ee5 \u00e9p bu\u1ed9c (force) Persistence Context ph\u1ea3i \u0111\u1ed3ng b\u1ed9 h\u00f3a tr\u1ea1ng th\u00e1i c\u1ee7a n\u00f3 v\u1edbi database. N\u00f3 s\u1ebd duy\u1ec7t qua t\u1ea5t c\u1ea3 c\u00e1c thay \u0111\u1ed5i \u0111ang &#8220;ch\u1edd&#8221; trong context (dirty checking) v\u00e0 sinh ra c\u00e1c c\u00e2u l\u1ec7nh SQL (INSERT, UPDATE, DELETE) t\u01b0\u01a1ng \u1ee9ng \u0111\u1ec3 th\u1ef1c thi ngay l\u1eadp t\u1ee9c.<\/li>\n\n\n\n<li>Tuy nhi\u00ean, flush() kh\u00f4ng commit transaction. N\u1ebfu sau khi flush() m\u00e0 c\u00f3 l\u1ed7i x\u1ea3y ra, transaction v\u1eabn c\u00f3 th\u1ec3 \u0111\u01b0\u1ee3c rollback, v\u00e0 c\u00e1c thay \u0111\u1ed5i v\u1eeba \u0111\u01b0\u1ee3c \u0111\u1ea9y xu\u1ed1ng database c\u0169ng s\u1ebd \u0111\u01b0\u1ee3c ho\u00e0n t\u00e1c.<\/li>\n<\/ol>\n\n\n\n<p><strong>saveAndFlush(S entity)<\/strong>:<\/p>\n\n\n\n<ol start=\"5\" class=\"wp-block-list\">\n<li>\u0110\u00e2y l\u00e0 s\u1ef1 k\u1ebft h\u1ee3p c\u1ee7a hai ph\u01b0\u01a1ng th\u1ee9c tr\u00ean. V\u1ec1 c\u01a1 b\u1ea3n, n\u00f3 t\u01b0\u01a1ng \u0111\u01b0\u01a1ng v\u1edbi vi\u1ec7c g\u1ecdi save(entity) r\u1ed3i ngay sau \u0111\u00f3 g\u1ecdi flush().<\/li>\n\n\n\n<li>N\u00f3 s\u1ebd l\u01b0u entity v\u00e0o Persistence Context v\u00e0 ngay l\u1eadp t\u1ee9c th\u1ef1c thi c\u00e2u l\u1ec7nh SQL \u0111\u1ec3 \u0111\u1ed3ng b\u1ed9 v\u1edbi database.<\/li>\n\n\n\n<li>Em th\u01b0\u1eddng d\u00f9ng saveAndFlush() trong nh\u1eefng tr\u01b0\u1eddng h\u1ee3p c\u1ea7n l\u1ea5y k\u1ebft qu\u1ea3 v\u1eeba \u0111\u01b0\u1ee3c thay \u0111\u1ed5i \u1edf database \u0111\u1ec3 s\u1eed d\u1ee5ng ngay trong c\u00f9ng m\u1ed9t transaction, v\u00ed d\u1ee5 nh\u01b0 khi c\u1ea7n ID v\u1eeba \u0111\u01b0\u1ee3c sinh ra b\u1edfi database trigger ho\u1eb7c c\u1ea7n d\u1eef li\u1ec7u m\u1edbi \u0111\u1ec3 th\u1ef1c hi\u1ec7n m\u1ed9t c\u00e2u native query kh\u00e1c.<\/li>\n<\/ol>\n\n\n\n<p><strong>T\u00f3m l\u1ea1i:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>save(): Ch\u1ec9 \u0111\u01b0a v\u00e0o cache, ch\u1edd commit \u0111\u1ec3 xu\u1ed1ng DB.<\/li>\n\n\n\n<li>flush(): \u00c9p cache \u0111\u1ed3ng b\u1ed9 xu\u1ed1ng DB, nh\u01b0ng ch\u01b0a commit.<\/li>\n\n\n\n<li>saveAndFlush(): V\u1eeba \u0111\u01b0a v\u00e0o cache, v\u1eeba \u00e9p \u0111\u1ed3ng b\u1ed9 xu\u1ed1ng DB ngay l\u1eadp t\u1ee9c.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-22-gi\u1ea3i-thich-v\u1ec1-transactional-annotation-cac-thu\u1ed9c-tinh-propagation-va-isolation-c\u1ee7a-no-co-y-nghia-gi\"><strong>22. Gi\u1ea3i th\u00edch v\u1ec1 @Transactional annotation. C\u00e1c thu\u1ed9c t\u00ednh propagation v\u00e0 isolation c\u1ee7a n\u00f3 c\u00f3 \u00fd ngh\u0129a g\u00ec?<\/strong><\/h3>\n\n\n\n<p>@Transactional l\u00e0 m\u1ed9t annotation c\u1ee7a Spring Framework, d\u00f9ng \u0111\u1ec3 khai b\u00e1o m\u1ed9t method (ho\u1eb7c t\u1ea5t c\u1ea3 c\u00e1c method trong m\u1ed9t class) \u0111\u01b0\u1ee3c th\u1ef1c thi b\u00ean trong m\u1ed9t bi\u00ean gi\u1edbi giao d\u1ecbch (transactional boundary).<\/p>\n\n\n\n<p>N\u00f3i \u0111\u01a1n gi\u1ea3n, khi m\u1ed9t method \u0111\u01b0\u1ee3c \u0111\u00e1nh d\u1ea5u @Transactional, Spring s\u1ebd lo vi\u1ec7c b\u1eaft \u0111\u1ea7u m\u1ed9t transaction tr\u01b0\u1edbc khi method \u0111\u01b0\u1ee3c g\u1ecdi v\u00e0 commit transaction \u0111\u00f3 sau khi method th\u1ef1c thi th\u00e0nh c\u00f4ng. N\u1ebfu c\u00f3 b\u1ea5t k\u1ef3 RuntimeException n\u00e0o x\u1ea3y ra trong qu\u00e1 tr\u00ecnh th\u1ef1c thi, Spring s\u1ebd t\u1ef1 \u0111\u1ed9ng rollback to\u00e0n b\u1ed9 c\u00e1c thay \u0111\u1ed5i \u0111\u00e3 th\u1ef1c hi\u1ec7n trong transaction \u0111\u00f3. \u0110i\u1ec1u n\u00e0y \u0111\u1ea3m b\u1ea3o t\u00ednh to\u00e0n v\u1eb9n d\u1eef li\u1ec7u (ACID).<\/p>\n\n\n\n<p>@Transactional c\u00f3 hai thu\u1ed9c t\u00ednh r\u1ea5t quan tr\u1ecdng l\u00e0 propagation v\u00e0 isolation.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"h-propagation\"><strong>Propagation<\/strong><\/h4>\n\n\n\n<p>Thu\u1ed9c t\u00ednh n\u00e0y \u0111\u1ecbnh ngh\u0129a <strong>h\u00e0nh vi c\u1ee7a m\u1ed9t method transactional khi n\u00f3 \u0111\u01b0\u1ee3c g\u1ecdi t\u1eeb m\u1ed9t method transactional kh\u00e1c<\/strong>. N\u00f3 quy\u1ebft \u0111\u1ecbnh xem method s\u1ebd tham gia v\u00e0o transaction hi\u1ec7n c\u00f3, hay t\u1ea1o m\u1ed9t transaction m\u1edbi, hay th\u1ef1c thi m\u00e0 kh\u00f4ng c\u1ea7n transaction.<\/p>\n\n\n\n<p>M\u1ed9t s\u1ed1 m\u1ee9c propagation ph\u1ed5 bi\u1ebfn l\u00e0:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>REQUIRED (M\u1eb7c \u0111\u1ecbnh)<\/strong>: N\u1ebfu \u0111\u00e3 c\u00f3 m\u1ed9t transaction \u0111ang ch\u1ea1y, method s\u1ebd tham gia v\u00e0o transaction \u0111\u00f3. N\u1ebfu kh\u00f4ng, n\u00f3 s\u1ebd t\u1ea1o m\u1ed9t transaction m\u1edbi. \u0110\u00e2y l\u00e0 m\u1ee9c ph\u1ed5 bi\u1ebfn nh\u1ea5t.<\/li>\n\n\n\n<li><strong>REQUIRES_NEW<\/strong>: Method s\u1ebd lu\u00f4n b\u1eaft \u0111\u1ea7u m\u1ed9t transaction m\u1edbi ho\u00e0n to\u00e0n, t\u1ea1m d\u1eebng transaction hi\u1ec7n c\u00f3 (n\u1ebfu c\u00f3). M\u1ee9c n\u00e0y h\u1eefu \u00edch khi m\u00ecnh mu\u1ed1n logic c\u1ee7a method ph\u1ea3i \u0111\u01b0\u1ee3c commit ho\u1eb7c rollback m\u1ed9t c\u00e1ch \u0111\u1ed9c l\u1eadp, kh\u00f4ng ph\u1ee5 thu\u1ed9c v\u00e0o transaction b\u00ean ngo\u00e0i. V\u00ed d\u1ee5: ghi log giao d\u1ecbch v\u00e0o DB b\u1ea5t k\u1ec3 giao d\u1ecbch ch\u00ednh c\u00f3 th\u00e0nh c\u00f4ng hay kh\u00f4ng.<\/li>\n\n\n\n<li><strong>SUPPORTS<\/strong>: N\u1ebfu c\u00f3 transaction \u0111ang ch\u1ea1y, method s\u1ebd tham gia. N\u1ebfu kh\u00f4ng, n\u00f3 s\u1ebd th\u1ef1c thi m\u00e0 kh\u00f4ng c\u1ea7n transaction.<\/li>\n\n\n\n<li><strong>NOT_SUPPORTED<\/strong>: T\u1ea1m d\u1eebng transaction hi\u1ec7n c\u00f3 (n\u1ebfu c\u00f3) v\u00e0 th\u1ef1c thi method m\u00e0 kh\u00f4ng c\u00f3 transaction.<\/li>\n\n\n\n<li><strong>NESTED<\/strong>: T\u1ea1o m\u1ed9t transaction l\u1ed3ng b\u00ean trong transaction hi\u1ec7n c\u00f3. Transaction l\u1ed3ng n\u00e0y c\u00f3 th\u1ec3 rollback \u0111\u1ed9c l\u1eadp m\u00e0 kh\u00f4ng \u1ea3nh h\u01b0\u1edfng \u0111\u1ebfn transaction cha. Tuy nhi\u00ean, n\u1ebfu transaction cha rollback th\u00ec n\u00f3 c\u0169ng s\u1ebd b\u1ecb rollback. M\u1ee9c n\u00e0y kh\u00f4ng ph\u1ea3i l\u00fac n\u00e0o c\u0169ng \u0111\u01b0\u1ee3c h\u1ed7 tr\u1ee3 b\u1edfi t\u1ea5t c\u1ea3 c\u00e1c TransactionManager.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"h-isolation-nbsp\"><strong>Isolation&nbsp;<\/strong><\/h4>\n\n\n\n<p>Thu\u1ed9c t\u00ednh n\u00e0y \u0111\u1ecbnh ngh\u0129a <strong>m\u1ee9c \u0111\u1ed9 m\u00e0 m\u1ed9t transaction \u0111\u01b0\u1ee3c c\u00f4 l\u1eadp kh\u1ecfi c\u00e1c transaction kh\u00e1c \u0111ang ch\u1ea1y \u0111\u1ed3ng th\u1eddi<\/strong>. N\u00f3 gi\u1ea3i quy\u1ebft c\u00e1c v\u1ea5n \u0111\u1ec1 v\u1ec1 t\u01b0\u01a1ng tranh d\u1eef li\u1ec7u nh\u01b0 Dirty Read, Non-repeatable Read, v\u00e0 Phantom Read.<\/p>\n\n\n\n<p>C\u00e1c m\u1ee9c isolation theo ti\u00eau chu\u1ea9n SQL l\u00e0:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>READ_UNCOMMITTED<\/strong>: M\u1ee9c th\u1ea5p nh\u1ea5t. M\u1ed9t transaction c\u00f3 th\u1ec3 \u0111\u1ecdc c\u1ea3 nh\u1eefng d\u1eef li\u1ec7u ch\u01b0a \u0111\u01b0\u1ee3c commit b\u1edfi m\u1ed9t transaction kh\u00e1c (Dirty Read). M\u1ee9c n\u00e0y hi\u1ec7u n\u0103ng cao nh\u1ea5t nh\u01b0ng k\u00e9m an to\u00e0n nh\u1ea5t.<\/li>\n\n\n\n<li><strong>READ_COMMITTED<\/strong>: M\u1ed9t transaction ch\u1ec9 c\u00f3 th\u1ec3 \u0111\u1ecdc nh\u1eefng d\u1eef li\u1ec7u \u0111\u00e3 \u0111\u01b0\u1ee3c commit. M\u1ee9c n\u00e0y gi\u1ea3i quy\u1ebft \u0111\u01b0\u1ee3c Dirty Read. \u0110\u00e2y l\u00e0 m\u1ee9c m\u1eb7c \u0111\u1ecbnh c\u1ee7a h\u1ea7u h\u1ebft c\u00e1c CSDL nh\u01b0 PostgreSQL, SQL Server.<\/li>\n\n\n\n<li><strong>REPEATABLE_READ<\/strong>: \u0110\u1ea3m b\u1ea3o r\u1eb1ng n\u1ebfu m\u1ed9t transaction \u0111\u1ecdc m\u1ed9t d\u00f2ng d\u1eef li\u1ec7u nhi\u1ec1u l\u1ea7n, n\u00f3 s\u1ebd lu\u00f4n nh\u1eadn \u0111\u01b0\u1ee3c k\u1ebft qu\u1ea3 gi\u1ed1ng nhau. M\u1ee9c n\u00e0y gi\u1ea3i quy\u1ebft \u0111\u01b0\u1ee3c Dirty Read v\u00e0 Non-repeatable Read. \u0110\u00e2y l\u00e0 m\u1ee9c m\u1eb7c \u0111\u1ecbnh c\u1ee7a MySQL.<\/li>\n\n\n\n<li><strong>SERIALIZABLE<\/strong>: M\u1ee9c cao nh\u1ea5t v\u00e0 an to\u00e0n nh\u1ea5t. C\u00e1c transaction \u0111\u01b0\u1ee3c th\u1ef1c thi tu\u1ea7n t\u1ef1, gi\u1ed1ng nh\u01b0 ch\u00fang ch\u1ea1y n\u1ed1i \u0111u\u00f4i nhau. M\u1ee9c n\u00e0y gi\u1ea3i quy\u1ebft t\u1ea5t c\u1ea3 c\u00e1c v\u1ea5n \u0111\u1ec1 v\u1ec1 t\u01b0\u01a1ng tranh nh\u01b0ng \u1ea3nh h\u01b0\u1edfng nhi\u1ec1u nh\u1ea5t \u0111\u1ebfn hi\u1ec7u n\u0103ng.<\/li>\n<\/ul>\n\n\n\n<p>Vi\u1ec7c l\u1ef1a ch\u1ecdn propagation v\u00e0 isolation ph\u00f9 h\u1ee3p ph\u1ee5 thu\u1ed9c r\u1ea5t nhi\u1ec1u v\u00e0o y\u00eau c\u1ea7u nghi\u1ec7p v\u1ee5 c\u1ee5 th\u1ec3 c\u1ee7a \u1ee9ng d\u1ee5ng.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-23-phan-bi\u1ec7t-gi\u1eefa-fetch-type-lazy-va-eager-\u01b0u-va-nh\u01b0\u1ee3c-di\u1ec3m-c\u1ee7a-t\u1eebng-lo\u1ea1i-la-gi\"><strong>23. Ph\u00e2n bi\u1ec7t gi\u1eefa Fetch Type LAZY v\u00e0 EAGER. \u01afu v\u00e0 nh\u01b0\u1ee3c \u0111i\u1ec3m c\u1ee7a t\u1eebng lo\u1ea1i l\u00e0 g\u00ec?<\/strong><\/h3>\n\n\n\n<p>So s\u00e1nh Fetch Type EAGER v\u00e0 LAZY:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Ti\u00eau ch\u00ed<\/strong><\/td><td><strong>EAGER Loading<\/strong><\/td><td><strong>LAZY Loading<\/strong><\/td><\/tr><tr><td><strong>C\u00e1ch ho\u1ea1t \u0111\u1ed9ng<\/strong><\/td><td>T\u1ea3i entity cha v\u00e0 t\u1ea5t c\u1ea3 entity con li\u00ean quan ngay l\u1eadp t\u1ee9c trong c\u00f9ng m\u1ed9t truy v\u1ea5n.<\/td><td>Ch\u1ec9 t\u1ea3i entity cha. D\u1eef li\u1ec7u c\u1ee7a entity con ch\u1ec9 \u0111\u01b0\u1ee3c t\u1ea3i khi c\u00f3 truy c\u1eadp \u0111\u1ebfn l\u1ea7n \u0111\u1ea7u ti\u00ean.<\/td><\/tr><tr><td><strong>S\u1ed1 l\u01b0\u1ee3ng truy v\u1ea5n<\/strong><\/td><td>Th\u01b0\u1eddng l\u00e0 1 c\u00e2u truy v\u1ea5n l\u1edbn (v\u1edbi JOIN).<\/td><td>Ban \u0111\u1ea7u l\u00e0 1 truy v\u1ea5n, sau \u0111\u00f3 c\u00f3 th\u1ec3 ph\u00e1t sinh th\u00eam c\u00e1c truy v\u1ea5n kh\u00e1c khi c\u1ea7n.<\/td><\/tr><tr><td><strong>\u01afu \u0111i\u1ec3m<\/strong><\/td><td>\u0110\u01a1n gi\u1ea3n, d\u1ec5 s\u1eed d\u1ee5ng. &#8211; Lu\u00f4n c\u00f3 s\u1eb5n d\u1eef li\u1ec7u, kh\u00f4ng lo l\u1ed7i LazyInitializationException.<\/td><td>Hi\u1ec7u n\u0103ng cao: Ch\u1ec9 t\u1ea3i d\u1eef li\u1ec7u khi th\u1ef1c s\u1ef1 c\u1ea7n thi\u1ebft, gi\u00fap truy v\u1ea5n ban \u0111\u1ea7u nhanh h\u01a1n. &#8211; Ti\u1ebft ki\u1ec7m b\u1ed9 nh\u1edb v\u00e0 t\u00e0i nguy\u00ean h\u1ec7 th\u1ed1ng.<\/td><\/tr><tr><td><strong>Nh\u01b0\u1ee3c \u0111i\u1ec3m<\/strong><\/td><td>Hi\u1ec7u n\u0103ng k\u00e9m: D\u1ec5 d\u00e0ng t\u1ea3i th\u1eeba d\u1eef li\u1ec7u kh\u00f4ng c\u1ea7n thi\u1ebft, l\u00e0m ch\u1eadm \u1ee9ng d\u1ee5ng. &#8211; C\u00f3 nguy c\u01a1 g\u00e2y ra v\u1ea5n \u0111\u1ec1 N+1 n\u1ebfu c\u1ea5u h\u00ecnh sai.<\/td><td>&#8211; D\u1ec5 g\u1eb7p l\u1ed7i LazyInitializationException n\u1ebfu truy c\u1eadp d\u1eef li\u1ec7u ngo\u00e0i ph\u1ea1m vi session\/transaction. &#8211; \u0110\u00f2i h\u1ecfi l\u1eadp tr\u00ecnh vi\u00ean ph\u1ea3i qu\u1ea3n l\u00fd c\u1ea9n th\u1eadn h\u01a1n.<\/td><\/tr><tr><td><strong>M\u1eb7c \u0111\u1ecbnh<\/strong><\/td><td>\u00c1p d\u1ee5ng cho c\u00e1c quan h\u1ec7 @ManyToOne v\u00e0 @OneToOne.<\/td><td>\u00c1p d\u1ee5ng cho c\u00e1c quan h\u1ec7 @OneToMany v\u00e0 @ManyToMany.<\/td><\/tr><tr><td><strong>Khi n\u00e0o d\u00f9ng<\/strong><\/td><td>Khi b\u1ea1n ch\u1eafc ch\u1eafn lu\u00f4n lu\u00f4n c\u1ea7n d\u1eef li\u1ec7u con m\u1ed7i khi t\u1ea3i d\u1eef li\u1ec7u cha.<\/td><td>Trong h\u1ea7u h\u1ebft c\u00e1c tr\u01b0\u1eddng h\u1ee3p \u0111\u1ec3 t\u1ed1i \u01b0u hi\u1ec7u n\u0103ng. \u0110\u00e2y \u0111\u01b0\u1ee3c coi l\u00e0 best practice.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p><strong>L\u1eddi khuy\u00ean:<\/strong> C\u00e1ch t\u1ed1t nh\u1ea5t l\u00e0 <strong>lu\u00f4n \u0111\u1eb7t fetch = FetchType.LAZY<\/strong> cho t\u1ea5t c\u1ea3 c\u00e1c m\u1ed1i quan h\u1ec7. Khi c\u1ea7n d\u1eef li\u1ec7u li\u00ean quan, h\u00e3y ch\u1ee7 \u0111\u1ed9ng t\u1ea3i ch\u00fang b\u1eb1ng c\u00e1c k\u1ef9 thu\u1eadt nh\u01b0 <strong>JOIN FETCH<\/strong> ho\u1eb7c <strong>@EntityGraph<\/strong> \u0111\u1ec3 ki\u1ec3m so\u00e1t ho\u00e0n to\u00e0n c\u00e1c c\u00e2u truy v\u1ea5n \u0111\u01b0\u1ee3c sinh ra.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-24-v\u1ea5n-d\u1ec1-n-1-query-la-gi-trinh-bay-m\u1ed9t-s\u1ed1-cach-d\u1ec3-gi\u1ea3i-quy\u1ebft-no-vi-d\u1ee5-join-fetch-entitygraph\"><strong>24. V\u1ea5n \u0111\u1ec1 N+1 query l\u00e0 g\u00ec? Tr\u00ecnh b\u00e0y m\u1ed9t s\u1ed1 c\u00e1ch \u0111\u1ec3 gi\u1ea3i quy\u1ebft n\u00f3 (v\u00ed d\u1ee5: JOIN FETCH, @EntityGraph).<\/strong><\/h3>\n\n\n\n<p><strong>V\u1ea5n \u0111\u1ec1 N+1 query<\/strong> l\u00e0 m\u1ed9t trong nh\u1eefng v\u1ea5n \u0111\u1ec1 hi\u1ec7u n\u0103ng ph\u1ed5 bi\u1ebfn nh\u1ea5t khi l\u00e0m vi\u1ec7c v\u1edbi ORM nh\u01b0 Hibernate. N\u00f3 x\u1ea3y ra khi \u1ee9ng d\u1ee5ng th\u1ef1c thi <strong>1 c\u00e2u query \u0111\u1ec3 l\u1ea5y danh s\u00e1ch c\u00e1c \u0111\u1ed1i t\u01b0\u1ee3ng cha (N \u0111\u1ed1i t\u01b0\u1ee3ng), v\u00e0 sau \u0111\u00f3 l\u1ea1i th\u1ef1c thi th\u00eam N c\u00e2u query n\u1eefa \u0111\u1ec3 l\u1ea5y c\u00e1c \u0111\u1ed1i t\u01b0\u1ee3ng con li\u00ean quan cho m\u1ed7i \u0111\u1ed1i t\u01b0\u1ee3ng cha<\/strong>. T\u1ed5ng c\u1ed9ng, h\u1ec7 th\u1ed1ng ph\u1ea3i th\u1ef1c hi\u1ec7n N+1 c\u00e2u query \u0111\u1ec3 l\u1ea5y \u0111\u1ee7 d\u1eef li\u1ec7u.<\/p>\n\n\n\n<p><strong>V\u00ed d\u1ee5 kinh \u0111i\u1ec3n:<\/strong><\/p>\n\n\n\n<p>Gi\u1ea3 s\u1eed ta c\u00f3 2 entity Author v\u00e0 Book v\u1edbi quan h\u1ec7 @OneToMany t\u1eeb Author \u0111\u1ebfn Book (Fetch Type l\u00e0 LAZY).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Trong Author.java\n@OneToMany(mappedBy = \"author\", fetch = FetchType.LAZY)\nprivate List&lt;Book&gt; books;\nB\u00e2y gi\u1edd, ta mu\u1ed1n l\u1ea5y danh s\u00e1ch t\u1ea5t c\u1ea3 c\u00e1c t\u00e1c gi\u1ea3 v\u00e0 hi\u1ec3n th\u1ecb t\u00ean c\u00f9ng v\u1edbi t\u00ean c\u00e1c cu\u1ed1n s\u00e1ch c\u1ee7a h\u1ecd.\nList&lt;Author&gt; authors = authorRepository.findAll(); \/\/ Query 1: SELECT * FROM authors\nfor (Author author : authors) {\n    System.out.println(\"Author: \" + author.getName());\n    \/\/ D\u00f2ng d\u01b0\u1edbi \u0111\u00e2y trigger N queries c\u00f2n l\u1ea1i\n    System.out.println(\"Books: \" + author.getBooks().stream().map(Book::getTitle).collect(Collectors.joining(\", \"))); \n}<\/code><\/pre>\n\n\n\n<p>authorRepository.findAll() s\u1ebd th\u1ef1c thi <strong>1<\/strong> c\u00e2u query \u0111\u1ea7u ti\u00ean \u0111\u1ec3 l\u1ea5y danh s\u00e1ch t\u1ea5t c\u1ea3 c\u00e1c t\u00e1c gi\u1ea3.<\/p>\n\n\n\n<p>Trong v\u00f2ng l\u1eb7p, khi author.getBooks() \u0111\u01b0\u1ee3c g\u1ecdi l\u1ea7n \u0111\u1ea7u cho m\u1ed7i author, Hibernate s\u1ebd ph\u1ea3i th\u1ef1c thi th\u00eam <strong>1<\/strong> c\u00e2u query n\u1eefa \u0111\u1ec3 l\u1ea5y danh s\u00e1ch s\u00e1ch c\u1ee7a t\u00e1c gi\u1ea3 \u0111\u00f3. N\u1ebfu c\u00f3 N t\u00e1c gi\u1ea3, s\u1ebd c\u00f3 th\u00eam N c\u00e2u query.<\/p>\n\n\n\n<p>=&gt; T\u1ed5ng c\u1ed9ng l\u00e0 <strong>N+1<\/strong> c\u00e2u query, g\u00e2y \u1ea3nh h\u01b0\u1edfng nghi\u00eam tr\u1ecdng \u0111\u1ebfn hi\u1ec7u n\u0103ng.<\/p>\n\n\n\n<p><strong>C\u00e1c c\u00e1ch \u0111\u1ec3 gi\u1ea3i quy\u1ebft v\u1ea5n \u0111\u1ec1 n\u00e0y:<\/strong><\/p>\n\n\n\n<p>T\u00f4i th\u01b0\u1eddng s\u1eed d\u1ee5ng 2 c\u00e1ch ch\u00ednh sau:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>S\u1eed d\u1ee5ng JOIN FETCH trong JPQL:<\/strong><\/li>\n<\/ul>\n\n\n\n<p>\u0110\u00e2y l\u00e0 c\u00e1ch ph\u1ed5 bi\u1ebfn v\u00e0 hi\u1ec7u qu\u1ea3 nh\u1ea5t. ta s\u1ebd vi\u1ebft m\u1ed9t c\u00e2u truy v\u1ea5n t\u00f9y ch\u1ec9nh trong Repository s\u1eed d\u1ee5ng t\u1eeb kh\u00f3a JOIN FETCH \u0111\u1ec3 ch\u1ec9 th\u1ecb cho JPA t\u1ea3i c\u1ea3 entity cha v\u00e0 c\u00e1c entity con li\u00ean quan trong <strong>c\u00f9ng m\u1ed9t c\u00e2u query duy nh\u1ea5t<\/strong>.<\/p>\n\n\n\n<p><strong>V\u00ed d\u1ee5:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Trong AuthorRepository.java\n@Query(\"SELECT a FROM Author a JOIN FETCH a.books\")\nList&lt;Author&gt; findAllWithBooks();<\/code><\/pre>\n\n\n\n<p>Khi g\u1ecdi findAllWithBooks(), JPA s\u1ebd sinh ra m\u1ed9t c\u00e2u SQL duy nh\u1ea5t c\u00f3 LEFT JOIN gi\u1eefa b\u1ea3ng authors v\u00e0 books, gi\u1ea3i quy\u1ebft tri\u1ec7t \u0111\u1ec3 v\u1ea5n \u0111\u1ec1 N+1.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>S\u1eed d\u1ee5ng @EntityGraph:<\/strong><\/li>\n<\/ul>\n\n\n\n<p>\u0110\u00e2y l\u00e0 m\u1ed9t c\u00e1ch ti\u1ebfp c\u1eadn kh\u00e1c, linh ho\u1ea1t h\u01a1n, \u0111\u01b0\u1ee3c gi\u1edbi thi\u1ec7u t\u1eeb JPA 2.1. N\u00f3 cho ph\u00e9p ta \u0111\u1ecbnh ngh\u0129a m\u1ed9t &#8220;bi\u1ec3u \u0111\u1ed3&#8221; c\u00e1c thu\u1ed9c t\u00ednh li\u00ean quan c\u1ea7n \u0111\u01b0\u1ee3c t\u1ea3i c\u00f9ng v\u1edbi entity g\u1ed1c m\u00e0 <strong>kh\u00f4ng c\u1ea7n ph\u1ea3i vi\u1ebft l\u1ea1i c\u00e2u JPQL<\/strong>.<\/p>\n\n\n\n<p>T\u00f4i c\u00f3 th\u1ec3 \u0111\u1ecbnh ngh\u0129a @EntityGraph ngay tr\u00ean entity ho\u1eb7c tr\u1ef1c ti\u1ebfp tr\u00ean ph\u01b0\u01a1ng th\u1ee9c c\u1ee7a Repository.<\/p>\n\n\n\n<p><strong>V\u00ed d\u1ee5:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Trong AuthorRepository.java\n@EntityGraph(attributePaths = {\"books\"})\n@Query(\"SELECT a FROM Author a\") \/\/ Ho\u1eb7c d\u00f9ng method c\u00f3 s\u1eb5n\nList&lt;Author&gt; findAllAuthorsAndTheirBooks();\n\n\/\/ Ho\u1eb7c d\u00f9ng v\u1edbi ph\u01b0\u01a1ng th\u1ee9c c\u00f3 s\u1eb5n c\u1ee7a JpaRepository\n@Override\n@EntityGraph(attributePaths = {\"books\"})\nList&lt;Author&gt; findAll();<\/code><\/pre>\n\n\n\n<p>@EntityGraph r\u1ea5t m\u1ea1nh m\u1ebd khi ta mu\u1ed1n c\u00f3 nhi\u1ec1u &#8220;g\u00f3i&#8221; d\u1eef li\u1ec7u kh\u00e1c nhau cho c\u00f9ng m\u1ed9t entity. V\u00ed d\u1ee5, m\u1ed9t method t\u1ea3i author k\u00e8m s\u00e1ch, m\u1ed9t method kh\u00e1c t\u1ea3i author k\u00e8m nh\u00e0 xu\u1ea5t b\u1ea3n.<\/p>\n\n\n\n<p>C\u1ea3 hai c\u00e1ch tr\u00ean \u0111\u1ec1u nh\u1eb1m m\u1ee5c ti\u00eau chung l\u00e0 t\u1ea3i t\u1ea5t c\u1ea3 d\u1eef li\u1ec7u c\u1ea7n thi\u1ebft trong m\u1ed9t ho\u1eb7c m\u1ed9t s\u1ed1 \u00edt c\u00e2u query thay v\u00ec N+1 c\u00e2u.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-25-so-sanh-gi\u1eefa-cach-ti\u1ebfp-c\u1eadn-optimistic-locking-va-pessimistic-locking-trong-qu\u1ea3n-ly-xung-d\u1ed9t-d\u1eef-li\u1ec7u\"><strong>25. So s\u00e1nh gi\u1eefa c\u00e1ch ti\u1ebfp c\u1eadn Optimistic Locking v\u00e0 Pessimistic Locking trong qu\u1ea3n l\u00fd xung \u0111\u1ed9t d\u1eef li\u1ec7u.<\/strong><\/h3>\n\n\n\n<p><strong>Optimistic Locking<\/strong> v\u00e0 <strong>Pessimistic Locking<\/strong> l\u00e0 hai chi\u1ebfn l\u01b0\u1ee3c ch\u00ednh \u0111\u1ec3 qu\u1ea3n l\u00fd xung \u0111\u1ed9t d\u1eef li\u1ec7u khi c\u00f3 nhi\u1ec1u transaction c\u00f9ng c\u1ed1 g\u1eafng s\u1eeda \u0111\u1ed5i m\u1ed9t b\u1ea3n ghi t\u1ea1i c\u00f9ng m\u1ed9t th\u1eddi \u0111i\u1ec3m.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-pessimistic-locking-nbsp\"><strong>Pessimistic Locking<\/strong>&nbsp;<\/h3>\n\n\n\n<p><strong>T\u01b0 t\u01b0\u1edfng<\/strong>: &#8220;Xung \u0111\u1ed9t r\u1ea5t c\u00f3 th\u1ec3 s\u1ebd x\u1ea3y ra, v\u00ec v\u1eady h\u00e3y kh\u00f3a b\u1ea3n ghi l\u1ea1i ngay khi \u0111\u1ecdc \u0111\u1ec3 kh\u00f4ng ai kh\u00e1c c\u00f3 th\u1ec3 s\u1eeda n\u00f3 cho \u0111\u1ebfn khi t\u00f4i xong vi\u1ec7c.&#8221;<\/p>\n\n\n\n<p><strong>C\u00e1ch ho\u1ea1t \u0111\u1ed9ng<\/strong>: Khi m\u1ed9t transaction \u0111\u1ecdc m\u1ed9t b\u1ea3n ghi v\u00e0 c\u00f3 \u00fd \u0111\u1ecbnh c\u1eadp nh\u1eadt, n\u00f3 s\u1ebd y\u00eau c\u1ea7u database kh\u00f3a b\u1ea3n ghi \u0111\u00f3 l\u1ea1i (SELECT &#8230; FOR UPDATE). C\u00e1c transaction kh\u00e1c mu\u1ed1n \u0111\u1ecdc ho\u1eb7c ghi tr\u00ean b\u1ea3n ghi n\u00e0y s\u1ebd ph\u1ea3i <strong>ch\u1edd<\/strong> cho \u0111\u1ebfn khi transaction \u0111\u1ea7u ti\u00ean commit ho\u1eb7c rollback v\u00e0 nh\u1ea3 kh\u00f3a.<\/p>\n\n\n\n<p><strong>Tri\u1ec3n khai trong JPA<\/strong>: S\u1eed d\u1ee5ng LockModeType.PESSIMISTIC_WRITE ho\u1eb7c PESSIMISTIC_READ.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ S\u1ebd t\u1ea1o c\u00e2u l\u1ec7nh SELECT ... FOR UPDATE\nentityManager.find(Product.class, productId, LockModeType.PESSIMISTIC_WRITE);<\/code><\/pre>\n\n\n\n<p><strong>\u01afu \u0111i\u1ec3m<\/strong>: \u0110\u1ea3m b\u1ea3o t\u00ednh to\u00e0n v\u1eb9n d\u1eef li\u1ec7u r\u1ea5t cao, xung \u0111\u1ed9t \u0111\u01b0\u1ee3c ng\u0103n ch\u1eb7n ngay t\u1eeb \u0111\u1ea7u, kh\u00f4ng c\u00f3 chuy\u1ec7n hai ng\u01b0\u1eddi c\u00f9ng s\u1eeda m\u1ed9t l\u00fac.<\/p>\n\n\n\n<p><strong>Nh\u01b0\u1ee3c \u0111i\u1ec3m<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u1ea2nh h\u01b0\u1edfng nghi\u00eam tr\u1ecdng \u0111\u1ebfn hi\u1ec7u n\u0103ng v\u00e0 kh\u1ea3 n\u0103ng m\u1edf r\u1ed9ng<\/strong>: Vi\u1ec7c gi\u1eef kh\u00f3a trong th\u1eddi gian d\u00e0i c\u00f3 th\u1ec3 g\u00e2y ra deadlock v\u00e0 l\u00e0m gi\u1ea3m th\u00f4ng l\u01b0\u1ee3ng (throughput) c\u1ee7a h\u1ec7 th\u1ed1ng v\u00ec c\u00e1c transaction kh\u00e1c ph\u1ea3i ch\u1edd \u0111\u1ee3i.<\/li>\n\n\n\n<li>Ph\u1ee5 thu\u1ed9c nhi\u1ec1u v\u00e0o c\u01a1 ch\u1ebf kh\u00f3a c\u1ee7a database.<\/li>\n<\/ul>\n\n\n\n<p><strong>Khi n\u00e0o d\u00f9ng<\/strong>: Ph\u00f9 h\u1ee3p v\u1edbi c\u00e1c h\u1ec7 th\u1ed1ng c\u00f3 \u0111\u1ed9 t\u01b0\u01a1ng tranh cao tr\u00ean c\u00f9ng m\u1ed9t d\u1eef li\u1ec7u, n\u01a1i m\u00e0 xung \u0111\u1ed9t x\u1ea3y ra th\u01b0\u1eddng xuy\u00ean v\u00e0 vi\u1ec7c \u0111\u1ea3m b\u1ea3o to\u00e0n v\u1eb9n d\u1eef li\u1ec7u l\u00e0 tuy\u1ec7t \u0111\u1ed1i quan tr\u1ecdng (v\u00ed d\u1ee5: h\u1ec7 th\u1ed1ng t\u00e0i ch\u00ednh, ng\u00e2n h\u00e0ng).<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"h-optimistic-locking\"><strong>Optimistic Locking<\/strong><\/h4>\n\n\n\n<p><strong>T\u01b0 t\u01b0\u1edfng<\/strong>: &#8220;Xung \u0111\u1ed9t hi\u1ebfm khi x\u1ea3y ra, c\u1ee9 \u0111\u1ec3 m\u1ecdi ng\u01b0\u1eddi c\u00f9ng \u0111\u1ecdc v\u00e0 s\u1eeda. Tr\u01b0\u1edbc khi commit, t\u00f4i s\u1ebd ki\u1ec3m tra xem c\u00f3 ai \u0111\u00e3 s\u1eeda b\u1ea3n ghi n\u00e0y trong l\u00fac t\u00f4i \u0111ang l\u00e0m vi\u1ec7c hay kh\u00f4ng. N\u1ebfu c\u00f3, t\u00f4i s\u1ebd b\u00e1o l\u1ed7i.&#8221;<\/p>\n\n\n\n<p><strong>C\u00e1ch ho\u1ea1t \u0111\u1ed9ng<\/strong>: Kh\u00f4ng c\u00f3 kh\u00f3a n\u00e0o \u0111\u01b0\u1ee3c \u0111\u1eb7t \u1edf database. Thay v\u00e0o \u0111\u00f3, m\u1ed7i b\u1ea3n ghi s\u1ebd c\u00f3 th\u00eam m\u1ed9t c\u1ed9t phi\u00ean b\u1ea3n (version), th\u01b0\u1eddng l\u00e0 m\u1ed9t s\u1ed1 nguy\u00ean ho\u1eb7c timestamp.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Khi transaction 1 \u0111\u1ecdc b\u1ea3n ghi, n\u00f3 c\u0169ng \u0111\u1ecdc lu\u00f4n gi\u00e1 tr\u1ecb version (v\u00ed d\u1ee5: version = 1).<\/li>\n\n\n\n<li>Khi transaction 1 mu\u1ed1n c\u1eadp nh\u1eadt, n\u00f3 s\u1ebd g\u1eedi c\u00e2u l\u1ec7nh UPDATE k\u00e8m \u0111i\u1ec1u ki\u1ec7n WHERE id = ? AND version = 1.<\/li>\n\n\n\n<li>N\u1ebfu kh\u00f4ng c\u00f3 ai thay \u0111\u1ed5i b\u1ea3n ghi, \u0111i\u1ec1u ki\u1ec7n version = 1 \u0111\u00fang, update th\u00e0nh c\u00f4ng v\u00e0 gi\u00e1 tr\u1ecb version \u0111\u01b0\u1ee3c t\u0103ng l\u00ean 2.<\/li>\n\n\n\n<li>N\u1ebfu transaction 2 \u0111\u00e3 v\u00e0o c\u1eadp nh\u1eadt b\u1ea3n ghi n\u00e0y tr\u01b0\u1edbc v\u00e0 commit (l\u00e0m version t\u0103ng l\u00ean 2), th\u00ec c\u00e2u l\u1ec7nh UPDATE c\u1ee7a transaction 1 s\u1ebd th\u1ea5t b\u1ea1i (v\u00ec version = 1 kh\u00f4ng c\u00f2n \u0111\u00fang).<\/li>\n\n\n\n<li>Khi \u0111\u00f3, JPA s\u1ebd n\u00e9m ra m\u1ed9t OptimisticLockException, v\u00e0 \u1ee9ng d\u1ee5ng ph\u1ea3i x\u1eed l\u00fd l\u1ed7i n\u00e0y (v\u00ed d\u1ee5: th\u00f4ng b\u00e1o cho ng\u01b0\u1eddi d\u00f9ng, t\u1ea3i l\u1ea1i d\u1eef li\u1ec7u v\u00e0 th\u1eed l\u1ea1i).<\/li>\n<\/ul>\n\n\n\n<p><strong>Tri\u1ec3n khai trong JPA<\/strong>: S\u1eed d\u1ee5ng annotation <strong>@Version<\/strong> tr\u00ean m\u1ed9t thu\u1ed9c t\u00ednh c\u1ee7a entity.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@Entity\npublic class Product {\n    \/\/ ...\n    @Version\n    private Integer version;\n}<\/code><\/pre>\n\n\n\n<p><strong>\u01afu \u0111i\u1ec3m<\/strong>: Hi\u1ec7u n\u0103ng v\u00e0 kh\u1ea3 n\u0103ng m\u1edf r\u1ed9ng cao. Kh\u00f4ng c\u00f3 kh\u00f3a \u1edf database, kh\u00f4ng c\u00f3 blocking, cho ph\u00e9p th\u00f4ng l\u01b0\u1ee3ng cao h\u01a1n.<\/p>\n\n\n\n<p><strong>Nh\u01b0\u1ee3c \u0111i\u1ec3m<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Ph\u1ea3i x\u1eed l\u00fd xung \u0111\u1ed9t \u1edf t\u1ea7ng \u1ee9ng d\u1ee5ng: C\u1ea7n ph\u1ea3i c\u00f3 code \u0111\u1ec3 b\u1eaft v\u00e0 x\u1eed l\u00fd OptimisticLockException.<\/li>\n\n\n\n<li>N\u1ebfu xung \u0111\u1ed9t x\u1ea3y ra th\u01b0\u1eddng xuy\u00ean, vi\u1ec7c ph\u1ea3i th\u1eed l\u1ea1i li\u00ean t\u1ee5c c\u00f3 th\u1ec3 l\u00e0m gi\u1ea3m hi\u1ec7u n\u0103ng.<\/li>\n<\/ul>\n\n\n\n<p><strong>Khi n\u00e0o d\u00f9ng<\/strong>: Ph\u00f9 h\u1ee3p v\u1edbi h\u1ea7u h\u1ebft c\u00e1c \u1ee9ng d\u1ee5ng web th\u00f4ng th\u01b0\u1eddng, n\u01a1i d\u1eef li\u1ec7u ch\u1ee7 y\u1ebfu \u0111\u01b0\u1ee3c \u0111\u1ecdc v\u00e0 xung \u0111\u1ed9t ghi x\u1ea3y ra kh\u00f4ng th\u01b0\u1eddng xuy\u00ean.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-26-khi-nao-b\u1ea1n-s\u1ebd-ch\u1ecdn-s\u1eed-d\u1ee5ng-jdbctemplate-thay-vi-spring-data-jpa\"><strong>26. Khi n\u00e0o b\u1ea1n s\u1ebd ch\u1ecdn s\u1eed d\u1ee5ng JdbcTemplate thay v\u00ec Spring Data JPA?<\/strong><\/h3>\n\n\n\n<p>T\u00f4i xem Spring Data JPA l\u00e0 c\u00f4ng c\u1ee5 m\u1eb7c \u0111\u1ecbnh cho c\u00e1c l\u1edbp truy c\u1eadp d\u1eef li\u1ec7u th\u00f4ng th\u01b0\u1eddng. Nh\u01b0ng khi <strong>\u0111\u1ed1i m\u1eb7t v\u1edbi c\u00e1c b\u00e0i to\u00e1n \u0111\u00f2i h\u1ecfi s\u1ef1 ki\u1ec3m so\u00e1t truy v\u1ea5n \u1edf m\u1ee9c \u0111\u1ed9 th\u1ea5p, hi\u1ec7u n\u0103ng t\u1ed1i \u0111a cho t\u00e1c v\u1ee5 batch, ho\u1eb7c s\u1ef1 linh ho\u1ea1t c\u1ee7a SQL<\/strong>, JdbcTemplate l\u00e0 m\u1ed9t c\u00f4ng c\u1ee5 b\u1ed5 sung m\u1ea1nh m\u1ebd v\u00e0 l\u00e0 l\u1ef1a ch\u1ecdn ph\u00f9 h\u1ee3p h\u01a1n.<\/p>\n\n\n\n<p>T\u00f4i s\u1ebd c\u00e2n nh\u1eafc d\u00f9ng JdbcTemplate trong c\u00e1c tr\u01b0\u1eddng h\u1ee3p sau:<\/p>\n\n\n\n<p><strong>Khi c\u1ea7n th\u1ef1c thi c\u00e1c c\u00e2u SQL ph\u1ee9c t\u1ea1p ho\u1eb7c Native Query:<\/strong><\/p>\n\n\n\n<p>\u0110\u00f4i khi c\u00f3 nh\u1eefng c\u00e2u truy v\u1ea5n ph\u1ee9c t\u1ea1p s\u1eed d\u1ee5ng c\u00e1c t\u00ednh n\u0103ng \u0111\u1eb7c th\u00f9 c\u1ee7a m\u1ed9t h\u1ec7 qu\u1ea3n tr\u1ecb CSDL (v\u00ed d\u1ee5: CONNECT BY trong Oracle, c\u00e1c h\u00e0m window function ph\u1ee9c t\u1ea1p, Common Table Expressions &#8211; CTE) m\u00e0 vi\u1ec7c bi\u1ec3u di\u1ec5n ch\u00fang b\u1eb1ng JPQL ho\u1eb7c Criteria API l\u00e0 r\u1ea5t kh\u00f3 kh\u0103n, th\u1eadm ch\u00ed l\u00e0 kh\u00f4ng th\u1ec3. JdbcTemplate cho ph\u00e9p ta vi\u1ebft v\u00e0 th\u1ef1c thi c\u00e1c c\u00e2u native SQL m\u1ed9t c\u00e1ch tr\u1ef1c ti\u1ebfp v\u00e0 d\u1ec5 d\u00e0ng.<\/p>\n\n\n\n<p><strong>Khi c\u1ea7n hi\u1ec7u n\u0103ng t\u1ed1i \u0111a cho c\u00e1c t\u00e1c v\u1ee5 Bulk Operations:<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u0110\u1ed1i v\u1edbi c\u00e1c t\u00e1c v\u1ee5 c\u1eadp nh\u1eadt ho\u1eb7c x\u00f3a h\u00e0ng lo\u1ea1t (bulk update\/delete) tr\u00ean h\u00e0ng ngh\u00ecn, h\u00e0ng tri\u1ec7u b\u1ea3n ghi, vi\u1ec7c s\u1eed d\u1ee5ng JPA c\u00f3 th\u1ec3 kh\u00f4ng hi\u1ec7u qu\u1ea3. JPA s\u1ebd ph\u1ea3i t\u1ea3i c\u00e1c entity v\u00e0o Persistence Context, th\u1ef1c hi\u1ec7n dirty checking r\u1ed3i m\u1edbi sinh c\u00e2u l\u1ec7nh UPDATE.<\/li>\n\n\n\n<li>JdbcTemplate cho ph\u00e9p th\u1ef1c thi c\u00e1c c\u00e2u UPDATE ho\u1eb7c DELETE tr\u1ef1c ti\u1ebfp tr\u00ean database, b\u1ecf qua to\u00e0n b\u1ed9 overhead c\u1ee7a ORM, mang l\u1ea1i hi\u1ec7u n\u0103ng cao h\u01a1n \u0111\u00e1ng k\u1ec3. Ch\u1ee9c n\u0103ng batchUpdate() c\u1ee7a JdbcTemplate c\u0169ng \u0111\u01b0\u1ee3c t\u1ed1i \u01b0u r\u1ea5t t\u1ed1t cho c\u00e1c k\u1ecbch b\u1ea3n n\u00e0y.<\/li>\n<\/ol>\n\n\n\n<p><strong>Khi l\u00e0m vi\u1ec7c v\u1edbi c\u00e1c Stored Procedures v\u00e0 Functions:<\/strong><\/p>\n\n\n\n<p>M\u1eb7c d\u00f9 JPA c\u00f3 h\u1ed7 tr\u1ee3 g\u1ecdi stored procedures, nh\u01b0ng c\u00fa ph\u00e1p c\u00f3 th\u1ec3 h\u01a1i r\u01b0\u1eddm r\u00e0. JdbcTemplate (c\u00f9ng v\u1edbi SimpleJdbcCall) cung c\u1ea5p m\u1ed9t API tr\u1ef1c quan v\u00e0 m\u1ea1nh m\u1ebd h\u01a1n \u0111\u1ec3 l\u00e0m vi\u1ec7c v\u1edbi stored procedures, bao g\u1ed3m vi\u1ec7c x\u1eed l\u00fd c\u00e1c tham s\u1ed1 IN, OUT v\u00e0 c\u00e1c t\u1eadp k\u1ebft qu\u1ea3 (ResultSets).<\/p>\n\n\n\n<p><strong>Khi x\u00e2y d\u1ef1ng c\u00e1c b\u00e1o c\u00e1o (Reporting) ph\u1ee9c t\u1ea1p:<\/strong><\/p>\n\n\n\n<p>C\u00e1c truy v\u1ea5n \u0111\u1ec3 t\u1ea1o b\u00e1o c\u00e1o th\u01b0\u1eddng join r\u1ea5t nhi\u1ec1u b\u1ea3ng, t\u00ednh to\u00e1n t\u1ed5ng h\u1ee3p tr\u00ean nhi\u1ec1u c\u1ed9t v\u00e0 kh\u00f4ng nh\u1ea5t thi\u1ebft ph\u1ea3i map k\u1ebft qu\u1ea3 tr\u1ea3 v\u1ec1 th\u00e0nh m\u1ed9t Entity c\u1ee5 th\u1ec3. Vi\u1ec7c d\u00f9ng JdbcTemplate v\u00e0 map k\u1ebft qu\u1ea3 v\u00e0o c\u00e1c DTO (Data Transfer Object) \u0111\u01a1n gi\u1ea3n th\u00f4ng qua RowMapper th\u01b0\u1eddng linh ho\u1ea1t v\u00e0 hi\u1ec7u qu\u1ea3 h\u01a1n l\u00e0 c\u1ed1 g\u1eafng &#8220;\u00e9p&#8221; k\u1ebft qu\u1ea3 v\u00e0o c\u1ea5u tr\u00fac Entity c\u1ee7a JPA.<\/p>\n\n\n\n<p><strong>Khi l\u00e0m vi\u1ec7c v\u1edbi Legacy Database:<\/strong><\/p>\n\n\n\n<p>Trong tr\u01b0\u1eddng h\u1ee3p ph\u1ea3i t\u00edch h\u1ee3p v\u1edbi m\u1ed9t schema database c\u0169 kh\u00f4ng \u0111\u01b0\u1ee3c thi\u1ebft k\u1ebf theo c\u00e1c quy t\u1eafc c\u1ee7a ORM (v\u00ed d\u1ee5: kh\u00f4ng c\u00f3 kh\u00f3a ch\u00ednh, t\u00ean c\u1ed9t v\u00e0 b\u1ea3ng kh\u00f4ng nh\u1ea5t qu\u00e1n), vi\u1ec7c \u00e1nh x\u1ea1 th\u00e0nh c\u00e1c Entity c\u00f3 th\u1ec3 r\u1ea5t v\u1ea5t v\u1ea3. JdbcTemplate cho ph\u00e9p t\u01b0\u01a1ng t\u00e1c v\u1edbi database m\u00e0 kh\u00f4ng c\u1ea7n \u0111\u1ebfn t\u1ea7ng \u00e1nh x\u1ea1 n\u00e0y.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-cau-h\u1ecfi-ph\u1ecfng-v\u1ea5n-v\u1ec1-spring-security\"><span class=\"ez-toc-section\" id=\"Cau_hoi_phong_van_ve_Spring_Security\"><\/span><strong>C\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n v\u1ec1 Spring Security<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Ki\u1ec3m tra ki\u1ebfn th\u1ee9c v\u1ec1 b\u1ea3o m\u1eadt \u1ee9ng d\u1ee5ng, m\u1ed9t ph\u1ea7n kh\u00f4ng th\u1ec3 thi\u1ebfu trong c\u00e1c h\u1ec7 th\u1ed1ng th\u1ef1c t\u1ebf.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-27-authentication-xac-th\u1ef1c-va-authorization-\u1ee7y-quy\u1ec1n-khac-nhau-nh\u01b0-th\u1ebf-nao\"><strong>27. Authentication (X\u00e1c th\u1ef1c) v\u00e0 Authorization (\u1ee6y quy\u1ec1n) kh\u00e1c nhau nh\u01b0 th\u1ebf n\u00e0o?<\/strong><\/h3>\n\n\n\n<p>C\u00f3 th\u1ec3 d\u00f9ng m\u1ed9t v\u00ed d\u1ee5 \u0111\u01a1n gi\u1ea3n \u0111\u1ec3 ph\u00e2n bi\u1ec7t nh\u01b0 sau. H\u00e3y t\u01b0\u1edfng t\u01b0\u1ee3ng m\u1ed9t nh\u00e2n vi\u00ean \u0111\u1ebfn m\u1ed9t t\u00f2a nh\u00e0 v\u0103n ph\u00f2ng:<\/p>\n\n\n\n<p><strong>Authentication (X\u00e1c th\u1ef1c)&nbsp;<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u0110\u00e2y l\u00e0 qu\u00e1 tr\u00ecnh <strong>ch\u1ee9ng minh danh t\u00ednh<\/strong>. Khi \u0111\u1ebfn c\u1eeda, nh\u00e2n vi\u00ean qu\u1eb9t th\u1ebb nh\u00e2n vi\u00ean. H\u1ec7 th\u1ed1ng s\u1ebd ki\u1ec3m tra xem th\u1ebb n\u00e0y c\u00f3 h\u1ee3p l\u1ec7 v\u00e0 thu\u1ed9c v\u1ec1 ai trong h\u1ec7 th\u1ed1ng hay kh\u00f4ng.<\/li>\n\n\n\n<li>Trong \u1ee9ng d\u1ee5ng web, \u0111\u00e2y l\u00e0 l\u00fac ng\u01b0\u1eddi d\u00f9ng cung c\u1ea5p <strong>username v\u00e0 password<\/strong>. H\u1ec7 th\u1ed1ng s\u1ebd ki\u1ec3m tra xem th\u00f4ng tin n\u00e0y c\u00f3 kh\u1edbp v\u1edbi d\u1eef li\u1ec7u trong database hay kh\u00f4ng. N\u1ebfu kh\u1edbp, qu\u00e1 tr\u00ecnh x\u00e1c th\u1ef1c th\u00e0nh c\u00f4ng.<\/li>\n<\/ol>\n\n\n\n<p><strong>Authorization (\u1ee6y quy\u1ec1n)<\/strong><\/p>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li>\u0110\u00e2y l\u00e0 qu\u00e1 tr\u00ecnh <strong>ki\u1ec3m tra quy\u1ec1n h\u1ea1n<\/strong> sau khi danh t\u00ednh \u0111\u00e3 \u0111\u01b0\u1ee3c x\u00e1c th\u1ef1c. Sau khi qu\u1eb9t th\u1ebb th\u00e0nh c\u00f4ng (\u0111\u00e3 x\u00e1c th\u1ef1c), h\u1ec7 th\u1ed1ng s\u1ebd ki\u1ec3m tra xem nh\u00e2n vi\u00ean c\u00f3 \u0111\u01b0\u1ee3c ph\u00e9p v\u00e0o m\u1ed9t ph\u00f2ng ban c\u1ee5 th\u1ec3 (v\u00ed d\u1ee5: ph\u00f2ng K\u1ebf to\u00e1n) hay kh\u00f4ng. M\u1ed9t nh\u00e2n vi\u00ean IT c\u00f3 th\u1ec3 v\u00e0o \u0111\u01b0\u1ee3c ph\u00f2ng server, nh\u01b0ng nh\u00e2n vi\u00ean kinh doanh th\u00ec kh\u00f4ng.<\/li>\n\n\n\n<li>Trong \u1ee9ng d\u1ee5ng, sau khi \u0111\u0103ng nh\u1eadp th\u00e0nh c\u00f4ng, h\u1ec7 th\u1ed1ng s\u1ebd ki\u1ec3m tra xem ng\u01b0\u1eddi d\u00f9ng \u0111\u00f3 c\u00f3 <strong>vai tr\u00f2 (role)<\/strong> ho\u1eb7c <strong>quy\u1ec1n (permission)<\/strong> \u0111\u1ec3 truy c\u1eadp m\u1ed9t API hay th\u1ef1c hi\u1ec7n m\u1ed9t h\u00e0nh \u0111\u1ed9ng c\u1ee5 th\u1ec3 hay kh\u00f4ng. V\u00ed d\u1ee5, m\u1ed9t user c\u00f3 vai tr\u00f2 ADMIN m\u1edbi \u0111\u01b0\u1ee3c ph\u00e9p truy c\u1eadp trang qu\u1ea3n tr\u1ecb, c\u00f2n user c\u00f3 vai tr\u00f2 MEMBER th\u00ec kh\u00f4ng.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-28-gi\u1ea3i-thich-ki\u1ebfn-truc-c\u01a1-b\u1ea3n-c\u1ee7a-spring-security-filterchain-authenticationmanager-securitycontextholder\"><strong>28. Gi\u1ea3i th\u00edch ki\u1ebfn tr\u00fac c\u01a1 b\u1ea3n c\u1ee7a Spring Security (FilterChain, AuthenticationManager, SecurityContextHolder).<\/strong><\/h3>\n\n\n\n<p>Ki\u1ebfn tr\u00fac c\u1ee7a Spring Security d\u1ef1a tr\u00ean m\u1ed9t chu\u1ed7i c\u00e1c Servlet Filter, ho\u1ea1t \u0111\u1ed9ng nh\u01b0 nh\u1eefng &#8220;ch\u1ed1t ch\u1eb7n&#8221; an ninh cho m\u1ecdi request g\u1eedi \u0111\u1ebfn \u1ee9ng d\u1ee5ng.&nbsp;<\/p>\n\n\n\n<p>3 th\u00e0nh ph\u1ea7n c\u1ed1t l\u00f5i l\u00e0:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>SecurityFilterChain (hay FilterChainProxy)<\/strong>:<\/li>\n<\/ul>\n\n\n\n<p>\u0110\u00e2y l\u00e0 c\u1eeda ng\u00f5 ch\u00ednh c\u1ee7a Spring Security. Khi m\u1ed9t request \u0111\u1ebfn, thay v\u00ec \u0111i th\u1eb3ng t\u1edbi DispatcherServlet, n\u00f3 s\u1ebd \u0111\u01b0\u1ee3c ch\u1eb7n l\u1ea1i b\u1edfi FilterChainProxy.<\/p>\n\n\n\n<p>FilterChainProxy s\u1ebd \u1ee7y quy\u1ec1n cho m\u1ed9t chu\u1ed7i c\u00e1c filter (SecurityFilterChain) do Spring Security qu\u1ea3n l\u00fd. M\u1ed7i filter trong chu\u1ed7i n\u00e0y c\u00f3 m\u1ed9t nhi\u1ec7m v\u1ee5 ri\u00eang bi\u1ec7t, v\u00ed d\u1ee5:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>CsrfFilter: Ch\u1ed1ng t\u1ea5n c\u00f4ng CSRF.<\/li>\n\n\n\n<li>UsernamePasswordAuthenticationFilter: X\u1eed l\u00fd vi\u1ec7c x\u00e1c th\u1ef1c t\u1eeb form \u0111\u0103ng nh\u1eadp.<\/li>\n\n\n\n<li>BearerTokenAuthenticationFilter: X\u1eed l\u00fd x\u00e1c th\u1ef1c b\u1eb1ng JWT.<\/li>\n\n\n\n<li>AuthorizationFilter: Ki\u1ec3m tra quy\u1ec1n truy c\u1eadp t\u00e0i nguy\u00ean.<\/li>\n<\/ul>\n\n\n\n<p>Request s\u1ebd \u0111i l\u1ea7n l\u01b0\u1ee3t qua chu\u1ed7i filter n\u00e0y. N\u1ebfu m\u1ed9t filter n\u00e0o \u0111\u00f3 x\u00e1c \u0111\u1ecbnh request kh\u00f4ng h\u1ee3p l\u1ec7 (v\u00ed d\u1ee5: x\u00e1c th\u1ef1c th\u1ea5t b\u1ea1i), n\u00f3 s\u1ebd t\u1eeb ch\u1ed1i request ngay l\u1eadp t\u1ee9c m\u00e0 kh\u00f4ng c\u1ea7n \u0111i ti\u1ebfp.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>AuthenticationManager<\/strong>:<\/li>\n<\/ul>\n\n\n\n<p>\u0110\u00e2y l\u00e0 <strong>b\u1ed9 n\u00e3o c\u1ee7a qu\u00e1 tr\u00ecnh x\u00e1c th\u1ef1c<\/strong>. Nhi\u1ec7m v\u1ee5 ch\u00ednh c\u1ee7a n\u00f3 l\u00e0 nh\u1eadn m\u1ed9t \u0111\u1ed1i t\u01b0\u1ee3ng Authentication (th\u01b0\u1eddng ch\u1ee9a username v\u00e0 password ch\u01b0a \u0111\u01b0\u1ee3c x\u00e1c th\u1ef1c) v\u00e0 quy\u1ebft \u0111\u1ecbnh xem th\u00f4ng tin \u0111\u00f3 c\u00f3 h\u1ee3p l\u1ec7 hay kh\u00f4ng.<\/p>\n\n\n\n<p>AuthenticationManager th\u01b0\u1eddng \u1ee7y quy\u1ec1n cho m\u1ed9t ho\u1eb7c nhi\u1ec1u AuthenticationProvider. M\u1ed7i Provider s\u1ebd th\u1ef1c hi\u1ec7n m\u1ed9t logic x\u00e1c th\u1ef1c c\u1ee5 th\u1ec3. V\u00ed d\u1ee5, DaoAuthenticationProvider s\u1ebd l\u1ea5y th\u00f4ng tin user t\u1eeb database (th\u00f4ng qua UserDetailsService), so s\u00e1nh hash c\u1ee7a m\u1eadt kh\u1ea9u, v\u00e0 tr\u1ea3 v\u1ec1 k\u1ebft qu\u1ea3.<\/p>\n\n\n\n<p>N\u1ebfu x\u00e1c th\u1ef1c th\u00e0nh c\u00f4ng, AuthenticationManager s\u1ebd tr\u1ea3 v\u1ec1 m\u1ed9t \u0111\u1ed1i t\u01b0\u1ee3ng Authentication \u0111\u00e3 \u0111\u01b0\u1ee3c &#8220;l\u00e0m \u0111\u1ea7y&#8221; th\u00f4ng tin, bao g\u1ed3m c\u1ea3 danh s\u00e1ch c\u00e1c quy\u1ec1n (authorities) c\u1ee7a ng\u01b0\u1eddi d\u00f9ng.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>SecurityContextHolder<\/strong>:<\/li>\n<\/ul>\n\n\n\n<p>\u0110\u00e2y l\u00e0 n\u01a1i <strong>l\u01b0u tr\u1eef th\u00f4ng tin c\u1ee7a ng\u01b0\u1eddi d\u00f9ng \u0111\u00e3 \u0111\u01b0\u1ee3c x\u00e1c th\u1ef1c<\/strong> trong su\u1ed1t v\u00f2ng \u0111\u1eddi c\u1ee7a m\u1ed9t request.<\/p>\n\n\n\n<p>Sau khi AuthenticationManager x\u00e1c th\u1ef1c th\u00e0nh c\u00f4ng, \u0111\u1ed1i t\u01b0\u1ee3ng Authentication s\u1ebd \u0111\u01b0\u1ee3c l\u01b0u v\u00e0o SecurityContext, v\u00e0 SecurityContext l\u1ea1i \u0111\u01b0\u1ee3c \u0111\u1eb7t v\u00e0o SecurityContextHolder.<\/p>\n\n\n\n<p>SecurityContextHolder s\u1eed d\u1ee5ng m\u1ed9t ThreadLocal \u0111\u1ec3 l\u01b0u tr\u1eef SecurityContext. \u0110i\u1ec1u n\u00e0y \u0111\u1ea3m b\u1ea3o r\u1eb1ng th\u00f4ng tin x\u00e1c th\u1ef1c c\u1ee7a m\u1ed9t request ch\u1ec9 c\u00f3 th\u1ec3 truy c\u1eadp \u0111\u01b0\u1ee3c b\u1edfi ch\u00ednh thread \u0111ang x\u1eed l\u00fd request \u0111\u00f3.<\/p>\n\n\n\n<p>B\u1ea5t k\u1ef3 \u0111\u00e2u trong \u1ee9ng d\u1ee5ng, ta c\u0169ng c\u00f3 th\u1ec3 l\u1ea5y th\u00f4ng tin ng\u01b0\u1eddi d\u00f9ng \u0111ang \u0111\u0103ng nh\u1eadp b\u1eb1ng c\u00e1ch g\u1ecdi: SecurityContextHolder.getContext().getAuthentication();.<\/p>\n\n\n\n<p><strong>Lu\u1ed3ng t\u00f3m t\u1eaft:<\/strong> Request -&gt; FilterChainProxy -&gt; SecurityFilterChain -&gt; UsernamePasswordAuthenticationFilter -&gt; AuthenticationManager -&gt; AuthenticationProvider (l\u00e0m vi\u1ec7c v\u1edbi UserDetailsService) -&gt; N\u1ebfu th\u00e0nh c\u00f4ng, Authentication object \u0111\u01b0\u1ee3c l\u01b0u v\u00e0o SecurityContextHolder -&gt; Request \u0111i ti\u1ebfp.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-29-jwt-json-web-token-la-gi-va-c\u1ea5u-truc-c\u1ee7a-no-nh\u01b0-th\u1ebf-nao\"><strong>29. JWT (JSON Web Token) l\u00e0 g\u00ec v\u00e0 c\u1ea5u tr\u00fac c\u1ee7a n\u00f3 nh\u01b0 th\u1ebf n\u00e0o?\u00a0<\/strong><\/h3>\n\n\n\n<p><strong>JWT (JSON Web Token)<\/strong> l\u00e0 m\u1ed9t ti\u00eau chu\u1ea9n m\u1edf (RFC 7519) \u0111\u1ecbnh ngh\u0129a m\u1ed9t c\u00e1ch nh\u1ecf g\u1ecdn v\u00e0 kh\u00e9p k\u00edn (self-contained) \u0111\u1ec3 truy\u1ec1n th\u00f4ng tin an to\u00e0n gi\u1eefa c\u00e1c b\u00ean d\u01b0\u1edbi d\u1ea1ng m\u1ed9t \u0111\u1ed1i t\u01b0\u1ee3ng JSON. Th\u00f4ng tin n\u00e0y c\u00f3 th\u1ec3 \u0111\u01b0\u1ee3c x\u00e1c minh v\u00e0 tin c\u1eady v\u00ec n\u00f3 \u0111\u00e3 \u0111\u01b0\u1ee3c k\u00fd \u0111i\u1ec7n t\u1eed. JWT r\u1ea5t ph\u00f9 h\u1ee3p \u0111\u1ec3 x\u00e2y d\u1ef1ng c\u00e1c h\u1ec7 th\u1ed1ng x\u00e1c th\u1ef1c stateless cho RESTful API.<\/p>\n\n\n\n<p><strong>C\u1ea5u tr\u00fac c\u1ee7a JWT<\/strong><\/p>\n\n\n\n<p>M\u1ed9t JWT bao g\u1ed3m 3 ph\u1ea7n, \u0111\u01b0\u1ee3c ng\u0103n c\u00e1ch b\u1edfi d\u1ea5u ch\u1ea5m (.): header.payload.signature.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Header (Ti\u00eau \u0111\u1ec1)<\/strong>: Ch\u1ee9a th\u00f4ng tin v\u1ec1 thu\u1eadt to\u00e1n m\u00e3 h\u00f3a \u0111\u01b0\u1ee3c s\u1eed d\u1ee5ng v\u00e0 lo\u1ea1i token.\n<ul class=\"wp-block-list\">\n<li>V\u00ed d\u1ee5: { &#8220;alg&#8221;: &#8220;HS256&#8221;, &#8220;typ&#8221;: &#8220;JWT&#8221; }.<\/li>\n\n\n\n<li>Ph\u1ea7n n\u00e0y s\u1ebd \u0111\u01b0\u1ee3c m\u00e3 h\u00f3a Base64Url \u0111\u1ec3 t\u1ea1o th\u00e0nh ph\u1ea7n \u0111\u1ea7u ti\u00ean c\u1ee7a JWT.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Payload (D\u1eef li\u1ec7u)<\/strong>: Ch\u1ee9a c\u00e1c &#8220;claims&#8221; (tuy\u00ean b\u1ed1). Claims l\u00e0 c\u00e1c th\u00f4ng tin v\u1ec1 m\u1ed9t th\u1ef1c th\u1ec3 (th\u01b0\u1eddng l\u00e0 ng\u01b0\u1eddi d\u00f9ng) v\u00e0 c\u00e1c d\u1eef li\u1ec7u b\u1ed5 sung. C\u00f3 3 lo\u1ea1i claims:\n<ul class=\"wp-block-list\">\n<li><strong>Registered claims<\/strong>: C\u00e1c claim \u0111\u00e3 \u0111\u01b0\u1ee3c \u0111\u0103ng k\u00fd s\u1eb5n (v\u00ed d\u1ee5: iss &#8211; nh\u00e0 ph\u00e1t h\u00e0nh, sub &#8211; ch\u1ee7 \u0111\u1ec1\/user id, exp &#8211; th\u1eddi gian h\u1ebft h\u1ea1n).<\/li>\n\n\n\n<li><strong>Public claims<\/strong>: C\u00e1c claim t\u1ef1 \u0111\u1ecbnh ngh\u0129a nh\u01b0ng n\u00ean tr\u00e1nh xung \u0111\u1ed9t t\u00ean.<\/li>\n\n\n\n<li><strong>Private claims<\/strong>: C\u00e1c claim t\u1ef1 \u0111\u1ecbnh ngh\u0129a ri\u00eang gi\u1eefa c\u00e1c b\u00ean.<\/li>\n\n\n\n<li>V\u00ed d\u1ee5: { &#8220;sub&#8221;: &#8220;12345&#8221;, &#8220;name&#8221;: &#8220;John Doe&#8221;, &#8220;roles&#8221;: [&#8220;ADMIN&#8221;, &#8220;USER&#8221;], &#8220;iat&#8221;: 1516239022 }.<\/li>\n\n\n\n<li>Ph\u1ea7n n\u00e0y c\u0169ng \u0111\u01b0\u1ee3c m\u00e3 h\u00f3a Base64Url.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Signature (Ch\u1eef k\u00fd)<\/strong>: \u0110\u00e2y l\u00e0 ph\u1ea7n quan tr\u1ecdng nh\u1ea5t \u0111\u1ec3 \u0111\u1ea3m b\u1ea3o t\u00ednh to\u00e0n v\u1eb9n c\u1ee7a token. Ch\u1eef k\u00fd \u0111\u01b0\u1ee3c t\u1ea1o ra b\u1eb1ng c\u00e1ch:\n<ul class=\"wp-block-list\">\n<li>L\u1ea5y header v\u00e0 payload \u0111\u00e3 \u0111\u01b0\u1ee3c m\u00e3 h\u00f3a Base64Url.<\/li>\n\n\n\n<li>S\u1eed d\u1ee5ng thu\u1eadt to\u00e1n \u0111\u01b0\u1ee3c ch\u1ec9 \u0111\u1ecbnh trong header (v\u00ed d\u1ee5: HMAC-SHA256).<\/li>\n\n\n\n<li>K\u00fd ch\u00fang b\u1eb1ng m\u1ed9t <strong>secret key<\/strong> (kh\u00f3a b\u00ed m\u1eadt) ho\u1eb7c m\u1ed9t c\u1eb7p public\/private key.<\/li>\n\n\n\n<li>C\u00f4ng th\u1ee9c: HMACSHA256(base64UrlEncode(header) + &#8220;.&#8221; + base64UrlEncode(payload), secret)<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-30-trinh-bay-lu\u1ed3ng-xac-th\u1ef1c-s\u1eed-d\u1ee5ng-jwt-trong-m\u1ed9t-\u1ee9ng-d\u1ee5ng-spring-boot\"><strong>30. Tr\u00ecnh b\u00e0y lu\u1ed3ng x\u00e1c th\u1ef1c s\u1eed d\u1ee5ng JWT trong m\u1ed9t \u1ee9ng d\u1ee5ng Spring Boot.<\/strong><\/h3>\n\n\n\n<p>\u0110\u00e2y l\u00e0 lu\u1ed3ng stateless ph\u1ed5 bi\u1ebfn m\u00e0 t\u00f4i th\u01b0\u1eddng tri\u1ec3n khai:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Ng\u01b0\u1eddi d\u00f9ng \u0111\u0103ng nh\u1eadp<\/strong>: Client g\u1eedi request \u0111\u1ebfn m\u1ed9t endpoint \/api\/auth\/login v\u1edbi username v\u00e0 password trong request body.<\/li>\n\n\n\n<li><strong>Server x\u00e1c th\u1ef1c<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Endpoint n\u00e0y s\u1ebd g\u1ecdi AuthenticationManager c\u1ee7a Spring Security \u0111\u1ec3 x\u00e1c th\u1ef1c th\u00f4ng tin \u0111\u0103ng nh\u1eadp.<\/li>\n\n\n\n<li>N\u1ebfu x\u00e1c th\u1ef1c th\u00e0nh c\u00f4ng, server s\u1ebd t\u1ea1o ra m\u1ed9t JWT.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>T\u1ea1o JWT<\/strong>: Server s\u1ebd l\u1ea5y th\u00f4ng tin c\u1ee7a ng\u01b0\u1eddi d\u00f9ng (user ID, roles&#8230;) \u0111\u1ec3 \u0111\u01b0a v\u00e0o ph\u1ea7n <strong>payload<\/strong> c\u1ee7a JWT, \u0111\u1eb7t th\u1eddi gian h\u1ebft h\u1ea1n (exp), v\u00e0 d\u00f9ng m\u1ed9t <strong>secret key<\/strong> l\u01b0u tr\u00ean server \u0111\u1ec3 t\u1ea1o ra <strong>ch\u1eef k\u00fd<\/strong>.<\/li>\n\n\n\n<li><strong>Server tr\u1ea3 JWT v\u1ec1 cho Client<\/strong>: Server tr\u1ea3 v\u1ec1 m\u1ed9t response ch\u1ee9a JWT (v\u00ed d\u1ee5: trong m\u1ed9t JSON object { &#8220;accessToken&#8221;: &#8220;&#8230;&#8221; }).<\/li>\n\n\n\n<li><strong>Client l\u01b0u tr\u1eef JWT<\/strong>: Client (v\u00ed d\u1ee5: tr\u00ecnh duy\u1ec7t web) s\u1ebd l\u01b0u JWT n\u00e0y l\u1ea1i, th\u01b0\u1eddng l\u00e0 trong localStorage ho\u1eb7c sessionStorage.<\/li>\n\n\n\n<li><strong>Client g\u1eedi request k\u00e8m JWT<\/strong>: \u0110\u1ed1i v\u1edbi t\u1ea5t c\u1ea3 c\u00e1c request ti\u1ebfp theo c\u1ea7n x\u00e1c th\u1ef1c, client s\u1ebd \u0111\u00ednh k\u00e8m JWT v\u00e0o Authorization header theo \u0111\u1ecbnh d\u1ea1ng Bearer <token>.<\/token>\n<ul class=\"wp-block-list\">\n<li>V\u00ed d\u1ee5: Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9&#8230;<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Server x\u00e1c th\u1ef1c JWT<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Em s\u1ebd t\u1ea1o m\u1ed9t filter t\u00f9y ch\u1ec9nh (v\u00ed d\u1ee5: JwtAuthenticationFilter) v\u00e0 ch\u00e8n n\u00f3 v\u00e0o SecurityFilterChain (th\u01b0\u1eddng l\u00e0 tr\u01b0\u1edbc UsernamePasswordAuthenticationFilter).<\/li>\n\n\n\n<li>V\u1edbi m\u1ed7i request \u0111\u1ebfn, filter n\u00e0y s\u1ebd:\n<ul class=\"wp-block-list\">\n<li>\u0110\u1ecdc Authorization header \u0111\u1ec3 l\u1ea5y token.&nbsp;<\/li>\n\n\n\n<li>X\u00e1c minh ch\u1eef k\u00fd: D\u00f9ng ch\u00ednh secret key \u0111\u00e3 l\u01b0u tr\u00ean server \u0111\u1ec3 ki\u1ec3m tra xem ch\u1eef k\u00fd c\u1ee7a token c\u00f3 h\u1ee3p l\u1ec7 kh\u00f4ng. N\u1ebfu kh\u00f4ng, token \u0111\u00e3 b\u1ecb ch\u1ec9nh s\u1eeda -&gt; t\u1eeb ch\u1ed1i request.&nbsp;<\/li>\n\n\n\n<li>Ki\u1ec3m tra t\u00ednh h\u1ee3p l\u1ec7: Ki\u1ec3m tra xem token \u0111\u00e3 h\u1ebft h\u1ea1n hay ch\u01b0a.&nbsp;<\/li>\n\n\n\n<li>N\u1ebfu token h\u1ee3p l\u1ec7, filter s\u1ebd tr\u00edch xu\u1ea5t th\u00f4ng tin t\u1eeb payload (user ID, roles).&nbsp;<\/li>\n\n\n\n<li>T\u1ea1o m\u1ed9t \u0111\u1ed1i t\u01b0\u1ee3ng Authentication (v\u00ed d\u1ee5: UsernamePasswordAuthenticationToken) t\u1eeb c\u00e1c th\u00f4ng tin n\u00e0y v\u00e0 l\u01b0u v\u00e0o SecurityContextHolder.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>\u1ee6y quy\u1ec1n v\u00e0 x\u1eed l\u00fd request<\/strong>: Sau khi th\u00f4ng tin x\u00e1c th\u1ef1c \u0111\u01b0\u1ee3c l\u01b0u v\u00e0o SecurityContextHolder, c\u00e1c b\u01b0\u1edbc ti\u1ebfp theo c\u1ee7a Spring Security (nh\u01b0 ki\u1ec3m tra quy\u1ec1n h\u1ea1n @PreAuthorize) v\u00e0 logic trong Controller s\u1ebd ho\u1ea1t \u0111\u1ed9ng nh\u01b0 b\u00ecnh th\u01b0\u1eddng.<\/li>\n<\/ul>\n\n\n\n<p>Lu\u1ed3ng n\u00e0y l\u00e0 <strong>stateless<\/strong> v\u00ec server kh\u00f4ng c\u1ea7n l\u01b0u tr\u1eef b\u1ea5t k\u1ef3 th\u00f4ng tin session n\u00e0o c\u1ee7a ng\u01b0\u1eddi d\u00f9ng. M\u1ecdi th\u00f4ng tin c\u1ea7n thi\u1ebft \u0111\u1ec1u n\u1eb1m trong JWT m\u00e0 client g\u1eedi l\u00ean.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-31-cors-cross-origin-resource-sharing-la-gi-va-lam-th\u1ebf-nao-d\u1ec3-c\u1ea5u-hinh-no-trong-spring-boot\"><strong>31. CORS (Cross-Origin Resource Sharing) l\u00e0 g\u00ec v\u00e0 l\u00e0m th\u1ebf n\u00e0o \u0111\u1ec3 c\u1ea5u h\u00ecnh n\u00f3 trong Spring Boot?<\/strong><\/h3>\n\n\n\n<p><strong>CORS (Cross-Origin Resource Sharing)<\/strong> l\u00e0 m\u1ed9t c\u01a1 ch\u1ebf b\u1ea3o m\u1eadt c\u1ee7a tr\u00ecnh duy\u1ec7t. N\u00f3 cho ph\u00e9p ho\u1eb7c ng\u0103n ch\u1eb7n c\u00e1c trang web \u0111\u01b0\u1ee3c t\u1ea3i t\u1eeb m\u1ed9t &#8220;origin&#8221; (g\u1ed1c &#8211; bao g\u1ed3m scheme, domain, port) n\u00e0y y\u00eau c\u1ea7u t\u00e0i nguy\u00ean t\u1eeb m\u1ed9t &#8220;origin&#8221; kh\u00e1c.<\/p>\n\n\n\n<p>M\u1eb7c \u0111\u1ecbnh, ch\u00ednh s\u00e1ch Same-Origin Policy (SOP) c\u1ee7a tr\u00ecnh duy\u1ec7t s\u1ebd ch\u1eb7n c\u00e1c request nh\u01b0 v\u1eady. V\u00ed d\u1ee5, m\u1ed9t trang frontend ch\u1ea1y \u1edf http:\/\/localhost:3000 s\u1ebd kh\u00f4ng th\u1ec3 g\u1ecdi API \u1edf http:\/\/localhost:8080 n\u1ebfu server kh\u00f4ng cho ph\u00e9p. CORS \u0111\u01b0\u1ee3c sinh ra \u0111\u1ec3 n\u1edbi l\u1ecfng ch\u00ednh s\u00e1ch n\u00e0y m\u1ed9t c\u00e1ch c\u00f3 ki\u1ec3m so\u00e1t.<\/p>\n\n\n\n<p><strong>Trong Spring Boot, ta c\u00f3 th\u1ec3 c\u1ea5u h\u00ecnh CORS theo hai c\u00e1ch ch\u00ednh:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>C\u1ea5u h\u00ecnh \u1edf m\u1ee9c Controller\/Method v\u1edbi @CrossOrigin<\/strong>:<\/li>\n<\/ul>\n\n\n\n<p>\u0110\u00e2y l\u00e0 c\u00e1ch \u0111\u01a1n gi\u1ea3n v\u00e0 nhanh ch\u00f3ng nh\u1ea5t, ph\u00f9 h\u1ee3p khi ch\u1ec9 c\u1ea7n m\u1edf CORS cho m\u1ed9t v\u00e0i endpoint c\u1ee5 th\u1ec3. T\u00f4i c\u00f3 th\u1ec3 \u0111\u1eb7t annotation @CrossOrigin tr\u00ean m\u1ed9t class Controller ho\u1eb7c tr\u00ean m\u1ed9t method ri\u00eang l\u1ebb.<\/p>\n\n\n\n<p>V\u00ed d\u1ee5:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@RestController\n@RequestMapping(\"\/api\/tasks\")\n\/\/ M\u1edf CORS cho t\u1ea5t c\u1ea3 c\u00e1c method trong class n\u00e0y t\u1eeb m\u1ecdi origin\n@CrossOrigin(origins = \"*\") \npublic class TaskController {\n\n    @GetMapping(\"\/{id}\")\n    \/\/ Ghi \u0111\u00e8 c\u1ea5u h\u00ecnh, ch\u1ec9 cho ph\u00e9p origin c\u1ee5 th\u1ec3 v\u00e0 method GET\n    @CrossOrigin(origins = \"http:\/\/localhost:3000\", methods = RequestMethod.GET)\n    public Task getTask(@PathVariable Long id) {\n        \/\/ ...\n    }\n}<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>C\u1ea5u h\u00ecnh to\u00e0n c\u1ee5c (Global Configuration)<\/strong>:<\/li>\n<\/ul>\n\n\n\n<p>\u0110\u00e2y l\u00e0 c\u00e1ch l\u00e0m \u0111\u01b0\u1ee3c khuy\u1ebfn kh\u00edch khi c\u1ea7n \u00e1p d\u1ee5ng m\u1ed9t ch\u00ednh s\u00e1ch CORS nh\u1ea5t qu\u00e1n cho to\u00e0n b\u1ed9 \u1ee9ng d\u1ee5ng. T\u00f4i s\u1ebd t\u1ea1o m\u1ed9t class Configuration v\u00e0 implement interface WebMvcConfigurer, sau \u0111\u00f3 override method addCorsMappings.<\/p>\n\n\n\n<p>V\u00ed d\u1ee5:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@Configuration\npublic class WebConfig implements WebMvcConfigurer {\n\n    @Override\n    public void addCorsMappings(CorsRegistry registry) {\n        registry.addMapping(\"\/api\/**\") \/\/ \u00c1p d\u1ee5ng cho c\u00e1c path b\u1eaft \u0111\u1ea7u b\u1eb1ng \/api\/\n                .allowedOrigins(\"http:\/\/localhost:3000\", \"https:\/\/my-prod-domain.com\") \/\/ C\u00e1c origin \u0111\u01b0\u1ee3c ph\u00e9p\n                .allowedMethods(\"GET\", \"POST\", \"PUT\", \"DELETE\", \"OPTIONS\") \/\/ C\u00e1c method \u0111\u01b0\u1ee3c ph\u00e9p\n                .allowedHeaders(\"*\") \/\/ C\u00e1c header \u0111\u01b0\u1ee3c ph\u00e9p\n                .allowCredentials(true) \/\/ Cho ph\u00e9p g\u1eedi cookie\n                .maxAge(3600); \/\/ Th\u1eddi gian cache c\u1ee7a pre-flight request\n    }\n}<\/code><\/pre>\n\n\n\n<p>C\u00e1ch n\u00e0y m\u1ea1nh m\u1ebd h\u01a1n v\u00ec n\u00f3 cho ph\u00e9p c\u1ea5u h\u00ecnh chi ti\u1ebft v\u00e0 t\u1eadp trung logic CORS \u1edf m\u1ed9t n\u01a1i duy nh\u1ea5t. Khi k\u1ebft h\u1ee3p v\u1edbi Spring Security, ta th\u01b0\u1eddng s\u1ebd \u0111\u1ecbnh ngh\u0129a m\u1ed9t CorsConfigurationSource bean \u0111\u1ec3 t\u00edch h\u1ee3p.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-32-gi\u1ea3i-thich-vai-tro-c\u1ee7a-delegatingfilterproxy-trong-vi\u1ec7c-tich-h\u1ee3p-spring-security-v\u1edbi-servlet-container\"><strong>32. Gi\u1ea3i th\u00edch vai tr\u00f2 c\u1ee7a DelegatingFilterProxy trong vi\u1ec7c t\u00edch h\u1ee3p Spring Security v\u1edbi Servlet Container.<\/strong><\/h3>\n\n\n\n<p><strong>DelegatingFilterProxy<\/strong> \u0111\u00f3ng vai tr\u00f2 l\u00e0 m\u1ed9t <strong>c\u00e2y c\u1ea7u n\u1ed1i<\/strong> gi\u1eefa v\u00f2ng \u0111\u1eddi c\u1ee7a Servlet Container (v\u00ed d\u1ee5: Tomcat) v\u00e0 Application Context c\u1ee7a Spring.<\/p>\n\n\n\n<p>Trong ki\u1ebfn tr\u00fac Servlet chu\u1ea9n, c\u00e1c filter l\u00e0 m\u1ed9t ph\u1ea7n c\u1ee7a Servlet Container. Ch\u00fang \u0111\u01b0\u1ee3c kh\u1edfi t\u1ea1o v\u00e0 qu\u1ea3n l\u00fd b\u1edfi container, kh\u00f4ng ph\u1ea3i b\u1edfi Spring. \u0110i\u1ec1u n\u00e0y c\u00f3 ngh\u0129a l\u00e0 m\u1ed9t filter th\u00f4ng th\u01b0\u1eddng kh\u00f4ng th\u1ec3 @Autowired c\u00e1c Spring bean kh\u00e1c.<\/p>\n\n\n\n<p>Tuy nhi\u00ean, Spring Security l\u1ea1i l\u00e0 m\u1ed9t framework \u0111\u01b0\u1ee3c x\u00e2y d\u1ef1ng ho\u00e0n to\u00e0n d\u1ef1a tr\u00ean c\u00e1c Spring bean. To\u00e0n b\u1ed9 chu\u1ed7i filter, AuthenticationManager, UserDetailsService&#8230; \u0111\u1ec1u l\u00e0 c\u00e1c bean \u0111\u01b0\u1ee3c qu\u1ea3n l\u00fd b\u1edfi Spring IoC container.<\/p>\n\n\n\n<p>\u0110\u00e2y ch\u00ednh l\u00e0 l\u00fac DelegatingFilterProxy ph\u00e1t huy vai tr\u00f2:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>N\u00f3 l\u00e0 m\u1ed9t Servlet Filter ti\u00eau chu\u1ea9n<\/strong>: DelegatingFilterProxy \u0111\u01b0\u1ee3c \u0111\u0103ng k\u00fd v\u1edbi Servlet Container nh\u01b0 b\u1ea5t k\u1ef3 filter n\u00e0o kh\u00e1c (th\u00f4ng qua web.xml ng\u00e0y x\u01b0a ho\u1eb7c FilterRegistrationBean trong c\u00e1c \u1ee9ng d\u1ee5ng Spring Boot hi\u1ec7n \u0111\u1ea1i). V\u00ec v\u1eady, Servlet Container bi\u1ebft c\u00e1ch kh\u1edfi t\u1ea1o v\u00e0 s\u1eed d\u1ee5ng n\u00f3.<\/li>\n\n\n\n<li><strong>N\u00f3 kh\u00f4ng th\u1ef1c hi\u1ec7n logic g\u00ec c\u1ea3<\/strong>: Nhi\u1ec7m v\u1ee5 duy nh\u1ea5t c\u1ee7a DelegatingFilterProxy l\u00e0 t\u00ecm ki\u1ebfm m\u1ed9t Spring bean trong Application Context c\u00f3 c\u00f9ng t\u00ean v\u1edbi n\u00f3 v\u00e0 \u1ee7y quy\u1ec1n (delegate) to\u00e0n b\u1ed9 c\u00f4ng vi\u1ec7c x\u1eed l\u00fd request cho bean \u0111\u00f3.<\/li>\n\n\n\n<li><strong>Bean \u0111\u00edch th\u01b0\u1eddng l\u00e0 FilterChainProxy<\/strong>: Trong Spring Security, bean m\u00e0 DelegatingFilterProxy t\u00ecm ki\u1ebfm m\u1eb7c \u0111\u1ecbnh c\u00f3 t\u00ean l\u00e0 springSecurityFilterChain. Bean n\u00e0y ch\u00ednh l\u00e0 FilterChainProxy, l\u00e0 \u0111i\u1ec3m kh\u1edfi \u0111\u1ea7u th\u1ef1c s\u1ef1 c\u1ee7a chu\u1ed7i filter trong Spring Security.<\/li>\n<\/ol>\n\n\n\n<p><strong>T\u00f3m l\u1ea1i:<\/strong> DelegatingFilterProxy cho ph\u00e9p m\u1ed9t request t\u1eeb th\u1ebf gi\u1edbi &#8220;Servlet Container&#8221; \u0111i qua m\u1ed9t &#8220;c\u1ed5ng trung gian&#8221; \u0111\u1ec3 v\u00e0o th\u1ebf gi\u1edbi &#8220;Spring Application Context&#8221;. Nh\u1edd c\u00f3 n\u00f3, chu\u1ed7i filter c\u1ee7a Spring Security c\u00f3 th\u1ec3 t\u1eadn d\u1ee5ng to\u00e0n b\u1ed9 s\u1ee9c m\u1ea1nh c\u1ee7a Spring nh\u01b0 Dependency Injection, AOP, qu\u1ea3n l\u00fd transaction&#8230; m\u00e0 v\u1eabn t\u00edch h\u1ee3p li\u1ec1n m\u1ea1ch v\u00e0o lu\u1ed3ng x\u1eed l\u00fd request chu\u1ea9n c\u1ee7a m\u1ed9t \u1ee9ng d\u1ee5ng Java web.<\/p>\n\n\n\n<p><strong>Lu\u1ed3ng t\u00f3m t\u1eaft:<\/strong> Tomcat -&gt; DelegatingFilterProxy (qu\u1ea3n l\u00fd b\u1edfi Tomcat) -&gt; t\u00ecm bean &#8220;springSecurityFilterChain&#8221; trong Spring Context -&gt; FilterChainProxy (qu\u1ea3n l\u00fd b\u1edfi Spring) -&gt; b\u1eaft \u0111\u1ea7u x\u1eed l\u00fd request.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-33-so-sanh-cac-cach-ti\u1ebfp-c\u1eadn-b\u1ea3o-m\u1eadt-\u1edf-m\u1ee9c-ph\u01b0\u01a1ng-th\u1ee9c-secured-rolesallowed-va-preauthorize-postauthorize\"><strong>33. So s\u00e1nh c\u00e1c c\u00e1ch ti\u1ebfp c\u1eadn b\u1ea3o m\u1eadt \u1edf m\u1ee9c ph\u01b0\u01a1ng th\u1ee9c: @Secured, @RolesAllowed, v\u00e0 @PreAuthorize\/@PostAuthorize.\u00a0<\/strong><\/h3>\n\n\n\n<p>C\u1ea3 ba nh\u00f3m annotation n\u00e0y \u0111\u1ec1u d\u00f9ng \u0111\u1ec3 b\u1ea3o m\u1eadt \u1edf m\u1ee9c ph\u01b0\u01a1ng th\u1ee9c (method-level security), nh\u01b0ng ch\u00fang kh\u00e1c nhau v\u1ec1 ngu\u1ed3n g\u1ed1c, kh\u1ea3 n\u0103ng v\u00e0 \u0111\u1ed9 linh ho\u1ea1t.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Ti\u00eau ch\u00ed<\/strong><\/td><td><strong>@Secured<\/strong><\/td><td><strong>@RolesAllowed<\/strong><\/td><td><strong>@PreAuthorize \/ @PostAuthorize<\/strong><\/td><\/tr><tr><td><strong>Ngu\u1ed3n g\u1ed1c<\/strong><\/td><td>Spring Security-specific (c\u0169 nh\u1ea5t)<\/td><td>JSR-250 Standard (chu\u1ea9n Java EE)<\/td><td>Spring Security-specific (m\u1edbi v\u00e0 m\u1ea1nh nh\u1ea5t)<\/td><\/tr><tr><td><strong>Kh\u1ea3 n\u0103ng<\/strong><\/td><td>\u0110\u01a1n gi\u1ea3n: Ch\u1ec9 ki\u1ec3m tra vai tr\u00f2 (authority) ch\u00ednh x\u00e1c.<\/td><td>\u0110\u01a1n gi\u1ea3n: G\u1ea7n gi\u1ed1ng @Secured, ch\u1ec9 ki\u1ec3m tra vai tr\u00f2.<\/td><td>C\u1ef1c k\u1ef3 linh ho\u1ea1t: S\u1eed d\u1ee5ng SpEL (Spring Expression Language).<\/td><\/tr><tr><td><strong>C\u00fa ph\u00e1p<\/strong><\/td><td>@Secured(&#8220;ROLE_ADMIN&#8221;)&nbsp;&nbsp;@Secured({&#8220;ROLE_USER&#8221;, &#8220;ROLE_VIEWER&#8221;})<\/td><td>@RolesAllowed(&#8220;ADMIN&#8221;)&nbsp;&nbsp;@RolesAllowed({&#8220;USER&#8221;, &#8220;VIEWER&#8221;})<\/td><td>@PreAuthorize(&#8220;hasRole(&#8216;ADMIN&#8217;)&#8221;)&nbsp;&nbsp;@PreAuthorize(&#8220;hasAuthority(&#8216;WRITE_PRIVILEGE&#8217;)&#8221;)&nbsp;&nbsp;@PreAuthorize(&#8220;isAuthenticated() and #user.name == principal.username&#8221;)<\/td><\/tr><tr><td><strong>Y\u00eau c\u1ea7u ROLE_ prefix?<\/strong><\/td><td>C\u00f3, theo quy \u01b0\u1edbc.<\/td><td>Kh\u00f4ng.<\/td><td>Kh\u00f4ng (khi d\u00f9ng hasRole()), nh\u01b0ng v\u1eabn c\u00f3 th\u1ec3 c\u1ea5u h\u00ecnh.<\/td><\/tr><tr><td><strong>\u0110\u1ed9 ph\u1ee9c t\u1ea1p<\/strong><\/td><td>D\u1ec5 nh\u1ea5t.<\/td><td>D\u1ec5&nbsp;<\/td><td>Ph\u1ee9c t\u1ea1p h\u01a1n nh\u01b0ng m\u1ea1nh m\u1ebd h\u01a1n.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-34-khi-nao-nen-dung-spel-spring-expression-language-trong-cac-bi\u1ec3u-th\u1ee9c-b\u1ea3o-m\u1eadt\"><strong>34. Khi n\u00e0o n\u00ean d\u00f9ng SpEL (Spring Expression Language) trong c\u00e1c bi\u1ec3u th\u1ee9c b\u1ea3o m\u1eadt?<\/strong><\/h3>\n\n\n\n<p>T\u00f4i s\u1ebd ch\u1ecdn s\u1eed d\u1ee5ng <strong>SpEL<\/strong> khi y\u00eau c\u1ea7u v\u1ec1 \u1ee7y quy\u1ec1n <strong>v\u01b0\u1ee3t ra ngo\u00e0i vi\u1ec7c ki\u1ec3m tra vai tr\u00f2 \u0111\u01a1n gi\u1ea3n<\/strong>. SpEL cho ph\u00e9p vi\u1ebft c\u00e1c bi\u1ec3u th\u1ee9c logic ph\u1ee9c t\u1ea1p \u0111\u1ec3 \u0111\u01b0a ra quy\u1ebft \u0111\u1ecbnh \u1ee7y quy\u1ec1n. C\u1ee5 th\u1ec3:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u1ee6y quy\u1ec1n d\u1ef1a tr\u00ean tham s\u1ed1 c\u1ee7a ph\u01b0\u01a1ng th\u1ee9c<\/strong>: Khi c\u1ea7n ki\u1ec3m tra xem ng\u01b0\u1eddi d\u00f9ng hi\u1ec7n t\u1ea1i c\u00f3 li\u00ean quan g\u00ec \u0111\u1ebfn d\u1eef li\u1ec7u \u0111ang \u0111\u01b0\u1ee3c truy c\u1eadp hay kh\u00f4ng. \u0110\u00e2y l\u00e0 tr\u01b0\u1eddng h\u1ee3p ph\u1ed5 bi\u1ebfn nh\u1ea5t.<\/li>\n<\/ul>\n\n\n\n<p>V\u00ed d\u1ee5: Ch\u1ec9 cho ph\u00e9p ng\u01b0\u1eddi d\u00f9ng ch\u1ec9nh s\u1eeda th\u00f4ng tin c\u1ee7a ch\u00ednh h\u1ecd.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@PreAuthorize(\"#username == authentication.principal.username or hasRole('ADMIN')\")\npublic void updateUser(String username, @RequestBody UserDto userDto) { ... }<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u1ee6y quy\u1ec1n d\u1ef1a tr\u00ean k\u1ebft qu\u1ea3 tr\u1ea3 v\u1ec1 c\u1ee7a ph\u01b0\u01a1ng th\u1ee9c (@PostAuthorize)<\/strong>: Khi quy\u1ebft \u0111\u1ecbnh \u1ee7y quy\u1ec1n ph\u1ee5 thu\u1ed9c v\u00e0o n\u1ed9i dung c\u1ee7a d\u1eef li\u1ec7u sau khi ph\u01b0\u01a1ng th\u1ee9c \u0111\u00e3 th\u1ef1c thi.<\/li>\n<\/ul>\n\n\n\n<p>V\u00ed d\u1ee5: Ch\u1ec9 tr\u1ea3 v\u1ec1 m\u1ed9t \u0111\u01a1n h\u00e0ng n\u1ebfu ng\u01b0\u1eddi d\u00f9ng hi\u1ec7n t\u1ea1i l\u00e0 ch\u1ee7 c\u1ee7a \u0111\u01a1n h\u00e0ng \u0111\u00f3.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@PostAuthorize(\"returnObject.owner == authentication.principal.username\")\npublic Order getOrderDetails(Long id) { ... }<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>K\u1ebft h\u1ee3p nhi\u1ec1u \u0111i\u1ec1u ki\u1ec7n logic ph\u1ee9c t\u1ea1p<\/strong>: Khi c\u1ea7n c\u00e1c to\u00e1n t\u1eed AND, OR, NOT \u0111\u1ec3 k\u1ebft h\u1ee3p nhi\u1ec1u \u0111i\u1ec1u ki\u1ec7n ki\u1ec3m tra.<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>@PreAuthorize(\"hasRole('ADMIN') and hasAuthority('READ_SENSITIVE_DATA')\")<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>G\u1ecdi c\u00e1c bean kh\u00e1c \u0111\u1ec3 ki\u1ec3m tra quy\u1ec1n<\/strong>: SpEL cho ph\u00e9p g\u1ecdi c\u00e1c ph\u01b0\u01a1ng th\u1ee9c t\u1eeb c\u00e1c Spring bean kh\u00e1c \u0111\u1ec3 th\u1ef1c hi\u1ec7n logic ki\u1ec3m tra quy\u1ec1n t\u00f9y ch\u1ec9nh.<\/li>\n<\/ul>\n\n\n\n<p>V\u00ed d\u1ee5: Ki\u1ec3m tra xem user c\u00f3 ph\u1ea3i l\u00e0 th\u00e0nh vi\u00ean c\u1ee7a m\u1ed9t project hay kh\u00f4ng b\u1eb1ng c\u00e1ch g\u1ecdi m\u1ed9t PermissionService bean.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@PreAuthorize(\"@permissionService.isProjectMember(authentication, #projectId)\")\npublic Project getProject(Long projectId) { ... }<\/code><\/pre>\n\n\n\n<p><strong>T\u00f3m t\u1eaft:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>D\u00f9ng <strong>@RolesAllowed<\/strong> (ho\u1eb7c @Secured) khi nhu c\u1ea7u ch\u1ec9 \u0111\u01a1n gi\u1ea3n l\u00e0 &#8220;user n\u00e0y ph\u1ea3i c\u00f3 vai tr\u00f2 X&#8221;.<\/li>\n\n\n\n<li>D\u00f9ng <strong>@PreAuthorize \/ @PostAuthorize<\/strong> khi c\u1ea7n b\u1ea5t k\u1ef3 logic \u1ee7y quy\u1ec1n \u0111\u1ed9ng, ph\u1ee9c t\u1ea1p n\u00e0o li\u00ean quan \u0111\u1ebfn d\u1eef li\u1ec7u ho\u1eb7c ng\u1eef c\u1ea3nh c\u1ee7a request. Theo em, @PreAuthorize l\u00e0 l\u1ef1a ch\u1ecdn m\u1ea1nh m\u1ebd v\u00e0 n\u00ean \u0111\u01b0\u1ee3c \u01b0u ti\u00ean trong h\u1ea7u h\u1ebft c\u00e1c d\u1ef1 \u00e1n hi\u1ec7n \u0111\u1ea1i.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-35-csrf-cross-site-request-forgery-la-gi-va-spring-security-giup-chung-ta-ch\u1ed1ng-l\u1ea1i-no-nh\u01b0-th\u1ebf-nao\"><strong>35. CSRF (Cross-Site Request Forgery) l\u00e0 g\u00ec v\u00e0 Spring Security gi\u00fap ch\u00fang ta ch\u1ed1ng l\u1ea1i n\u00f3 nh\u01b0 th\u1ebf n\u00e0o?<\/strong><\/h3>\n\n\n\n<p><strong>CSRF (Cross-Site Request Forgery)<\/strong> l\u00e0 m\u1ed9t lo\u1ea1i t\u1ea5n c\u00f4ng b\u1ea3o m\u1eadt, trong \u0111\u00f3 k\u1ebb t\u1ea5n c\u00f4ng l\u1eeba m\u1ed9t ng\u01b0\u1eddi d\u00f9ng \u0111\u00e3 \u0111\u01b0\u1ee3c x\u00e1c th\u1ef1c (\u0111ang \u0111\u0103ng nh\u1eadp) th\u1ef1c hi\u1ec7n m\u1ed9t h\u00e0nh \u0111\u1ed9ng kh\u00f4ng mong mu\u1ed1n tr\u00ean m\u1ed9t trang web.<\/p>\n\n\n\n<p><strong>Lu\u1ed3ng t\u1ea5n c\u00f4ng di\u1ec5n ra nh\u01b0 sau:<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Ng\u01b0\u1eddi d\u00f9ng \u0111\u0103ng nh\u1eadp v\u00e0o m\u1ed9t trang web \u0111\u00e1ng tin c\u1eady, v\u00ed d\u1ee5 my-bank.com. Tr\u00ecnh duy\u1ec7t c\u1ee7a h\u1ecd s\u1ebd l\u01b0u l\u1ea1i cookie session cho trang n\u00e0y.<\/li>\n\n\n\n<li>K\u1ebb t\u1ea5n c\u00f4ng l\u1eeba ng\u01b0\u1eddi d\u00f9ng truy c\u1eadp m\u1ed9t trang web \u0111\u1ed9c h\u1ea1i, v\u00ed d\u1ee5 evil-site.com.<\/li>\n\n\n\n<li>Tr\u00ean trang evil-site.com, c\u00f3 m\u1ed9t \u0111o\u1ea1n code (v\u00ed d\u1ee5: m\u1ed9t form \u1ea9n ho\u1eb7c m\u1ed9t th\u1ebb &lt;img&gt;) t\u1ef1 \u0111\u1ed9ng g\u1eedi m\u1ed9t request \u0111\u1ebfn my-bank.com, v\u00ed d\u1ee5 nh\u01b0 m\u1ed9t request chuy\u1ec3n ti\u1ec1n: POST \/transfer?to=attacker&amp;amount=1000.<\/li>\n\n\n\n<li>V\u00ec ng\u01b0\u1eddi d\u00f9ng v\u1eabn \u0111ang \u0111\u0103ng nh\u1eadp v\u00e0o my-bank.com, tr\u00ecnh duy\u1ec7t s\u1ebd t\u1ef1 \u0111\u1ed9ng \u0111\u00ednh k\u00e8m cookie session v\u00e0o request n\u00e0y.<\/li>\n\n\n\n<li>Server c\u1ee7a my-bank.com nh\u1eadn \u0111\u01b0\u1ee3c request, th\u1ea5y c\u00f3 cookie h\u1ee3p l\u1ec7, v\u00e0 th\u1ef1c hi\u1ec7n h\u00e0nh \u0111\u1ed9ng chuy\u1ec3n ti\u1ec1n m\u00e0 kh\u00f4ng h\u1ec1 bi\u1ebft r\u1eb1ng request n\u00e0y kh\u00f4ng xu\u1ea5t ph\u00e1t t\u1eeb \u00fd mu\u1ed1n c\u1ee7a ng\u01b0\u1eddi d\u00f9ng.<\/li>\n<\/ol>\n\n\n\n<p><strong>C\u00e1ch Spring Security ch\u1ed1ng l\u1ea1i CSRF<\/strong><\/p>\n\n\n\n<p>Spring Security tri\u1ec3n khai m\u1ed9t trong nh\u1eefng c\u01a1 ch\u1ebf ph\u00f2ng ch\u1ed1ng CSRF hi\u1ec7u qu\u1ea3 nh\u1ea5t, \u0111\u00f3 l\u00e0 <strong>Synchronizer Token Pattern<\/strong>. C\u01a1 ch\u1ebf n\u00e0y ho\u1ea1t \u0111\u1ed9ng nh\u01b0 sau:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>B\u1eadt t\u00ednh n\u0103ng ch\u1ed1ng CSRF<\/strong>: Trong c\u00e1c \u1ee9ng d\u1ee5ng web truy\u1ec1n th\u1ed1ng (s\u1eed d\u1ee5ng session v\u00e0 cookie), Spring Security b\u1eadt t\u00ednh n\u0103ng n\u00e0y theo m\u1eb7c \u0111\u1ecbnh.<\/li>\n\n\n\n<li><strong>T\u1ea1o v\u00e0 l\u01b0u tr\u1eef CSRF Token<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Khi ng\u01b0\u1eddi d\u00f9ng truy c\u1eadp trang web l\u1ea7n \u0111\u1ea7u (ho\u1eb7c \u0111\u0103ng nh\u1eadp), server s\u1ebd t\u1ea1o ra m\u1ed9t token ng\u1eabu nhi\u00ean, kh\u00f4ng th\u1ec3 \u0111o\u00e1n tr\u01b0\u1edbc, g\u1ecdi l\u00e0 CSRF token.<\/li>\n\n\n\n<li>Token n\u00e0y s\u1ebd \u0111\u01b0\u1ee3c l\u01b0u tr\u1eef \u1edf ph\u00eda server, th\u01b0\u1eddng l\u00e0 trong HttpSession.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>G\u1eedi Token \u0111\u1ebfn Client<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Server s\u1ebd g\u1eedi token n\u00e0y \u0111\u1ebfn cho client. \u0110\u1ed1i v\u1edbi c\u00e1c \u1ee9ng d\u1ee5ng web truy\u1ec1n th\u1ed1ng, Spring Security s\u1ebd t\u1ef1 \u0111\u1ed9ng ch\u00e8n token n\u00e0y nh\u01b0 m\u1ed9t tr\u01b0\u1eddng \u1ea9n (&lt;input type=&#8221;hidden&#8221;&gt;) v\u00e0o t\u1ea5t c\u1ea3 c\u00e1c form \u0111\u01b0\u1ee3c t\u1ea1o b\u1edfi Spring Form tags ho\u1eb7c Thymeleaf.<\/li>\n\n\n\n<li>Token n\u00e0y c\u0169ng c\u00f3 th\u1ec3 \u0111\u01b0\u1ee3c g\u1eedi qua m\u1ed9t cookie (v\u00ed d\u1ee5: XSRF-TOKEN) \u0111\u1ec3 c\u00e1c JavaScript framework nh\u01b0 Angular c\u00f3 th\u1ec3 \u0111\u1ecdc v\u00e0 s\u1eed d\u1ee5ng.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Client g\u1eedi l\u1ea1i Token<\/strong>: Khi ng\u01b0\u1eddi d\u00f9ng submit m\u1ed9t form ho\u1eb7c g\u1eedi m\u1ed9t request thay \u0111\u1ed5i tr\u1ea1ng th\u00e1i (nh\u01b0 POST, PUT, DELETE), client ph\u1ea3i g\u1eedi l\u1ea1i CSRF token n\u00e0y cho server. Token th\u01b0\u1eddng \u0111\u01b0\u1ee3c g\u1eedi d\u01b0\u1edbi d\u1ea1ng m\u1ed9t request parameter (t\u1eeb tr\u01b0\u1eddng \u1ea9n trong form) ho\u1eb7c m\u1ed9t HTTP header (v\u00ed d\u1ee5: X-XSRF-TOKEN).<\/li>\n\n\n\n<li><strong>Server x\u00e1c minh Token<\/strong>:<\/li>\n<\/ul>\n\n\n\n<p>Tr\u01b0\u1edbc khi th\u1ef1c hi\u1ec7n request, CsrfFilter c\u1ee7a Spring Security s\u1ebd ch\u1eb7n request l\u1ea1i. N\u00f3 s\u1ebd so s\u00e1nh token \u0111\u01b0\u1ee3c g\u1eedi l\u00ean t\u1eeb client v\u1edbi token \u0111\u00e3 \u0111\u01b0\u1ee3c l\u01b0u trong session c\u1ee7a ng\u01b0\u1eddi d\u00f9ng tr\u00ean server.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>N\u1ebfu hai token kh\u1edbp nhau, request \u0111\u01b0\u1ee3c coi l\u00e0 h\u1ee3p l\u1ec7 v\u00e0 \u0111\u01b0\u1ee3c x\u1eed l\u00fd ti\u1ebfp.<\/li>\n\n\n\n<li>N\u1ebfu token thi\u1ebfu ho\u1eb7c kh\u00f4ng kh\u1edbp, request s\u1ebd b\u1ecb t\u1eeb ch\u1ed1i v\u1edbi l\u1ed7i 403 Forbidden.<\/li>\n<\/ul>\n\n\n\n<p><strong>T\u1ea1i sao c\u01a1 ch\u1ebf n\u00e0y hi\u1ec7u qu\u1ea3?<\/strong> Trang web \u0111\u1ed9c h\u1ea1i c\u1ee7a k\u1ebb t\u1ea5n c\u00f4ng (evil-site.com) kh\u00f4ng th\u1ec3 \u0111\u1ecdc ho\u1eb7c \u0111o\u00e1n \u0111\u01b0\u1ee3c gi\u00e1 tr\u1ecb c\u1ee7a CSRF token c\u1ee7a trang my-bank.com do ch\u00ednh s\u00e1ch Same-Origin Policy c\u1ee7a tr\u00ecnh duy\u1ec7t. V\u00ec v\u1eady, n\u00f3 kh\u00f4ng th\u1ec3 g\u1eedi m\u1ed9t request h\u1ee3p l\u1ec7 c\u00f3 ch\u1ee9a \u0111\u00fang CSRF token.<\/p>\n\n\n\n<p><strong>L\u01b0u \u00fd quan tr\u1ecdng:<\/strong> C\u01a1 ch\u1ebf ch\u1ed1ng CSRF n\u00e0y ch\u1ec9 th\u1ef1c s\u1ef1 c\u1ea7n thi\u1ebft cho c\u00e1c \u1ee9ng d\u1ee5ng <strong>stateful<\/strong> (s\u1eed d\u1ee5ng session\/cookie). \u0110\u1ed1i v\u1edbi c\u00e1c \u1ee9ng d\u1ee5ng <strong>stateless<\/strong> s\u1eed d\u1ee5ng JWT \u0111\u1ec3 x\u00e1c th\u1ef1c qua Authorization header, t\u1ea5n c\u00f4ng CSRF kh\u00f4ng c\u00f2n l\u00e0 m\u1ed9t m\u1ed1i \u0111e d\u1ecda l\u1edbn, v\u00e0 t\u00ednh n\u0103ng n\u00e0y th\u01b0\u1eddng s\u1ebd \u0111\u01b0\u1ee3c t\u1eaft (.csrf(csrf -&gt; csrf.disable())) \u0111\u1ec3 tr\u00e1nh c\u00e1c r\u1eafc r\u1ed1i kh\u00f4ng c\u1ea7n thi\u1ebft.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-cau-h\u1ecfi-ph\u1ecfng-v\u1ea5n-v\u1ec1-microservices-testing-amp-ki\u1ebfn-th\u1ee9c-nang-cao\"><span class=\"ez-toc-section\" id=\"Cau_hoi_phong_van_ve_microservices_testing_kien_thuc_nang_cao\"><\/span><strong>C\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n v\u1ec1 microservices, testing &amp; ki\u1ebfn th\u1ee9c n\u00e2ng cao<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Ph\u1ea7n n\u00e0y d\u00e0nh cho c\u00e1c \u1ee9ng vi\u00ean c\u00f3 kinh nghi\u1ec7m, t\u1eadp trung v\u00e0o ki\u1ebfn tr\u00fac h\u1ec7 th\u1ed1ng, kh\u1ea3 n\u0103ng \u0111\u1ea3m b\u1ea3o ch\u1ea5t l\u01b0\u1ee3ng v\u00e0 c\u00e1c ch\u1ee7 \u0111\u1ec1 n\u00e2ng cao.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-36-spring-boot-actuator-la-gi-no-cung-c\u1ea5p-nh\u1eefng-endpoint-h\u1eefu-ich-nao\"><strong>36. Spring Boot Actuator l\u00e0 g\u00ec? N\u00f3 cung c\u1ea5p nh\u1eefng endpoint h\u1eefu \u00edch n\u00e0o?\u00a0<\/strong><\/h3>\n\n\n\n<p>Spring Boot Actuator l\u00e0 m\u1ed9t sub-project c\u1ee7a Spring Boot, cung c\u1ea5p c\u00e1c t\u00ednh n\u0103ng s\u1eb5n c\u00f3 \u0111\u1ec3 <strong>gi\u00e1m s\u00e1t (monitor) v\u00e0 qu\u1ea3n l\u00fd (manage)<\/strong> \u1ee9ng d\u1ee5ng c\u1ee7a m\u00ecnh khi n\u00f3 \u0111ang ch\u1ea1y \u1edf m\u00f4i tr\u01b0\u1eddng production.<\/p>\n\n\n\n<p>Khi ta th\u00eam dependency spring-boot-starter-actuator v\u00e0o d\u1ef1 \u00e1n, n\u00f3 s\u1ebd t\u1ef1 \u0111\u1ed9ng cung c\u1ea5p m\u1ed9t lo\u1ea1t c\u00e1c endpoint qua HTTP ho\u1eb7c JMX. C\u00e1c endpoint n\u00e0y gi\u00fap c\u00e1c DevOps ho\u1eb7c L\u1eadp tr\u00ecnh vi\u00ean c\u00f3 th\u1ec3 &#8220;nh\u00ecn&#8221; v\u00e0o b\u00ean trong \u1ee9ng d\u1ee5ng \u0111\u1ec3 ki\u1ec3m tra tr\u1ea1ng th\u00e1i, xem metrics, v\u00e0 thu th\u1eadp th\u00f4ng tin m\u00e0 kh\u00f4ng c\u1ea7n ph\u1ea3i \u0111\u1ecdc log hay debug tr\u1ef1c ti\u1ebfp.<\/p>\n\n\n\n<p><strong>M\u1ed9t s\u1ed1 endpoint h\u1eefu \u00edch v\u00e0 ph\u1ed5 bi\u1ebfn nh\u1ea5t l\u00e0:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\/health:<\/strong> \u0110\u00e2y l\u00e0 endpoint quan tr\u1ecdng nh\u1ea5t. N\u00f3 cung c\u1ea5p th\u00f4ng tin t\u1ed5ng quan v\u1ec1 &#8220;s\u1ee9c kh\u1ecfe&#8221; c\u1ee7a \u1ee9ng d\u1ee5ng. Tr\u1ea1ng th\u00e1i chung s\u1ebd l\u00e0 UP n\u1ebfu m\u1ecdi th\u1ee9 \u0111\u1ec1u \u1ed5n. N\u00f3 c\u00f3 th\u1ec3 t\u00edch h\u1ee3p \u0111\u1ec3 ki\u1ec3m tra tr\u1ea1ng th\u00e1i c\u1ee7a c\u00e1c th\u00e0nh ph\u1ea7n kh\u00e1c nh\u01b0 k\u1ebft n\u1ed1i database, disk space, message broker&#8230; C\u00e1c c\u00f4ng c\u1ee5 nh\u01b0 Kubernetes th\u01b0\u1eddng d\u00f9ng endpoint n\u00e0y \u0111\u1ec3 th\u1ef1c hi\u1ec7n health check.<\/li>\n<\/ul>\n\n\n\n<p>V\u00ed d\u1ee5 response:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\"status\": \"UP\", \"components\": {\"db\": {\"status\": \"UP\"}, \"diskSpace\": {\"status\": \"UP\"}}}<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\/info:<\/strong> Hi\u1ec3n th\u1ecb c\u00e1c th\u00f4ng tin chung v\u1ec1 \u1ee9ng d\u1ee5ng do m\u00ecnh t\u1ef1 \u0111\u1ecbnh ngh\u0129a, v\u00ed d\u1ee5 nh\u01b0 t\u00ean \u1ee9ng d\u1ee5ng, phi\u00ean b\u1ea3n build, th\u00f4ng tin git commit\u2026<\/li>\n<\/ul>\n\n\n\n<p>V\u00ed d\u1ee5:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\"app\": {\"name\": \"My App\", \"version\": \"1.0.2-SNAPSHOT\"}}<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\/metrics: <\/strong>Cung c\u1ea5p c\u00e1c s\u1ed1 li\u1ec7u chi ti\u1ebft v\u1ec1 hi\u1ec7u n\u0103ng c\u1ee7a \u1ee9ng d\u1ee5ng, v\u00ed d\u1ee5 nh\u01b0 b\u1ed9 nh\u1edb (JVM memory usage), s\u1ed1 lu\u1ed3ng (threads), th\u1eddi gian x\u1eed l\u00fd request c\u1ee7a c\u00e1c endpoint (http.server.requests), t\u00e0i nguy\u00ean CPU&#8230; C\u00e1c h\u1ec7 th\u1ed1ng gi\u00e1m s\u00e1t nh\u01b0 Prometheus th\u01b0\u1eddng l\u1ea5y d\u1eef li\u1ec7u t\u1eeb \u0111\u00e2y \u0111\u1ec3 v\u1ebd bi\u1ec3u \u0111\u1ed3 v\u00e0 c\u1ea3nh b\u00e1o.<\/li>\n<\/ul>\n\n\n\n<p>V\u00ed d\u1ee5:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\"name\": \"jvm.memory.used\", \"measurements\": &#91;{\"statistic\": \"VALUE\", \"value\": 1.23E8}]}<\/code><\/pre>\n\n\n\n<p>Ngo\u00e0i ra c\u00f2n c\u00f3 c\u00e1c endpoint kh\u00e1c nh\u01b0 \/loggers (xem v\u00e0 thay \u0111\u1ed5i level log), \/env (xem c\u00e1c bi\u1ebfn m\u00f4i tr\u01b0\u1eddng), \/threaddump<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-37-trong-ki\u1ebfn-truc-microservices-api-gateway-va-service-discovery-vi-d\u1ee5-eureka-consul-co-vai-tro-gi\"><strong>37. Trong ki\u1ebfn tr\u00fac Microservices, API Gateway v\u00e0 Service Discovery (v\u00ed d\u1ee5: Eureka, Consul) c\u00f3 vai tr\u00f2 g\u00ec?<\/strong><\/h3>\n\n\n\n<p>Trong ki\u1ebfn tr\u00fac Microservices, API Gateway v\u00e0 Service Discovery l\u00e0 hai th\u00e0nh ph\u1ea7n h\u1ea1 t\u1ea7ng c\u1ef1c k\u1ef3 quan tr\u1ecdng, gi\u00fap h\u1ec7 th\u1ed1ng ho\u1ea1t \u0111\u1ed9ng m\u1ed9t c\u00e1ch linh ho\u1ea1t v\u00e0 c\u00f3 t\u1ed5 ch\u1ee9c.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>API Gateway (V\u00ed d\u1ee5: Spring Cloud Gateway, Netflix Zuul)<\/strong><\/li>\n<\/ul>\n\n\n\n<p><strong>API Gateway<\/strong> \u0111\u00f3ng vai tr\u00f2 l\u00e0 m\u1ed9t <strong>c\u1eeda ng\u00f5 (entry point) duy nh\u1ea5t<\/strong> cho t\u1ea5t c\u1ea3 c\u00e1c request t\u1eeb client (web, mobile app) \u0111i v\u00e0o h\u1ec7 th\u1ed1ng microservices.<\/p>\n\n\n\n<p>Vai tr\u00f2 ch\u00ednh c\u1ee7a n\u00f3 bao g\u1ed3m:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Routing (\u0110\u1ecbnh tuy\u1ebfn)<\/strong>: N\u00f3 nh\u1eadn request t\u1eeb client v\u00e0 \u0111\u1ecbnh tuy\u1ebfn \u0111\u1ebfn service ph\u00f9 h\u1ee3p \u1edf b\u00ean trong. Client kh\u00f4ng c\u1ea7n bi\u1ebft \u0111\u1ecba ch\u1ec9 c\u1ee7a t\u1eebng service, ch\u1ec9 c\u1ea7n bi\u1ebft \u0111\u1ecba ch\u1ec9 c\u1ee7a Gateway.<\/li>\n\n\n\n<li><strong>Single Point of Entry<\/strong>: Thay v\u00ec client ph\u1ea3i g\u1ecdi \u0111\u1ebfn 10 service kh\u00e1c nhau v\u1edbi 10 \u0111\u1ecba ch\u1ec9 IP\/port, gi\u1edd \u0111\u00e2y ch\u00fang ch\u1ec9 c\u1ea7n g\u1ecdi \u0111\u1ebfn 1 \u0111\u1ecba ch\u1ec9 duy nh\u1ea5t c\u1ee7a API Gateway.<\/li>\n\n\n\n<li><strong>Cross-Cutting Concerns (X\u1eed l\u00fd c\u00e1c m\u1ed1i quan t\u00e2m chung)<\/strong>: API Gateway l\u00e0 n\u01a1i l\u00fd t\u01b0\u1edfng \u0111\u1ec3 x\u1eed l\u00fd c\u00e1c logic chung cho to\u00e0n h\u1ec7 th\u1ed1ng, gi\u00fap c\u00e1c microservice b\u00ean trong ch\u1ec9 t\u1eadp trung v\u00e0o nghi\u1ec7p v\u1ee5 c\u1ee7a m\u00ecnh. C\u00e1c logic \u0111\u00f3 l\u00e0:<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Authentication &amp; Authorization<\/strong>: X\u00e1c th\u1ef1c ng\u01b0\u1eddi d\u00f9ng (v\u00ed d\u1ee5: ki\u1ec3m tra JWT) v\u00e0 ki\u1ec3m tra quy\u1ec1n h\u1ea1n tr\u01b0\u1edbc khi cho request \u0111i v\u00e0o b\u00ean trong.<\/li>\n\n\n\n<li><strong>Rate Limiting<\/strong>: Gi\u1edbi h\u1ea1n s\u1ed1 l\u01b0\u1ee3ng request t\u1eeb m\u1ed9t client \u0111\u1ec3 ch\u1ed1ng t\u1ea5n c\u00f4ng DoS.<\/li>\n\n\n\n<li><strong>Logging &amp; Monitoring<\/strong>: Ghi log v\u00e0 theo d\u00f5i t\u1ea5t c\u1ea3 c\u00e1c request \u0111i v\u00e0o h\u1ec7 th\u1ed1ng.<\/li>\n\n\n\n<li><strong>Response Caching<\/strong>: Cache l\u1ea1i c\u00e1c response th\u01b0\u1eddng xuy\u00ean \u0111\u01b0\u1ee3c y\u00eau c\u1ea7u.<\/li>\n\n\n\n<li><strong>Service Discovery (V\u00ed d\u1ee5: Eureka, Consul)<\/strong><\/li>\n<\/ul>\n\n\n\n<p>Service Discovery \u0111\u00f3ng vai tr\u00f2 nh\u01b0 m\u1ed9t &#8220;cu\u1ed1n danh b\u1ea1 \u0111i\u1ec7n tho\u1ea1i&#8221; \u0111\u1ed9ng cho c\u00e1c microservice.<\/p>\n\n\n\n<p>Trong m\u00f4i tr\u01b0\u1eddng microservices, c\u00e1c service c\u00f3 th\u1ec3 \u0111\u01b0\u1ee3c kh\u1edfi ch\u1ea1y, t\u1eaft \u0111i, ho\u1eb7c di chuy\u1ec3n li\u00ean t\u1ee5c (v\u00ed d\u1ee5: do auto-scaling, deploy phi\u00ean b\u1ea3n m\u1edbi, ho\u1eb7c l\u1ed7i). \u0110\u1ecba ch\u1ec9 IP v\u00e0 port c\u1ee7a ch\u00fang l\u00e0 kh\u00f4ng c\u1ed1 \u0111\u1ecbnh.<\/p>\n\n\n\n<p>Vai tr\u00f2 c\u1ee7a Service Discovery l\u00e0:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Service Registration (\u0110\u0103ng k\u00fd)<\/strong>: Khi m\u1ed9t instance c\u1ee7a microservice (v\u00ed d\u1ee5: product-service) kh\u1edfi \u0111\u1ed9ng, n\u00f3 s\u1ebd t\u1ef1 \u0111\u1ed9ng &#8220;g\u1ecdi \u0111i\u1ec7n&#8221; \u0111\u1ebfn Service Discovery server v\u00e0 \u0111\u0103ng k\u00fd th\u00f4ng tin c\u1ee7a m\u00ecnh, bao g\u1ed3m t\u00ean service, \u0111\u1ecba ch\u1ec9 IP v\u00e0 port.<\/li>\n\n\n\n<li><strong>Service Discovery (Kh\u00e1m ph\u00e1)<\/strong>: Khi order-service c\u1ea7n g\u1ecdi \u0111\u1ebfn product-service, thay v\u00ec hardcode \u0111\u1ecba ch\u1ec9, n\u00f3 s\u1ebd h\u1ecfi Service Discovery server: &#8220;Cho t\u00f4i xin \u0111\u1ecba ch\u1ec9 c\u1ee7a m\u1ed9t instance product-service \u0111ang ho\u1ea1t \u0111\u1ed9ng?&#8221;.<\/li>\n\n\n\n<li><strong>Health Check<\/strong>: Service Discovery server s\u1ebd li\u00ean t\u1ee5c ki\u1ec3m tra &#8220;s\u1ee9c kh\u1ecfe&#8221; c\u1ee7a c\u00e1c instance \u0111\u00e3 \u0111\u0103ng k\u00fd. N\u1ebfu m\u1ed9t instance n\u00e0o \u0111\u00f3 kh\u00f4ng ph\u1ea3n h\u1ed3i, n\u00f3 s\u1ebd b\u1ecb lo\u1ea1i kh\u1ecfi danh b\u1ea1 \u0111\u1ec3 c\u00e1c service kh\u00e1c kh\u00f4ng g\u1ecdi \u0111\u1ebfn n\u00f3 n\u1eefa.<\/li>\n<\/ul>\n\n\n\n<p><strong>T\u00f3m l\u1ea1i:<\/strong> API Gateway l\u00e0 c\u1ed5ng v\u00e0o cho th\u1ebf gi\u1edbi b\u00ean ngo\u00e0i, c\u00f2n Service Discovery gi\u00fap c\u00e1c service \u1edf th\u1ebf gi\u1edbi b\u00ean trong c\u00f3 th\u1ec3 t\u00ecm th\u1ea5y v\u00e0 n\u00f3i chuy\u1ec7n \u0111\u01b0\u1ee3c v\u1edbi nhau.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-38-resttemplate-va-webclient-dung-d\u1ec3-lam-gi-t\u1ea1i-sao-webclient-d\u01b0\u1ee3c-khuy\u1ebfn-khich-s\u1eed-d\u1ee5ng-trong-cac-\u1ee9ng-d\u1ee5ng-m\u1edbi\"><strong>38. RestTemplate v\u00e0 WebClient d\u00f9ng \u0111\u1ec3 l\u00e0m g\u00ec? T\u1ea1i sao WebClient \u0111\u01b0\u1ee3c khuy\u1ebfn kh\u00edch s\u1eed d\u1ee5ng trong c\u00e1c \u1ee9ng d\u1ee5ng m\u1edbi?<\/strong><\/h3>\n\n\n\n<p>C\u1ea3 RestTemplate v\u00e0 WebClient \u0111\u1ec1u l\u00e0 c\u00e1c HTTP client trong Spring, d\u00f9ng \u0111\u1ec3 gi\u00fap m\u1ed9t service c\u00f3 th\u1ec3 g\u1ecdi \u0111\u1ebfn c\u00e1c API c\u1ee7a service kh\u00e1c.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>RestTemplate <\/strong>l\u00e0 HTTP client truy\u1ec1n th\u1ed1ng c\u1ee7a Spring. N\u00f3 ho\u1ea1t \u0111\u1ed9ng theo c\u01a1 ch\u1ebf <strong>\u0111\u1ed3ng b\u1ed9 (synchronous) v\u00e0 ch\u1eb7n (blocking)<\/strong>. Ngh\u0129a l\u00e0 khi restTemplate.getForObject(&#8230;) \u0111\u01b0\u1ee3c g\u1ecdi, thread hi\u1ec7n t\u1ea1i s\u1ebd b\u1ecb block v\u00e0 ph\u1ea3i ch\u1edd cho \u0111\u1ebfn khi nh\u1eadn \u0111\u01b0\u1ee3c response t\u1eeb service kia.<\/li>\n\n\n\n<li><strong>WebClient<\/strong> l\u00e0 HTTP client hi\u1ec7n \u0111\u1ea1i, l\u00e0 m\u1ed9t ph\u1ea7n c\u1ee7a Spring WebFlux framework. N\u00f3 ho\u1ea1t \u0111\u1ed9ng theo c\u01a1 ch\u1ebf <strong>b\u1ea5t \u0111\u1ed3ng b\u1ed9 (asynchronous) v\u00e0 kh\u00f4ng ch\u1eb7n (non-blocking)<\/strong>. Khi webClient.get().retrieve().bodyToMono(&#8230;) \u0111\u01b0\u1ee3c g\u1ecdi, n\u00f3 s\u1ebd kh\u00f4ng block thread hi\u1ec7n t\u1ea1i. Thay v\u00e0o \u0111\u00f3, n\u00f3 tr\u1ea3 v\u1ec1 m\u1ed9t Publisher (c\u1ee5 th\u1ec3 l\u00e0 Mono ho\u1eb7c Flux). Vi\u1ec7c x\u1eed l\u00fd response s\u1ebd \u0111\u01b0\u1ee3c th\u1ef1c hi\u1ec7n sau khi d\u1eef li\u1ec7u th\u1ef1c s\u1ef1 tr\u1ea3 v\u1ec1, th\u00f4ng qua c\u00e1c callback.<\/li>\n<\/ul>\n\n\n\n<p><strong>T\u1ea1i sao WebClient \u0111\u01b0\u1ee3c khuy\u1ebfn kh\u00edch s\u1eed d\u1ee5ng?<\/strong><\/p>\n\n\n\n<p>L\u00fd do ch\u00ednh l\u00e0 v\u00ec c\u00e1ch n\u00f3 qu\u1ea3n l\u00fd t\u00e0i nguy\u00ean, \u0111\u1eb7c bi\u1ec7t l\u00e0 thread, hi\u1ec7u qu\u1ea3 h\u01a1n r\u1ea5t nhi\u1ec1u, gi\u00fap t\u0103ng kh\u1ea3 n\u0103ng ch\u1ecbu t\u1ea3i (throughput) c\u1ee7a \u1ee9ng d\u1ee5ng.<\/p>\n\n\n\n<p>H\u00e3y t\u01b0\u1edfng t\u01b0\u1ee3ng m\u1ed9t service c\u1ea7n g\u1ecdi \u0111\u1ed3ng th\u1eddi 100 API kh\u00e1c.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>N\u1ebfu d\u00f9ng <strong>RestTemplate<\/strong>, n\u00f3 s\u1ebd c\u1ea7n 100 thread \u0111\u1ec3 th\u1ef1c hi\u1ec7n 100 l\u1eddi g\u1ecdi n\u00e0y. M\u1ed7i thread s\u1ebd b\u1ecb block cho \u0111\u1ebfn khi c\u00f3 k\u1ebft qu\u1ea3. N\u1ebfu h\u1ec7 th\u1ed1ng c\u00f3 nhi\u1ec1u request t\u01b0\u01a1ng t\u1ef1, s\u1ed1 l\u01b0\u1ee3ng thread s\u1ebd t\u0103ng l\u00ean r\u1ea5t nhanh v\u00e0 tr\u1edf th\u00e0nh \u0111i\u1ec3m ngh\u1ebdn c\u1ee7a h\u1ec7 th\u1ed1ng.<\/li>\n\n\n\n<li>N\u1ebfu d\u00f9ng <strong>WebClient<\/strong>, n\u00f3 ch\u1ec9 c\u1ea7n m\u1ed9t s\u1ed1 l\u01b0\u1ee3ng nh\u1ecf c\u00e1c thread (event loop thread) \u0111\u1ec3 qu\u1ea3n l\u00fd t\u1ea5t c\u1ea3 100 l\u1eddi g\u1ecdi n\u00e0y. Khi m\u1ed9t l\u1eddi g\u1ecdi \u0111\u01b0\u1ee3c g\u1eedi \u0111i, thread \u0111\u00f3 \u0111\u01b0\u1ee3c gi\u1ea3i ph\u00f3ng \u0111\u1ec3 l\u00e0m vi\u1ec7c kh\u00e1c. Khi c\u00f3 response tr\u1ea3 v\u1ec1, m\u1ed9t thread s\u1ebd \u0111\u01b0\u1ee3c d\u00f9ng \u0111\u1ec3 x\u1eed l\u00fd k\u1ebft qu\u1ea3.<\/li>\n<\/ul>\n\n\n\n<p><strong>T\u00f3m l\u1ea1i<\/strong>: WebClient \u0111\u01b0\u1ee3c khuy\u1ebfn kh\u00edch cho c\u00e1c \u1ee9ng d\u1ee5ng m\u1edbi, \u0111\u1eb7c bi\u1ec7t l\u00e0 trong ki\u1ebfn tr\u00fac microservices (n\u01a1i vi\u1ec7c g\u1ecdi qua l\u1ea1i gi\u1eefa c\u00e1c service di\u1ec5n ra th\u01b0\u1eddng xuy\u00ean) v\u00ec n\u00f3 s\u1eed d\u1ee5ng t\u00e0i nguy\u00ean hi\u1ec7u qu\u1ea3 h\u01a1n, cho ph\u00e9p \u1ee9ng d\u1ee5ng x\u1eed l\u00fd nhi\u1ec1u request \u0111\u1ed3ng th\u1eddi h\u01a1n v\u1edbi c\u00f9ng m\u1ed9t l\u01b0\u1ee3ng t\u00e0i nguy\u00ean ph\u1ea7n c\u1ee9ng.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-39-phan-bi\u1ec7t-unit-test-va-integration-test\"><strong>39. Ph\u00e2n bi\u1ec7t Unit Test v\u00e0 Integration Test.\u00a0<\/strong><\/h3>\n\n\n\n<p>Unit Test v\u00e0 Integration Test l\u00e0 hai c\u1ea5p \u0111\u1ed9 ki\u1ec3m th\u1eed kh\u00e1c nhau trong kim t\u1ef1 th\u00e1p ki\u1ec3m th\u1eed. C\u00e1c \u0111i\u1ec3m kh\u00e1c nhau ch\u00ednh:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Ti\u00eau ch\u00ed<\/strong><\/td><td><strong>Unit Test<\/strong><\/td><td><strong>Integration Test<\/strong><\/td><\/tr><tr><td><strong>Ph\u1ea1m vi<\/strong><\/td><td>Nh\u1ecf v\u00e0 c\u00f4 l\u1eadp: Ch\u1ec9 ki\u1ec3m th\u1eed m\u1ed9t \u0111\u01a1n v\u1ecb code duy nh\u1ea5t (m\u1ed9t method ho\u1eb7c m\u1ed9t class) m\u1ed9t c\u00e1ch \u0111\u1ed9c l\u1eadp.<\/td><td>R\u1ed9ng: Ki\u1ec3m th\u1eed s\u1ef1 t\u01b0\u01a1ng t\u00e1c v\u00e0 ho\u1ea1t \u0111\u1ed9ng ph\u1ed1i h\u1ee3p c\u1ee7a nhi\u1ec1u th\u00e0nh ph\u1ea7n v\u1edbi nhau.<\/td><\/tr><tr><td><strong>Dependencies<\/strong><\/td><td>\u0110\u01b0\u1ee3c &#8220;mock&#8221; (gi\u1ea3 l\u1eadp). M\u1ecdi ph\u1ee5 thu\u1ed9c b\u00ean ngo\u00e0i nh\u01b0 database, API kh\u00e1c, file system&#8230; \u0111\u1ec1u \u0111\u01b0\u1ee3c thay th\u1ebf b\u1eb1ng c\u00e1c \u0111\u1ed1i t\u01b0\u1ee3ng gi\u1ea3.<\/td><td>S\u1eed d\u1ee5ng dependencies th\u1eadt ho\u1eb7c c\u00e1c phi\u00ean b\u1ea3n test g\u1ea7n gi\u1ed1ng th\u1eadt (v\u00ed d\u1ee5: database trong b\u1ed9 nh\u1edb H2, Testcontainers).<\/td><\/tr><tr><td><strong>T\u1ed1c \u0111\u1ed9<\/strong><\/td><td>R\u1ea5t nhanh. C\u00f3 th\u1ec3 ch\u1ea1y h\u00e0ng tr\u0103m, h\u00e0ng ngh\u00ecn test trong v\u00e0i gi\u00e2y.<\/td><td>Ch\u1eadm h\u01a1n v\u00ec ph\u1ea3i kh\u1edfi \u0111\u1ed9ng nhi\u1ec1u th\u00e0nh ph\u1ea7n, k\u1ebft n\u1ed1i database&#8230;<\/td><\/tr><tr><td><strong>M\u1ee5c \u0111\u00edch<\/strong><\/td><td>\u0110\u1ea3m b\u1ea3o m\u1ed9t \u0111\u01a1n v\u1ecb code ho\u1ea1t \u0111\u1ed9ng \u0111\u00fang nh\u01b0 thi\u1ebft k\u1ebf.<\/td><td>\u0110\u1ea3m b\u1ea3o c\u00e1c th\u00e0nh ph\u1ea7n khi k\u1ebft h\u1ee3p l\u1ea1i v\u1edbi nhau v\u1eabn ho\u1ea1t \u0111\u1ed9ng \u0111\u00fang<\/td><\/tr><tr><td><strong>V\u00ed d\u1ee5<\/strong><\/td><td>Test m\u1ed9t method t\u00ednh to\u00e1n trong Service. &#8211; Test logic validation trong m\u1ed9t class.<\/td><td>Test lu\u1ed3ng t\u1eeb Controller -&gt; Service -&gt; Repository. &#8211; Test vi\u1ec7c ghi v\u00e0 \u0111\u1ecdc d\u1eef li\u1ec7u t\u1eeb database.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-40-lam-th\u1ebf-nao-d\u1ec3-vi\u1ebft-m\u1ed9t-unit-test-cho-service-layer-b\u1eb1ng-mockito\"><strong>40. L\u00e0m th\u1ebf n\u00e0o \u0111\u1ec3 vi\u1ebft m\u1ed9t Unit Test cho Service Layer b\u1eb1ng Mockito?<\/strong><\/h3>\n\n\n\n<p>Gi\u1ea3 s\u1eed ta c\u00f3 m\u1ed9t UserService ph\u1ee5 thu\u1ed9c v\u00e0o UserRepository \u0111\u1ec3 t\u00ecm ng\u01b0\u1eddi d\u00f9ng:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Class c\u1ea7n test\npublic class UserService {\n    private final UserRepository userRepository;\n\n    public UserService(UserRepository userRepository) {\n        this.userRepository = userRepository;\n    }\n\n    public UserDto findUserById(Long id) {\n        User user = userRepository.findById(id)\n                .orElseThrow(() -&gt; new UserNotFoundException(\"User not found\"));\n        return convertToDto(user);\n    }\n}<\/code><\/pre>\n\n\n\n<p>T\u00f4i s\u1ebd vi\u1ebft Unit Test cho method findUserById b\u1eb1ng Mockito nh\u01b0 sau:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Thi\u1ebft l\u1eadp m\u00f4i tr\u01b0\u1eddng test<\/strong>: S\u1eed d\u1ee5ng JUnit 5 v\u00e0 Mockito.<\/li>\n\n\n\n<li><strong>Mock Dependency<\/strong>: ta s\u1ebd d\u00f9ng Mockito \u0111\u1ec3 t\u1ea1o m\u1ed9t \u0111\u1ed1i t\u01b0\u1ee3ng gi\u1ea3 (mock object) c\u1ee7a UserRepository. ta kh\u00f4ng mu\u1ed1n test k\u1ebft n\u1ed1i \u0111\u1ebfn database th\u1eadt, ch\u1ec9 mu\u1ed1n ki\u1ec3m tra logic c\u1ee7a UserService.<\/li>\n\n\n\n<li><strong>\u0110\u1ecbnh ngh\u0129a h\u00e0nh vi cho Mock<\/strong>: ta s\u1ebd &#8220;d\u1ea1y&#8221; cho userRepository mock bi\u1ebft ph\u1ea3i tr\u1ea3 v\u1ec1 c\u00e1i g\u00ec khi method findById() c\u1ee7a n\u00f3 \u0111\u01b0\u1ee3c g\u1ecdi.<\/li>\n\n\n\n<li><strong>G\u1ecdi ph\u01b0\u01a1ng th\u1ee9c c\u1ea7n test<\/strong>: G\u1ecdi userService.findUserById() v\u1edbi \u0111\u1ed1i t\u01b0\u1ee3ng userService \u0111\u00e3 \u0111\u01b0\u1ee3c ti\u00eam (inject) mock repository.<\/li>\n\n\n\n<li><strong>Ki\u1ec3m tra k\u1ebft qu\u1ea3 (Assertion)<\/strong>: D\u00f9ng c\u00e1c h\u00e0m c\u1ee7a AssertJ ho\u1eb7c JUnit \u0111\u1ec3 ki\u1ec3m tra xem k\u1ebft qu\u1ea3 tr\u1ea3 v\u1ec1 c\u00f3 \u0111\u00fang nh\u01b0 mong \u0111\u1ee3i kh\u00f4ng.<\/li>\n\n\n\n<li><strong>Ki\u1ec3m tra t\u01b0\u01a1ng t\u00e1c (Verification)<\/strong>: \u0110\u1ea3m b\u1ea3o r\u1eb1ng method findById() c\u1ee7a mock repository \u0111\u00e3 th\u1ef1c s\u1ef1 \u0111\u01b0\u1ee3c g\u1ecdi.<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ File test: UserServiceTest.java\n@ExtendWith(MockitoExtension.class) \/\/ T\u00edch h\u1ee3p Mockito v\u1edbi JUnit 5\nclass UserServiceTest {\n\n    @Mock \/\/ 2. T\u1ea1o m\u1ed9t mock object cho UserRepository\n    private UserRepository userRepository;\n\n    @InjectMocks \/\/ T\u1ef1 \u0111\u1ed9ng inject c\u00e1c mock \u1edf tr\u00ean v\u00e0o \u0111\u1ed1i t\u01b0\u1ee3ng n\u00e0y\n    private UserService userService;\n\n    @Test\n    void whenFindUserById_givenUserExists_thenReturnUserDto() {\n        \/\/ 1. ARRANGE (S\u1eafp \u0111\u1eb7t)\n        User user = new User(1L, \"John Doe\", \"john@example.com\");\n        UserDto expectedDto = new UserDto(1L, \"John Doe\");\n\n        \/\/ 3. \u0110\u1ecbnh ngh\u0129a h\u00e0nh vi cho mock\n        \/\/ Khi userRepository.findById(1L) \u0111\u01b0\u1ee3c g\u1ecdi, h\u00e3y tr\u1ea3 v\u1ec1 m\u1ed9t Optional ch\u1ee9a user\n        when(userRepository.findById(1L)).thenReturn(Optional.of(user));\n\n        \/\/ 2. ACT (H\u00e0nh \u0111\u1ed9ng)\n        \/\/ 4. G\u1ecdi ph\u01b0\u01a1ng th\u1ee9c c\u1ea7n test\n        UserDto actualDto = userService.findUserById(1L);\n\n        \/\/ 3. ASSERT (Ki\u1ec3m tra)\n        \/\/ 5. Ki\u1ec3m tra k\u1ebft qu\u1ea3\n        assertThat(actualDto).isNotNull();\n        assertThat(actualDto.getId()).isEqualTo(expectedDto.getId());\n        assertThat(actualDto.getName()).isEqualTo(expectedDto.getName());\n\n        \/\/ 6. Ki\u1ec3m tra t\u01b0\u01a1ng t\u00e1c (t\u00f9y ch\u1ecdn nh\u01b0ng n\u00ean c\u00f3)\n        \/\/ \u0110\u1ea3m b\u1ea3o r\u1eb1ng method findById(1L) c\u1ee7a repository \u0111\u00e3 \u0111\u01b0\u1ee3c g\u1ecdi \u0111\u00fang 1 l\u1ea7n\n        verify(userRepository, times(1)).findById(1L);\n    }\n    \n    @Test\n    void whenFindUserById_givenUserDoesNotExist_thenThrowException() {\n        \/\/ ARRANGE\n        when(userRepository.findById(anyLong())).thenReturn(Optional.empty());\n        \n        \/\/ ACT &amp; ASSERT\n        assertThatThrownBy(() -&gt; userService.findUserById(99L))\n            .isInstanceOf(UserNotFoundException.class)\n            .hasMessage(\"User not found\");\n    }\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-41-so-sanh-va-gi\u1ea3i-thich-tr\u01b0\u1eddng-h\u1ee3p-s\u1eed-d\u1ee5ng-c\u1ee7a-cac-annotation-testing-springboottest-webmvctest-va-datajpatest\"><strong>41. So s\u00e1nh v\u00e0 gi\u1ea3i th\u00edch tr\u01b0\u1eddng h\u1ee3p s\u1eed d\u1ee5ng c\u1ee7a c\u00e1c annotation testing: @SpringBootTest, @WebMvcTest, v\u00e0 @DataJpaTest<\/strong><\/h3>\n\n\n\n<p>Ba annotation n\u00e0y \u0111\u1ec1u d\u00f9ng \u0111\u1ec3 vi\u1ebft test trong Spring Boot, nh\u01b0ng ch\u00fang<strong> kh\u00e1c nhau \u1edf ph\u1ea1m vi c\u1ee7a Application Context<\/strong> m\u00e0 ch\u00fang kh\u1edfi t\u1ea1o, gi\u00fap t\u1ed1i \u01b0u h\u00f3a vi\u1ec7c test cho t\u1eebng layer c\u1ee5 th\u1ec3.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Annotation<\/strong><\/td><td><strong>@SpringBootTest<\/strong><\/td><td><strong>@WebMvcTest<\/strong><\/td><td><strong>@DataJpaTest<\/strong><\/td><\/tr><tr><td><strong>M\u1ee5c \u0111\u00edch<\/strong><\/td><td>Integration Test to\u00e0n di\u1ec7n.<\/td><td>Unit Test cho Web Layer.<\/td><td>Unit\/Integration Test cho Persistence Layer.<\/td><\/tr><tr><td><strong>Context \u0111\u01b0\u1ee3c t\u1ea3i<\/strong><\/td><td>To\u00e0n b\u1ed9 Application Context. T\u1ea3i t\u1ea5t c\u1ea3 c\u00e1c bean, gi\u1ed1ng nh\u01b0 khi ch\u1ea1y \u1ee9ng d\u1ee5ng th\u1eadt.<\/td><td>Ch\u1ec9 Web Layer: Controllers, @ControllerAdvice, Filters, WebMvcConfigurer&#8230; Kh\u00f4ng t\u1ea3i @Service, @Repository.<\/td><td>Ch\u1ec9 Persistence Layer: @Repository, Entities, EntityManager&#8230; Kh\u00f4ng t\u1ea3i @Service, @Controller.<\/td><\/tr><tr><td><strong>T\u1ed1c \u0111\u1ed9<\/strong><\/td><td>Ch\u1eadm nh\u1ea5t (v\u00ec ph\u1ea3i t\u1ea3i m\u1ecdi th\u1ee9).<\/td><td>Nhanh.<\/td><td>Nhanh.<\/td><\/tr><tr><td><strong>Dependencies<\/strong><\/td><td>Th\u01b0\u1eddng d\u00f9ng dependencies th\u1eadt ho\u1eb7c Testcontainers.<\/td><td>C\u00e1c service\/repository ph\u1ee5 thu\u1ed9c ph\u1ea3i \u0111\u01b0\u1ee3c mock (d\u00f9ng @MockBean).<\/td><td>M\u1eb7c \u0111\u1ecbnh s\u1eed d\u1ee5ng in-memory database (nh\u01b0 H2) v\u00e0 t\u1ef1 \u0111\u1ed9ng rollback transaction sau m\u1ed7i test.<\/td><\/tr><tr><td><strong>Tr\u01b0\u1eddng h\u1ee3p s\u1eed d\u1ee5ng<\/strong><\/td><td>Test lu\u1ed3ng ch\u1ea1y t\u1eeb \u0111\u1ea7u \u0111\u1ebfn cu\u1ed1i (end-to-end). &#8211; Test s\u1ef1 t\u00edch h\u1ee3p gi\u1eefa Service, Repository v\u00e0 Database th\u1eadt. &#8211; Ki\u1ec3m tra vi\u1ec7c load configuration, profile c\u00f3 \u0111\u00fang kh\u00f4ng.<\/td><td>Test logic c\u1ee7a Controller (validation, request mapping, response status&#8230;). &#8211; Test security rules \u1edf t\u1ea7ng web. &#8211; Test JSON serialization\/deserialization.<\/td><td>Test c\u00e1c ph\u01b0\u01a1ng th\u1ee9c custom query trong Repository. &#8211; Test c\u00e1c m\u1ed1i quan h\u1ec7 Entity, cascading. &#8211; Ki\u1ec3m tra vi\u1ec7c l\u01b0u v\u00e0 \u0111\u1ecdc d\u1eef li\u1ec7u c\u00f3 \u0111\u00fang kh\u00f4ng.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p><strong>T\u00f3m l\u1ea1i:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Khi mu\u1ed1n test m\u1ed9t lu\u1ed3ng ho\u00e0n ch\u1ec9nh, v\u00ed d\u1ee5 nh\u01b0 &#8220;g\u1eedi m\u1ed9t request HTTP v\u00e0 ki\u1ec3m tra xem d\u1eef li\u1ec7u c\u00f3 \u0111\u01b0\u1ee3c l\u01b0u \u0111\u00fang v\u00e0o DB kh\u00f4ng?&#8221;, h\u00e3y d\u00f9ng <strong>@SpringBootTest<\/strong>.<\/li>\n\n\n\n<li>Khi ch\u1ec9 mu\u1ed1n test Controller, v\u00ed d\u1ee5 nh\u01b0 &#8220;g\u1eedi m\u1ed9t request \u0111\u1ebfn endpoint X th\u00ec c\u00f3 tr\u1ea3 v\u1ec1 status 200 v\u00e0 JSON \u0111\u00fang \u0111\u1ecbnh d\u1ea1ng kh\u00f4ng?&#8221;, h\u00e3y d\u00f9ng <strong>@WebMvcTest<\/strong> v\u00e0 mock c\u00e1c service ph\u1ee5 thu\u1ed9c.<\/li>\n\n\n\n<li>Khi ch\u1ec9 mu\u1ed1n test Repository, v\u00ed d\u1ee5 nh\u01b0 &#8220;ph\u01b0\u01a1ng th\u1ee9c findByEmail() c\u00f3 tr\u1ea3 v\u1ec1 \u0111\u00fang User kh\u00f4ng?&#8221;, h\u00e3y d\u00f9ng <strong>@DataJpaTest<\/strong>.<\/li>\n<\/ul>\n\n\n\n<p>Vi\u1ec7c l\u1ef1a ch\u1ecdn \u0111\u00fang annotation s\u1ebd gi\u00fap b\u1ed9 test c\u1ee7a m\u00ecnh ch\u1ea1y nhanh h\u01a1n v\u00e0 t\u1eadp trung h\u01a1n v\u00e0o \u0111\u00fang \u0111\u1ed1i t\u01b0\u1ee3ng c\u1ea7n ki\u1ec3m th\u1eed.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-42-circuit-breaker-pattern-la-gi-vi-d\u1ee5-resilience4j-gi\u1ea3i-quy\u1ebft-v\u1ea5n-d\u1ec1-gi-trong-h\u1ec7-th\u1ed1ng-microservices\"><strong>42. Circuit Breaker Pattern l\u00e0 g\u00ec? (v\u00ed d\u1ee5: Resilience4j). Gi\u1ea3i quy\u1ebft v\u1ea5n \u0111\u1ec1 g\u00ec trong h\u1ec7 th\u1ed1ng microservices?<\/strong><\/h3>\n\n\n\n<p><strong>Circuit Breaker<\/strong> l\u00e0 m\u1ed9t design pattern d\u00f9ng \u0111\u1ec3 x\u00e2y d\u1ef1ng c\u00e1c h\u1ec7 th\u1ed1ng c\u00f3 kh\u1ea3 n\u0103ng ch\u1ecbu l\u1ed7i (fault-tolerant). T\u00ean c\u1ee7a n\u00f3 \u0111\u01b0\u1ee3c l\u1ea5y c\u1ea3m h\u1ee9ng t\u1eeb c\u00e1i c\u1ea7u dao (circuit breaker) trong h\u1ec7 th\u1ed1ng \u0111i\u1ec7n.<\/p>\n\n\n\n<p><strong>V\u1ea5n \u0111\u1ec1 c\u1ea7n gi\u1ea3i quy\u1ebft:<\/strong><\/p>\n\n\n\n<p>Trong ki\u1ebfn tr\u00fac microservices, c\u00e1c service g\u1ecdi l\u1eabn nhau li\u00ean t\u1ee5c. N\u1ebfu m\u1ed9t service B b\u1ecb ch\u1eadm ho\u1eb7c l\u1ed7i, c\u00e1c service A g\u1ecdi \u0111\u1ebfn B s\u1ebd ph\u1ea3i ch\u1edd \u0111\u1ee3i (c\u00f3 th\u1ec3 \u0111\u1ebfn khi timeout). N\u1ebfu c\u00f3 nhi\u1ec1u request \u0111\u1ebfn service A, s\u1ed1 l\u01b0\u1ee3ng thread b\u1ecb treo \u0111\u1ec3 ch\u1edd B s\u1ebd t\u0103ng l\u00ean, d\u1eabn \u0111\u1ebfn c\u1ea1n ki\u1ec7t t\u00e0i nguy\u00ean (thread pool) c\u1ee7a A. D\u1ea7n d\u1ea7n, l\u1ed7i t\u1eeb B s\u1ebd <strong>lan truy\u1ec1n (cascade)<\/strong> sang A, r\u1ed3i t\u1eeb A sang c\u00e1c service kh\u00e1c g\u1ecdi \u0111\u1ebfn A, g\u00e2y s\u1ee5p \u0111\u1ed5 c\u1ea3 m\u1ed9t h\u1ec7 th\u1ed1ng.<\/p>\n\n\n\n<p><strong>C\u00e1ch Circuit Breaker ho\u1ea1t \u0111\u1ed9ng:<\/strong><\/p>\n\n\n\n<p>Circuit Breaker Pattern ho\u1ea1t \u0111\u1ed9ng nh\u01b0 m\u1ed9t proxy gi\u00e1m s\u00e1t c\u00e1c l\u1eddi g\u1ecdi \u0111\u1ebfn m\u1ed9t service c\u00f3 nguy c\u01a1 b\u1ecb l\u1ed7i. N\u00f3 c\u00f3 3 tr\u1ea1ng th\u00e1i:<\/p>\n\n\n\n<p><strong>CLOSED<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u0110\u00e2y l\u00e0 tr\u1ea1ng th\u00e1i b\u00ecnh th\u01b0\u1eddng. M\u1ecdi request \u0111\u1ec1u \u0111\u01b0\u1ee3c cho ph\u00e9p \u0111i qua \u0111\u1ebfn service \u0111\u00edch.<\/li>\n\n\n\n<li>Circuit Breaker s\u1ebd \u0111\u1ebfm s\u1ed1 l\u1ea7n th\u1ea5t b\u1ea1i. N\u1ebfu t\u1ec9 l\u1ec7 l\u1ed7i trong m\u1ed9t kho\u1ea3ng th\u1eddi gian v\u01b0\u1ee3t qua m\u1ed9t ng\u01b0\u1ee1ng n\u00e0o \u0111\u00f3 (v\u00ed d\u1ee5: 50% l\u1ed7i trong 10 request g\u1ea7n nh\u1ea5t), n\u00f3 s\u1ebd &#8220;nh\u1ea3y&#8221; sang tr\u1ea1ng th\u00e1i OPEN.<\/li>\n<\/ul>\n\n\n\n<p><strong>OPEN<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Khi m\u1ea1ch &#8220;m\u1edf&#8221;, Circuit Breaker s\u1ebd t\u1eeb ch\u1ed1i ngay l\u1eadp t\u1ee9c t\u1ea5t c\u1ea3 c\u00e1c request \u0111\u1ebfn service \u0111\u00edch m\u00e0 kh\u00f4ng c\u1ea7n th\u1ef1c hi\u1ec7n l\u1eddi g\u1ecdi m\u1ea1ng. N\u00f3 s\u1ebd tr\u1ea3 v\u1ec1 l\u1ed7i ngay l\u1eadp t\u1ee9c (fail-fast).<\/li>\n\n\n\n<li>\u0110i\u1ec1u n\u00e0y gi\u00fap b\u1ea3o v\u1ec7 service g\u1ecdi (service A) kh\u1ecfi vi\u1ec7c b\u1ecb treo v\u00e0 c\u1ea1n ki\u1ec7t t\u00e0i nguy\u00ean.<\/li>\n\n\n\n<li>Quan tr\u1ecdng h\u01a1n, n\u00f3 cho service b\u1ecb l\u1ed7i (service B) c\u00f3 th\u1eddi gian \u0111\u1ec3 &#8220;ngh\u1ec9 ng\u01a1i&#8221; v\u00e0 ph\u1ee5c h\u1ed3i m\u00e0 kh\u00f4ng b\u1ecb qu\u00e1 t\u1ea3i b\u1edfi c\u00e1c request m\u1edbi.<\/li>\n\n\n\n<li>Sau m\u1ed9t kho\u1ea3ng th\u1eddi gian ch\u1edd (cooldown\/wait duration), Circuit Breaker s\u1ebd chuy\u1ec3n sang tr\u1ea1ng th\u00e1i HALF-OPEN.<\/li>\n<\/ul>\n\n\n\n<p><strong>HALF-OPEN<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u1ede tr\u1ea1ng th\u00e1i n\u00e0y, Circuit Breaker s\u1ebd cho ph\u00e9p m\u1ed9t s\u1ed1 l\u01b0\u1ee3ng request gi\u1edbi h\u1ea1n \u0111i qua \u0111\u1ec3 &#8220;th\u0103m d\u00f2&#8221; xem service \u0111\u00edch \u0111\u00e3 ph\u1ee5c h\u1ed3i hay ch\u01b0a.<\/li>\n\n\n\n<li>N\u1ebfu c\u00e1c request th\u1eed nghi\u1ec7m n\u00e0y th\u00e0nh c\u00f4ng, Circuit Breaker s\u1ebd k\u1ebft lu\u1eadn r\u1eb1ng service \u0111\u00e3 \u1ed5n \u0111\u1ecbnh v\u00e0 chuy\u1ec3n v\u1ec1 tr\u1ea1ng th\u00e1i CLOSED.<\/li>\n\n\n\n<li>N\u1ebfu ch\u00fang th\u1ea5t b\u1ea1i, n\u00f3 s\u1ebd quay l\u1ea1i tr\u1ea1ng th\u00e1i OPEN v\u00e0 b\u1eaft \u0111\u1ea7u l\u1ea1i th\u1eddi gian ch\u1edd.<\/li>\n<\/ul>\n\n\n\n<p>C\u00e1c th\u01b0 vi\u1ec7n nh\u01b0 <strong>Resilience4j<\/strong> hay Hystrix gi\u00fap tri\u1ec3n khai pattern n\u00e0y m\u1ed9t c\u00e1ch d\u1ec5 d\u00e0ng trong Spring Boot. Ngo\u00e0i ra, n\u00f3 th\u01b0\u1eddng \u0111\u01b0\u1ee3c k\u1ebft h\u1ee3p v\u1edbi c\u00e1c ph\u01b0\u01a1ng \u00e1n d\u1ef1 ph\u00f2ng (fallback), v\u00ed d\u1ee5: khi Circuit Breaker m\u1edf, thay v\u00ec b\u00e1o l\u1ed7i, h\u1ec7 th\u1ed1ng s\u1ebd tr\u1ea3 v\u1ec1 d\u1eef li\u1ec7u t\u1eeb cache ho\u1eb7c m\u1ed9t gi\u00e1 tr\u1ecb m\u1eb7c \u0111\u1ecbnh.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-43-trinh-bay-m\u1ed9t-chi\u1ebfn-l\u01b0\u1ee3c-caching-hi\u1ec7u-qu\u1ea3-cho-\u1ee9ng-d\u1ee5ng-spring-boot-vi-d\u1ee5-s\u1eed-d\u1ee5ng-redis\"><strong>43. Tr\u00ecnh b\u00e0y m\u1ed9t chi\u1ebfn l\u01b0\u1ee3c caching hi\u1ec7u qu\u1ea3 cho \u1ee9ng d\u1ee5ng Spring Boot (v\u00ed d\u1ee5: s\u1eed d\u1ee5ng Redis).\u00a0<\/strong><\/h3>\n\n\n\n<p>M\u1ed9t chi\u1ebfn l\u01b0\u1ee3c caching hi\u1ec7u qu\u1ea3 cho \u1ee9ng d\u1ee5ng Spring Boot th\u01b0\u1eddng bao g\u1ed3m vi\u1ec7c s\u1eed d\u1ee5ng m\u1ed9t distributed cache nh\u01b0 Redis v\u00e0 t\u1eadn d\u1ee5ng framework Spring Cache Abstraction.<\/p>\n\n\n\n<p>Chi\u1ebfn l\u01b0\u1ee3c chung \u0111\u1ec3 gi\u1ea3m t\u1ea3i \u0111\u00e1ng k\u1ec3 cho database, c\u1ea3i thi\u1ec7n latency v\u00e0 t\u0103ng kh\u1ea3 n\u0103ng ch\u1ecbu t\u1ea3i c\u1ee7a \u1ee9ng d\u1ee5ng:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>X\u00e1c \u0111\u1ecbnh d\u1eef li\u1ec7u c\u1ea7n cache<\/strong>: T\u00f4i s\u1ebd \u01b0u ti\u00ean cache nh\u1eefng d\u1eef li\u1ec7u:\n<ul class=\"wp-block-list\">\n<li>\u00cdt thay \u0111\u1ed5i nh\u01b0ng \u0111\u01b0\u1ee3c \u0111\u1ecdc th\u01b0\u1eddng xuy\u00ean (read-heavy), v\u00ed d\u1ee5: danh m\u1ee5c s\u1ea3n ph\u1ea9m, th\u00f4ng tin c\u1ea5u h\u00ecnh, th\u00f4ng tin h\u1ed3 s\u01a1 ng\u01b0\u1eddi d\u00f9ng.<\/li>\n\n\n\n<li>T\u1ed1n nhi\u1ec1u t\u00e0i nguy\u00ean \u0111\u1ec3 t\u00ednh to\u00e1n ho\u1eb7c truy v\u1ea5n.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Ch\u1ecdn Cache Provider<\/strong>: S\u1eed d\u1ee5ng Redis v\u00ec n\u00f3 l\u00e0 m\u1ed9t in-memory data store c\u1ef1c k\u1ef3 nhanh, h\u1ed7 tr\u1ee3 nhi\u1ec1u c\u1ea5u tr\u00fac d\u1eef li\u1ec7u v\u00e0 c\u00f3 th\u1ec3 ho\u1ea1t \u0111\u1ed9ng nh\u01b0 m\u1ed9t distributed cache, cho ph\u00e9p nhi\u1ec1u instance c\u1ee7a c\u00f9ng m\u1ed9t service chia s\u1ebb chung m\u1ed9t cache.<\/li>\n\n\n\n<li><strong>T\u00edch h\u1ee3p Spring Cache<\/strong>: B\u1eadt t\u00ednh n\u0103ng cache trong Spring Boot b\u1eb1ng annotation @EnableCaching v\u00e0 c\u1ea5u h\u00ecnh c\u00e1c th\u00f4ng s\u1ed1 k\u1ebft n\u1ed1i \u0111\u1ebfn Redis trong file application.properties.<\/li>\n\n\n\n<li><strong>\u00c1p d\u1ee5ng Caching Annotations<\/strong>: S\u1eed d\u1ee5ng c\u00e1c annotation c\u1ee7a Spring Cache (@Cacheable, @CachePut, @CacheEvict) \u0111\u1ec3 khai b\u00e1o logic caching m\u1ed9t c\u00e1ch t\u1ef1 \u0111\u1ed9ng tr\u00ean c\u00e1c method c\u1ee7a Service layer.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-44-cac-annotation-cacheable-cacheput-cacheevict-ho\u1ea1t-d\u1ed9ng-ra-sao\"><strong>44. C\u00e1c annotation @Cacheable, @CachePut, @CacheEvict ho\u1ea1t \u0111\u1ed9ng ra sao?<\/strong><\/h3>\n\n\n\n<p><strong>@Cacheable(cacheNames=&#8221;products&#8221;, key=&#8221;#id&#8221;)<\/strong>:<\/p>\n\n\n\n<p>Tr\u01b0\u1edbc khi th\u1ef1c thi method, Spring s\u1ebd ki\u1ec3m tra trong cache products xem c\u00f3 t\u1ed3n t\u1ea1i m\u1ed9t entry v\u1edbi key \u0111\u01b0\u1ee3c sinh ra t\u1eeb #id hay kh\u00f4ng.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Cache Hit (T\u00ecm th\u1ea5y): N\u1ebfu c\u00f3, k\u1ebft qu\u1ea3 s\u1ebd \u0111\u01b0\u1ee3c tr\u1ea3 v\u1ec1 tr\u1ef1c ti\u1ebfp t\u1eeb cache, v\u00e0 method s\u1ebd kh\u00f4ng \u0111\u01b0\u1ee3c th\u1ef1c thi.<\/li>\n\n\n\n<li>Cache Miss (Kh\u00f4ng t\u00ecm th\u1ea5y): N\u1ebfu kh\u00f4ng, method s\u1ebd \u0111\u01b0\u1ee3c th\u1ef1c thi. K\u1ebft qu\u1ea3 tr\u1ea3 v\u1ec1 t\u1eeb method s\u1ebd \u0111\u01b0\u1ee3c l\u01b0u v\u00e0o cache v\u1edbi key t\u01b0\u01a1ng \u1ee9ng tr\u01b0\u1edbc khi \u0111\u01b0\u1ee3c tr\u1ea3 v\u1ec1 cho ng\u01b0\u1eddi g\u1ecdi.<\/li>\n<\/ul>\n\n\n\n<p>Tr\u01b0\u1eddng h\u1ee3p s\u1eed d\u1ee5ng: D\u00f9ng cho c\u00e1c method l\u1ea5y d\u1eef li\u1ec7u (read operations).<\/p>\n\n\n\n<p><strong>@CachePut(cacheNames=&#8221;products&#8221;, key=&#8221;#product.id&#8221;)<\/strong>:<\/p>\n\n\n\n<p>Annotation n\u00e0y kh\u00f4ng ki\u1ec3m tra cache tr\u01b0\u1edbc khi ch\u1ea1y. N\u00f3 s\u1ebd lu\u00f4n lu\u00f4n th\u1ef1c thi method. Sau khi method th\u1ef1c thi th\u00e0nh c\u00f4ng, k\u1ebft qu\u1ea3 tr\u1ea3 v\u1ec1 s\u1ebd \u0111\u01b0\u1ee3c d\u00f9ng \u0111\u1ec3 c\u1eadp nh\u1eadt ho\u1eb7c th\u00eam m\u1edbi v\u00e0o cache products.<\/p>\n\n\n\n<p>Tr\u01b0\u1eddng h\u1ee3p s\u1eed d\u1ee5ng: D\u00f9ng cho c\u00e1c method c\u1eadp nh\u1eadt d\u1eef li\u1ec7u. N\u00f3 gi\u00fap gi\u1eef cho cache lu\u00f4n \u0111\u1ed3ng b\u1ed9 v\u1edbi database sau khi c\u00f3 s\u1ef1 thay \u0111\u1ed5i.<\/p>\n\n\n\n<p><strong>@CacheEvict(cacheNames=&#8221;products&#8221;, key=&#8221;#id&#8221;)<\/strong>:<\/p>\n\n\n\n<p>Annotation n\u00e0y d\u00f9ng \u0111\u1ec3 x\u00f3a (evict) d\u1eef li\u1ec7u kh\u1ecfi cache. Khi method \u0111\u01b0\u1ee3c g\u1ecdi, entry t\u01b0\u01a1ng \u1ee9ng v\u1edbi key \u0111\u01b0\u1ee3c ch\u1ec9 \u0111\u1ecbnh s\u1ebd b\u1ecb x\u00f3a.<\/p>\n\n\n\n<p>Tr\u01b0\u1eddng h\u1ee3p s\u1eed d\u1ee5ng: D\u00f9ng cho c\u00e1c method x\u00f3a d\u1eef li\u1ec7u.<\/p>\n\n\n\n<p>N\u00f3 c\u00f2n c\u00f3 thu\u1ed9c t\u00ednh allEntries=true (@CacheEvict(cacheNames=&#8221;products&#8221;, allEntries=true)) \u0111\u1ec3 x\u00f3a t\u1ea5t c\u1ea3 c\u00e1c entry trong m\u1ed9t cache, h\u1eefu \u00edch khi c\u00f3 m\u1ed9t thay \u0111\u1ed5i l\u1edbn l\u00e0m cho to\u00e0n b\u1ed9 cache kh\u00f4ng c\u00f2n h\u1ee3p l\u1ec7.<\/p>\n\n\n\n<p><strong>V\u00ed d\u1ee5:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@Service\npublic class ProductService {\n\n    @Cacheable(cacheNames=\"products\", key=\"#id\")\n    public Product getProductById(Long id) {\n        \/\/ Ch\u1ec9 ch\u1ea1y khi cache miss\n        return productRepository.findById(id).orElse(null);\n    }\n\n    @CachePut(cacheNames=\"products\", key=\"#product.id\")\n    public Product updateProduct(Product product) {\n        \/\/ Lu\u00f4n ch\u1ea1y v\u00e0 c\u1eadp nh\u1eadt cache\n        return productRepository.save(product);\n    }\n\n    @CacheEvict(cacheNames=\"products\", key=\"#id\")\n    public void deleteProduct(Long id) {\n        \/\/ Lu\u00f4n ch\u1ea1y v\u00e0 x\u00f3a cache\n        productRepository.deleteById(id);\n    }\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-45-hay-trinh-bay-hi\u1ec3u-bi\u1ebft-c\u1ee7a-b\u1ea1n-v\u1ec1-distributed-transaction-cac-thach-th\u1ee9c-khi-tri\u1ec3n-khai-va-gi\u1ea3i-phap-d\u1ec3-x\u1eed-ly\"><strong>45. H\u00e3y tr\u00ecnh b\u00e0y hi\u1ec3u bi\u1ebft c\u1ee7a b\u1ea1n v\u1ec1 Distributed Transaction, c\u00e1c th\u00e1ch th\u1ee9c khi tri\u1ec3n khai v\u00e0 gi\u1ea3i ph\u00e1p \u0111\u1ec3 x\u1eed l\u00fd?<\/strong><\/h3>\n\n\n\n<p><strong>Gi\u1ea3i th\u00edch v\u1ec1 Distributed Transaction<\/strong><\/p>\n\n\n\n<p>Distributed Transaction l\u00e0 m\u1ed9t giao d\u1ecbch (transaction) m\u00e0 c\u00e1c thao t\u00e1c c\u1ee7a n\u00f3 tr\u1ea3i d\u00e0i tr\u00ean nhi\u1ec1u h\u1ec7 th\u1ed1ng t\u00e0i nguy\u00ean kh\u00e1c nhau, th\u01b0\u1eddng l\u00e0 nhi\u1ec1u microservice v\u1edbi c\u00e1c database ri\u00eang bi\u1ec7t.<\/p>\n\n\n\n<p>V\u00ed d\u1ee5 kinh \u0111i\u1ec3n: Trong m\u1ed9t h\u1ec7 th\u1ed1ng e-commerce, h\u00e0nh \u0111\u1ed9ng &#8220;\u0110\u1eb7t h\u00e0ng&#8221; c\u00f3 th\u1ec3 bao g\u1ed3m:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Service <strong>Order<\/strong>: T\u1ea1o m\u1ed9t \u0111\u01a1n h\u00e0ng m\u1edbi (ghi v\u00e0o DB c\u1ee7a Order).<\/li>\n\n\n\n<li>Service <strong>Payment<\/strong>: Tr\u1eeb ti\u1ec1n trong t\u00e0i kho\u1ea3n kh\u00e1ch h\u00e0ng (ghi v\u00e0o DB c\u1ee7a Payment).<\/li>\n\n\n\n<li>Service <strong>Inventory<\/strong>: Gi\u1ea3m s\u1ed1 l\u01b0\u1ee3ng t\u1ed3n kho c\u1ee7a s\u1ea3n ph\u1ea9m (ghi v\u00e0o DB c\u1ee7a Inventory).<\/li>\n<\/ul>\n\n\n\n<p>M\u1ed9t distributed transaction ph\u1ea3i \u0111\u1ea3m b\u1ea3o t\u00ednh nguy\u00ean t\u1eed (Atomicity), t\u1ee9c l\u00e0 ho\u1eb7c c\u1ea3 3 b\u01b0\u1edbc tr\u00ean \u0111\u1ec1u th\u00e0nh c\u00f4ng, ho\u1eb7c n\u1ebfu c\u00f3 b\u1ea5t k\u1ef3 b\u01b0\u1edbc n\u00e0o th\u1ea5t b\u1ea1i th\u00ec t\u1ea5t c\u1ea3 c\u00e1c b\u01b0\u1edbc \u0111\u00e3 th\u1ef1c hi\u1ec7n tr\u01b0\u1edbc \u0111\u00f3 ph\u1ea3i \u0111\u01b0\u1ee3c ho\u00e0n t\u00e1c (rollback).<\/p>\n\n\n\n<p><strong>Th\u00e1ch th\u1ee9c khi tri\u1ec3n khai<\/strong><\/p>\n\n\n\n<p>Tri\u1ec3n khai distributed transaction r\u1ea5t kh\u00f3 kh\u0103n, \u0111\u1eb7c bi\u1ec7t trong ki\u1ebfn tr\u00fac microservices, v\u00ec:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>M\u1ea5t t\u00ednh ACID<\/strong>: Giao th\u1ee9c commit truy\u1ec1n th\u1ed1ng nh\u01b0 Two-Phase Commit (2PC) y\u00eau c\u1ea7u t\u1ea5t c\u1ea3 c\u00e1c service tham gia ph\u1ea3i kh\u00f3a t\u00e0i nguy\u00ean cho \u0111\u1ebfn khi transaction coordinator \u0111\u01b0a ra quy\u1ebft \u0111\u1ecbnh cu\u1ed1i c\u00f9ng.\n<ul class=\"wp-block-list\">\n<li>Tight Coupling: T\u1ea5t c\u1ea3 c\u00e1c service ph\u1ea3i online v\u00e0 s\u1eb5n s\u00e0ng. N\u1ebfu m\u1ed9t service b\u1ecb ch\u1eadm, c\u1ea3 h\u1ec7 th\u1ed1ng s\u1ebd b\u1ecb treo.<\/li>\n\n\n\n<li>Gi\u1ea3m t\u00ednh s\u1eb5n s\u00e0ng (Availability): Vi\u1ec7c kh\u00f3a t\u00e0i nguy\u00ean l\u00e0m gi\u1ea3m hi\u1ec7u n\u0103ng v\u00e0 kh\u1ea3 n\u0103ng ch\u1ecbu t\u1ea3i c\u1ee7a h\u1ec7 th\u1ed1ng.<\/li>\n\n\n\n<li>Trong th\u1ef1c t\u1ebf, 2PC g\u1ea7n nh\u01b0 kh\u00f4ng \u0111\u01b0\u1ee3c s\u1eed d\u1ee5ng trong c\u00e1c h\u1ec7 th\u1ed1ng microservices hi\u1ec7n \u0111\u1ea1i v\u00ec nh\u1eefng nh\u01b0\u1ee3c \u0111i\u1ec3m n\u00e0y.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>\u0110\u1ed9 ph\u1ee9c t\u1ea1p<\/strong> trong vi\u1ec7c qu\u1ea3n l\u00fd tr\u1ea1ng th\u00e1i, x\u1eed l\u00fd l\u1ed7i, v\u00e0 \u0111\u1ea3m b\u1ea3o d\u1eef li\u1ec7u nh\u1ea5t qu\u00e1n tr\u00ean nhi\u1ec1u service.<\/li>\n<\/ul>\n\n\n\n<p><strong>Gi\u1ea3i ph\u00e1p: Saga Pattern<\/strong><\/p>\n\n\n\n<p>Thay v\u00ec c\u1ed1 g\u1eafng \u0111\u1ea1t \u0111\u01b0\u1ee3c t\u00ednh nguy\u00ean t\u1eed tuy\u1ec7t \u0111\u1ed1i nh\u01b0 2PC, c\u1ed9ng \u0111\u1ed3ng microservices th\u01b0\u1eddng ch\u1ea5p nh\u1eadn m\u1ed9t tr\u1ea1ng th\u00e1i g\u1ecdi l\u00e0 BASE (Basically Available, Soft state, Eventually consistent). Gi\u1ea3i ph\u00e1p ph\u1ed5 bi\u1ebfn nh\u1ea5t \u0111\u1ec3 x\u1eed l\u00fd distributed transaction l\u00e0 Saga Pattern.<\/p>\n\n\n\n<p>Saga Pattern l\u00e0 m\u1ed9t chu\u1ed7i c\u00e1c giao d\u1ecbch c\u1ee5c b\u1ed9 (local transaction). M\u1ed7i giao d\u1ecbch c\u1ee5c b\u1ed9 s\u1ebd c\u1eadp nh\u1eadt database c\u1ee7a ch\u00ednh service \u0111\u00f3 v\u00e0 sau \u0111\u00f3 xu\u1ea5t b\u1ea3n (publish) m\u1ed9t s\u1ef1 ki\u1ec7n \u0111\u1ec3 k\u00edch ho\u1ea1t giao d\u1ecbch c\u1ee5c b\u1ed9 ti\u1ebfp theo trong saga.<\/p>\n\n\n\n<p>N\u1ebfu m\u1ed9t giao d\u1ecbch c\u1ee5c b\u1ed9 n\u00e0o \u0111\u00f3 th\u1ea5t b\u1ea1i, saga s\u1ebd th\u1ef1c thi m\u1ed9t chu\u1ed7i c\u00e1c giao d\u1ecbch b\u00f9 tr\u1eeb (compensating transaction) \u0111\u1ec3 ho\u00e0n t\u00e1c l\u1ea1i c\u00f4ng vi\u1ec7c \u0111\u00e3 \u0111\u01b0\u1ee3c th\u1ef1c hi\u1ec7n b\u1edfi c\u00e1c giao d\u1ecbch c\u1ee5c b\u1ed9 tr\u01b0\u1edbc \u0111\u00f3.<\/p>\n\n\n\n<p><strong>V\u00ed d\u1ee5 lu\u1ed3ng &#8220;\u0110\u1eb7t h\u00e0ng&#8221; theo Saga:<\/strong><\/p>\n\n\n\n<p><strong>Order Service<\/strong>: B\u1eaft \u0111\u1ea7u CreateOrderSaga.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>(Local Tx 1) T\u1ea1o \u0111\u01a1n h\u00e0ng v\u1edbi tr\u1ea1ng th\u00e1i PENDING.<\/li>\n\n\n\n<li>Publish s\u1ef1 ki\u1ec7n OrderCreated.<\/li>\n<\/ol>\n\n\n\n<p><strong>Payment Service<\/strong>: L\u1eafng nghe s\u1ef1 ki\u1ec7n OrderCreated.<\/p>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li>(Local Tx 2) X\u1eed l\u00fd thanh to\u00e1n.<\/li>\n\n\n\n<li>N\u1ebfu th\u00e0nh c\u00f4ng -&gt; Publish s\u1ef1 ki\u1ec7n PaymentProcessed.<\/li>\n\n\n\n<li>N\u1ebfu th\u1ea5t b\u1ea1i -&gt; Publish s\u1ef1 ki\u1ec7n PaymentFailed.<\/li>\n<\/ol>\n\n\n\n<p><strong>Inventory Service<\/strong>: L\u1eafng nghe s\u1ef1 ki\u1ec7n PaymentProcessed.<\/p>\n\n\n\n<ol start=\"6\" class=\"wp-block-list\">\n<li>(Local Tx 3) Tr\u1eeb kho.<\/li>\n\n\n\n<li>N\u1ebfu th\u00e0nh c\u00f4ng -&gt; Publish s\u1ef1 ki\u1ec7n InventoryUpdated. Saga k\u1ebft th\u00fac th\u00e0nh c\u00f4ng.<\/li>\n\n\n\n<li>N\u1ebfu th\u1ea5t b\u1ea1i (h\u1ebft h\u00e0ng) -&gt; Publish s\u1ef1 ki\u1ec7n InventoryUpdateFailed.<\/li>\n<\/ol>\n\n\n\n<p><strong>Lu\u1ed3ng b\u00f9 tr\u1eeb (khi InventoryUpdateFailed):<\/strong><\/p>\n\n\n\n<p><strong>Payment Service<\/strong>: L\u1eafng nghe InventoryUpdateFailed.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>(Compensating Tx 2) Ho\u00e0n ti\u1ec1n cho kh\u00e1ch h\u00e0ng.<\/li>\n\n\n\n<li>Publish s\u1ef1 ki\u1ec7n PaymentRefunded.<\/li>\n<\/ol>\n\n\n\n<p><strong>Order Service<\/strong>: L\u1eafng nghe PaymentRefunded.<\/p>\n\n\n\n<p>(Compensating Tx 1) C\u1eadp nh\u1eadt tr\u1ea1ng th\u00e1i \u0111\u01a1n h\u00e0ng th\u00e0nh FAILED.<\/p>\n\n\n\n<p><strong>C\u00f3 hai c\u00e1ch \u0111\u1ec3 \u0111i\u1ec1u ph\u1ed1i Saga:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Choreography<\/strong>: C\u00e1c service t\u1ef1 l\u1eafng nghe s\u1ef1 ki\u1ec7n c\u1ee7a nhau v\u00e0 h\u00e0nh \u0111\u1ed9ng (nh\u01b0 v\u00ed d\u1ee5 tr\u00ean). C\u00e1ch n\u00e0y linh ho\u1ea1t nh\u01b0ng kh\u00f3 theo d\u00f5i lu\u1ed3ng.<\/li>\n\n\n\n<li><strong>Orchestration<\/strong>: C\u00f3 m\u1ed9t service trung t\u00e2m (Orchestrator) \u0111i\u1ec1u ph\u1ed1i to\u00e0n b\u1ed9 lu\u1ed3ng, ra l\u1ec7nh cho c\u00e1c service kh\u00e1c th\u1ef1c hi\u1ec7n h\u00e0nh \u0111\u1ed9ng v\u00e0 b\u00f9 tr\u1eeb. C\u00e1ch n\u00e0y d\u1ec5 qu\u1ea3n l\u00fd h\u01a1n.<\/li>\n<\/ul>\n\n\n\n<p>Saga Pattern gi\u00fap duy tr\u00ec t\u00ednh nh\u1ea5t qu\u00e1n cu\u1ed1i c\u00f9ng (eventual consistency) v\u00e0 gi\u1eef cho c\u00e1c service \u0111\u01b0\u1ee3c d\u00e9couple, ph\u00f9 h\u1ee3p v\u1edbi tri\u1ebft l\u00fd c\u1ee7a microservices.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-46-testcontainers-la-gi-va-no-giup-ich-gi-trong-vi\u1ec7c-vi\u1ebft-integration-test\"><strong>46. Testcontainers l\u00e0 g\u00ec v\u00e0 n\u00f3 gi\u00fap \u00edch g\u00ec trong vi\u1ec7c vi\u1ebft Integration Test?<\/strong><\/h3>\n\n\n\n<p>Testcontainers l\u00e0 m\u1ed9t th\u01b0 vi\u1ec7n Java cho ph\u00e9p l\u1eadp tr\u00ecnh vi\u00ean kh\u1edfi t\u1ea1o v\u00e0 qu\u1ea3n l\u00fd c\u00e1c instance Docker container d\u00f9ng m\u1ed9t l\u1ea7n (throwaway) ngay t\u1eeb trong code Java test.<\/p>\n\n\n\n<p>N\u00f3i \u0111\u01a1n gi\u1ea3n, thay v\u00ec ph\u1ea3i c\u00e0i \u0111\u1eb7t v\u00e0 c\u1ea5u h\u00ecnh m\u1ed9t database PostgreSQL, m\u1ed9t message broker RabbitMQ, hay m\u1ed9t Redis server tr\u00ean m\u00e1y local ho\u1eb7c tr\u00ean CI server \u0111\u1ec3 ch\u1ea1y integration test, ta c\u00f3 th\u1ec3 d\u00f9ng Testcontainers \u0111\u1ec3 kh\u1edfi t\u1ea1o c\u00e1c d\u1ecbch v\u1ee5 n\u00e0y d\u01b0\u1edbi d\u1ea1ng Docker container ngay khi b\u1ed9 test b\u1eaft \u0111\u1ea7u ch\u1ea1y, v\u00e0 t\u1ef1 \u0111\u1ed9ng x\u00f3a ch\u00fang \u0111i khi test k\u1ebft th\u00fac.<\/p>\n\n\n\n<p><strong>Testcontainers gi\u00fap \u00edch r\u1ea5t nhi\u1ec1u trong vi\u1ec7c vi\u1ebft Integration Test, c\u1ee5 th\u1ec3 l\u00e0:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>T\u1ea1o m\u00f4i tr\u01b0\u1eddng test \u0111\u00e1ng tin c\u1eady v\u00e0 g\u1ea7n gi\u1ed1ng Production<\/strong>:<\/li>\n<\/ul>\n\n\n\n<p>V\u1ea5n \u0111\u1ec1 l\u1edbn c\u1ee7a integration test truy\u1ec1n th\u1ed1ng l\u00e0 th\u01b0\u1eddng ph\u1ea3i d\u00f9ng c\u00e1c phi\u00ean b\u1ea3n &#8220;nh\u1eb9&#8221; ho\u1eb7c in-memory c\u1ee7a c\u00e1c d\u1ecbch v\u1ee5, v\u00ed d\u1ee5 d\u00f9ng H2 database \u0111\u1ec3 test cho code ch\u1ea1y v\u1edbi PostgreSQL. \u0110i\u1ec1u n\u00e0y ti\u1ec1m \u1ea9n r\u1ee7i ro v\u00ec H2 v\u00e0 PostgreSQL c\u00f3 nh\u1eefng kh\u00e1c bi\u1ec7t v\u1ec1 c\u00fa ph\u00e1p SQL, ki\u1ec3u d\u1eef li\u1ec7u, v\u00e0 h\u00e0nh vi.<\/p>\n\n\n\n<p>Testcontainers cho ph\u00e9p ta ch\u1ea1y test v\u1edbi phi\u00ean b\u1ea3n ch\u00ednh x\u00e1c c\u1ee7a database (PostgreSQL 14, MySQL 8) ho\u1eb7c message broker m\u00e0 \u1ee9ng d\u1ee5ng s\u1ebd s\u1eed d\u1ee5ng \u1edf m\u00f4i tr\u01b0\u1eddng production. \u0110i\u1ec1u n\u00e0y gi\u00fap t\u0103ng \u0111\u1ed9 tin c\u1eady c\u1ee7a b\u1ed9 test l\u00ean r\u1ea5t nhi\u1ec1u.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u0110\u1ea3m b\u1ea3o c\u00e1c b\u00e0i test \u0111\u01b0\u1ee3c c\u00f4 l\u1eadp v\u00e0 c\u00f3 th\u1ec3 l\u1eb7p l\u1ea1i (Reproducible)<\/strong>:<\/li>\n<\/ul>\n\n\n\n<p>M\u1ed7i l\u1ea7n ch\u1ea1y test, Testcontainers s\u1ebd kh\u1edfi t\u1ea1o m\u1ed9t container ho\u00e0n to\u00e0n m\u1edbi, s\u1ea1ch s\u1ebd. \u0110i\u1ec1u n\u00e0y \u0111\u1ea3m b\u1ea3o r\u1eb1ng k\u1ebft qu\u1ea3 c\u1ee7a m\u1ed9t l\u1ea7n ch\u1ea1y test kh\u00f4ng b\u1ecb \u1ea3nh h\u01b0\u1edfng b\u1edfi &#8220;d\u1eef li\u1ec7u r\u00e1c&#8221; t\u1eeb l\u1ea7n ch\u1ea1y tr\u01b0\u1edbc, gi\u00fap c\u00e1c b\u00e0i test tr\u1edf n\u00ean \u1ed5n \u0111\u1ecbnh v\u00e0 d\u1ec5 d\u1ef1 \u0111o\u00e1n h\u01a1n.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u0110\u01a1n gi\u1ea3n h\u00f3a vi\u1ec7c thi\u1ebft l\u1eadp m\u00f4i tr\u01b0\u1eddng<\/strong>:<\/li>\n<\/ul>\n\n\n\n<p>L\u1eadp tr\u00ecnh vi\u00ean m\u1edbi tham gia d\u1ef1 \u00e1n kh\u00f4ng c\u1ea7n ph\u1ea3i m\u1ea5t h\u00e0ng gi\u1edd \u0111\u1ec3 c\u00e0i \u0111\u1eb7t v\u00e0 c\u1ea5u h\u00ecnh m\u1ed9t lo\u1ea1t c\u00e1c d\u1ecbch v\u1ee5 tr\u00ean m\u00e1y c\u1ee7a h\u1ecd. H\u1ecd ch\u1ec9 c\u1ea7n c\u00f3 Docker v\u00e0 ch\u1ea1y .\/mvnw test. Testcontainers s\u1ebd lo ph\u1ea7n c\u00f2n l\u1ea1i.<\/p>\n\n\n\n<p>\u0110i\u1ec1u n\u00e0y c\u0169ng \u00e1p d\u1ee5ng t\u01b0\u01a1ng t\u1ef1 cho m\u00f4i tr\u01b0\u1eddng CI\/CD, gi\u00fap \u0111\u01a1n gi\u1ea3n h\u00f3a pipeline m\u1ed9t c\u00e1ch \u0111\u00e1ng k\u1ec3.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>H\u1ed7 tr\u1ee3 \u0111a d\u1ea1ng c\u00e1c c\u00f4ng ngh\u1ec7<\/strong>:<\/li>\n<\/ul>\n\n\n\n<p>Testcontainers c\u00f3 c\u00e1c module \u0111\u01b0\u1ee3c x\u00e2y d\u1ef1ng s\u1eb5n cho r\u1ea5t nhi\u1ec1u c\u00f4ng ngh\u1ec7 ph\u1ed5 bi\u1ebfn nh\u01b0 PostgreSQL, MySQL, Kafka, RabbitMQ, Redis, Elasticsearch&#8230; v\u00e0 c\u0169ng cho ph\u00e9p s\u1eed d\u1ee5ng b\u1ea5t k\u1ef3 Docker image n\u00e0o c\u00f3 s\u1eb5n tr\u00ean Docker Hub.<\/p>\n\n\n\n<p><strong>V\u00ed d\u1ee5 s\u1eed d\u1ee5ng trong m\u1ed9t b\u00e0i test @DataJpaTest:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ T\u00edch h\u1ee3p Testcontainers v\u1edbi JUnit 5\n@Testcontainers \n\/\/ Thay v\u00ec d\u00f9ng H2, ta d\u00f9ng @DataJpaTest v\u1edbi c\u1ea5u h\u00ecnh \u0111\u1ec3 ch\u1ea1y tr\u00ean container th\u1eadt\n@DataJpaTest\n@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)\nclass UserRepositoryIntegrationTest {\n\n    \/\/ Khai b\u00e1o container s\u1ebd \u0111\u01b0\u1ee3c kh\u1edfi t\u1ea1o\n    @Container\n    static PostgreSQLContainer&lt;?&gt; postgres = new PostgreSQLContainer&lt;&gt;(\"postgres:14-alpine\");\n\n    \/\/ T\u1ef1 \u0111\u1ed9ng c\u1ea5u h\u00ecnh Spring Boot \u0111\u1ec3 k\u1ebft n\u1ed1i \u0111\u1ebfn container v\u1eeba kh\u1edfi t\u1ea1o\n    @DynamicPropertySource\n    static void configureProperties(DynamicPropertyRegistry registry) {\n        registry.add(\"spring.datasource.url\", postgres::getJdbcUrl);\n        registry.add(\"spring.datasource.username\", postgres::getUsername);\n        registry.add(\"spring.datasource.password\", postgres::getPassword);\n    }\n\n    @Autowired\n    private UserRepository userRepository;\n\n    @Test\n    void whenSaveAndFindUser_thenSuccess() {\n        User user = new User(\"testuser\", \"test@example.com\");\n        userRepository.save(user);\n\n        Optional&lt;User&gt; foundUser = userRepository.findByEmail(\"test@example.com\");\n\n        assertThat(foundUser).isPresent();\n        assertThat(foundUser.get().getUsername()).isEqualTo(\"testuser\");\n    }\n}<\/code><\/pre>\n\n\n\n<p>T\u00f3m l\u1ea1i, Testcontainers l\u00e0 m\u1ed9t c\u00f4ng c\u1ee5 c\u1ef1c k\u1ef3 m\u1ea1nh m\u1ebd, gi\u00fap n\u00e2ng t\u1ea7m c\u00e1c b\u00e0i integration test t\u1eeb vi\u1ec7c ch\u1ea1y tr\u00ean c\u00e1c m\u00f4i tr\u01b0\u1eddng gi\u1ea3 l\u1eadp sang ch\u1ea1y tr\u00ean c\u00e1c m\u00f4i tr\u01b0\u1eddng th\u1ef1c s\u1ef1, \u0111\u00e1ng tin c\u1eady, t\u1eeb \u0111\u00f3 gi\u00fap ph\u00e1t hi\u1ec7n l\u1ed7i s\u1edbm h\u01a1n v\u00e0 t\u1ef1 tin h\u01a1n khi deploy s\u1ea3n ph\u1ea9m.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-t\u1ed5ng-k\u1ebft\"><span class=\"ez-toc-section\" id=\"Tong_ket\"><\/span><strong>T\u1ed5ng k\u1ebft<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>T\u1eeb Spring Core, Spring Data, Spring Security cho \u0111\u1ebfn Microservices, hy v\u1ecdng b\u1ed9 c\u00e2u h\u1ecfi n\u00e0y \u0111\u00e3 gi\u00fap b\u1ea1n nh\u00ecn l\u1ea1i to\u00e0n b\u1ed9 \u201cb\u1ee9c tranh\u201d v\u1ec1 Java Spring Boot m\u1ed9t c\u00e1ch h\u1ec7 th\u1ed1ng, t\u1eeb n\u1ec1n t\u1ea3ng \u0111\u1ebfn \u1ee9ng d\u1ee5ng th\u1ef1c t\u1ebf.<\/p>\n\n\n\n<p>\u0110i\u1ec1u quan tr\u1ecdng kh\u00f4ng n\u1eb1m \u1edf vi\u1ec7c thu\u1ed9c l\u00f2ng c\u00e2u tr\u1ea3 l\u1eddi, m\u00e0 l\u00e0 hi\u1ec3u \u0111\u01b0\u1ee3c b\u1ea3n ch\u1ea5t v\u1ea5n \u0111\u1ec1. Khi \u0111\u00f3, b\u1ea1n s\u1ebd t\u1ef1 tin xem bu\u1ed5i ph\u1ecfng v\u1ea5n nh\u01b0 m\u1ed9t cu\u1ed9c trao \u0111\u1ed5i k\u1ef9 thu\u1eadt. Ch\u00fac b\u1ea1n chu\u1ea9n b\u1ecb t\u1ed1t v\u00e0 \u0111\u1ea1t \u0111\u01b0\u1ee3c th\u00e0nh c\u00f4ng trong bu\u1ed5i ph\u1ecfng v\u1ea5n s\u1eafp t\u1edbi!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hi\u1ec7n nay, Java Spring Boot \u0111\u00e3 tr\u1edf th\u00e0nh m\u1ed9t framework kh\u00f4ng th\u1ec3 thi\u1ebfu cho c\u00e1c nh\u00e0 ph\u00e1t tri\u1ec3n \u1ee9ng d\u1ee5ng web hi\u1ec7n \u0111\u1ea1i. T\u1eeb nh\u1eefng kh\u00e1i ni\u1ec7m c\u01a1 b\u1ea3n v\u1ec1 Spring Core \u0111\u1ebfn c\u00e1c ki\u1ebfn tr\u00fac Microservices ph\u1ee9c t\u1ea1p, vi\u1ec7c n\u1eafm v\u1eefng Spring Boot l\u00e0 ch\u00eca kh\u00f3a \u0111\u1ec3 b\u1ea1n t\u1ef1 tin chinh ph\u1ee5c m\u1ecdi bu\u1ed5i [&hellip;]<\/p>\n","protected":false},"author":203,"featured_media":93363,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_gspb_post_css":"","footnotes":""},"categories":[109,10350],"tags":[],"class_list":["post-92439","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-chuyen-mon-it","category-java"],"blocksy_meta":[],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.8 (Yoast SEO v27.8) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Top 45+ c\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n Java Spring Boot th\u01b0\u1eddng g\u1eb7p\u00a0 - ITviec Blog<\/title>\n<meta name=\"description\" content=\"T\u1ed5ng h\u1ee3p top nh\u1eefng c\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n Java Spring Boot th\u01b0\u1eddng g\u1eb7p nh\u1ea5t, k\u00e8m theo gi\u1ea3i th\u00edch chi ti\u1ebft v\u00e0 v\u00ed d\u1ee5 th\u1ef1c t\u1ebf.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/itviec.com\/blog\/cau-hoi-phong-van-java-spring-boot\/\" \/>\n<meta property=\"og:locale\" content=\"vi_VN\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Top 45+ c\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n Java Spring Boot th\u01b0\u1eddng g\u1eb7p\u00a0\" \/>\n<meta property=\"og:description\" content=\"Hi\u1ec7n nay, Java Spring Boot \u0111\u00e3 tr\u1edf th\u00e0nh m\u1ed9t framework kh\u00f4ng th\u1ec3 thi\u1ebfu cho c\u00e1c nh\u00e0 ph\u00e1t tri\u1ec3n \u1ee9ng d\u1ee5ng web hi\u1ec7n \u0111\u1ea1i. T\u1eeb nh\u1eefng kh\u00e1i ni\u1ec7m c\u01a1 b\u1ea3n v\u1ec1 Spring\" \/>\n<meta property=\"og:url\" content=\"https:\/\/itviec.com\/blog\/cau-hoi-phong-van-java-spring-boot\/\" \/>\n<meta property=\"og:site_name\" content=\"ITviec Blog\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/ITviec\" \/>\n<meta property=\"article:published_time\" content=\"2025-11-30T15:10:27+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-11-30T15:39:18+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/itviec.com\/blog\/wp-content\/uploads\/2025\/10\/cau-hoi-phong-van-java-spring-boot-scaled.png\" \/>\n\t<meta property=\"og:image:width\" content=\"800\" \/>\n\t<meta property=\"og:image:height\" content=\"421\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Tien Tran\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@ITviec\" \/>\n<meta name=\"twitter:site\" content=\"@ITviec\" \/>\n<meta name=\"twitter:label1\" content=\"\u0110\u01b0\u1ee3c vi\u1ebft b\u1edfi\" \/>\n\t<meta name=\"twitter:data1\" content=\"Tien Tran\" \/>\n\t<meta name=\"twitter:label2\" content=\"\u01af\u1edbc t\u00ednh th\u1eddi gian \u0111\u1ecdc\" \/>\n\t<meta name=\"twitter:data2\" content=\"1 ph\u00fat\" \/>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Top 45+ c\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n Java Spring Boot th\u01b0\u1eddng g\u1eb7p\u00a0 - ITviec Blog","description":"T\u1ed5ng h\u1ee3p top nh\u1eefng c\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n Java Spring Boot th\u01b0\u1eddng g\u1eb7p nh\u1ea5t, k\u00e8m theo gi\u1ea3i th\u00edch chi ti\u1ebft v\u00e0 v\u00ed d\u1ee5 th\u1ef1c t\u1ebf.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/itviec.com\/blog\/cau-hoi-phong-van-java-spring-boot\/","og_locale":"vi_VN","og_type":"article","og_title":"Top 45+ c\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n Java Spring Boot th\u01b0\u1eddng g\u1eb7p\u00a0","og_description":"Hi\u1ec7n nay, Java Spring Boot \u0111\u00e3 tr\u1edf th\u00e0nh m\u1ed9t framework kh\u00f4ng th\u1ec3 thi\u1ebfu cho c\u00e1c nh\u00e0 ph\u00e1t tri\u1ec3n \u1ee9ng d\u1ee5ng web hi\u1ec7n \u0111\u1ea1i. T\u1eeb nh\u1eefng kh\u00e1i ni\u1ec7m c\u01a1 b\u1ea3n v\u1ec1 Spring","og_url":"https:\/\/itviec.com\/blog\/cau-hoi-phong-van-java-spring-boot\/","og_site_name":"ITviec Blog","article_publisher":"https:\/\/www.facebook.com\/ITviec","article_published_time":"2025-11-30T15:10:27+00:00","article_modified_time":"2025-11-30T15:39:18+00:00","og_image":[{"width":800,"height":421,"url":"https:\/\/itviec.com\/blog\/wp-content\/uploads\/2025\/10\/cau-hoi-phong-van-java-spring-boot-scaled.png","type":"image\/png"}],"author":"Tien Tran","twitter_card":"summary_large_image","twitter_creator":"@ITviec","twitter_site":"@ITviec","twitter_misc":{"\u0110\u01b0\u1ee3c vi\u1ebft b\u1edfi":"Tien Tran","\u01af\u1edbc t\u00ednh th\u1eddi gian \u0111\u1ecdc":"1 ph\u00fat"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/itviec.com\/blog\/cau-hoi-phong-van-java-spring-boot\/#article","isPartOf":{"@id":"https:\/\/itviec.com\/blog\/cau-hoi-phong-van-java-spring-boot\/"},"author":{"name":"Tien Tran","@id":"https:\/\/itviec.com\/blog\/#\/schema\/person\/1595d671c49cfa2a48cd3c0a047a1298"},"headline":"Top 45+ c\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n Java Spring Boot th\u01b0\u1eddng g\u1eb7p\u00a0","datePublished":"2025-11-30T15:10:27+00:00","dateModified":"2025-11-30T15:39:18+00:00","mainEntityOfPage":{"@id":"https:\/\/itviec.com\/blog\/cau-hoi-phong-van-java-spring-boot\/"},"wordCount":22394,"publisher":{"@id":"https:\/\/itviec.com\/blog\/#organization"},"image":{"@id":"https:\/\/itviec.com\/blog\/cau-hoi-phong-van-java-spring-boot\/#primaryimage"},"thumbnailUrl":"https:\/\/itviec.com\/blog\/wp-content\/uploads\/2025\/10\/cau-hoi-phong-van-java-spring-boot-scaled.png","articleSection":["Chuy\u00ean m\u00f4n IT","Java"],"inLanguage":"vi"},{"@type":"WebPage","@id":"https:\/\/itviec.com\/blog\/cau-hoi-phong-van-java-spring-boot\/","url":"https:\/\/itviec.com\/blog\/cau-hoi-phong-van-java-spring-boot\/","name":"Top 45+ c\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n Java Spring Boot th\u01b0\u1eddng g\u1eb7p\u00a0 - ITviec Blog","isPartOf":{"@id":"https:\/\/itviec.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/itviec.com\/blog\/cau-hoi-phong-van-java-spring-boot\/#primaryimage"},"image":{"@id":"https:\/\/itviec.com\/blog\/cau-hoi-phong-van-java-spring-boot\/#primaryimage"},"thumbnailUrl":"https:\/\/itviec.com\/blog\/wp-content\/uploads\/2025\/10\/cau-hoi-phong-van-java-spring-boot-scaled.png","datePublished":"2025-11-30T15:10:27+00:00","dateModified":"2025-11-30T15:39:18+00:00","description":"T\u1ed5ng h\u1ee3p top nh\u1eefng c\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n Java Spring Boot th\u01b0\u1eddng g\u1eb7p nh\u1ea5t, k\u00e8m theo gi\u1ea3i th\u00edch chi ti\u1ebft v\u00e0 v\u00ed d\u1ee5 th\u1ef1c t\u1ebf.","breadcrumb":{"@id":"https:\/\/itviec.com\/blog\/cau-hoi-phong-van-java-spring-boot\/#breadcrumb"},"inLanguage":"vi","potentialAction":[{"@type":"ReadAction","target":["https:\/\/itviec.com\/blog\/cau-hoi-phong-van-java-spring-boot\/"]}]},{"@type":"ImageObject","inLanguage":"vi","@id":"https:\/\/itviec.com\/blog\/cau-hoi-phong-van-java-spring-boot\/#primaryimage","url":"https:\/\/itviec.com\/blog\/wp-content\/uploads\/2025\/10\/cau-hoi-phong-van-java-spring-boot-scaled.png","contentUrl":"https:\/\/itviec.com\/blog\/wp-content\/uploads\/2025\/10\/cau-hoi-phong-van-java-spring-boot-scaled.png","width":800,"height":421,"caption":"c\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n Java Spring Boot\u00a0- itviec blog"},{"@type":"BreadcrumbList","@id":"https:\/\/itviec.com\/blog\/cau-hoi-phong-van-java-spring-boot\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Chuy\u00ean m\u00f4n IT","item":"https:\/\/itviec.com\/blog\/chuyen-mon-it\/"},{"@type":"ListItem","position":2,"name":"Top 45+ c\u00e2u h\u1ecfi ph\u1ecfng v\u1ea5n Java Spring Boot th\u01b0\u1eddng g\u1eb7p\u00a0"}]},{"@type":"WebSite","@id":"https:\/\/itviec.com\/blog\/#website","url":"https:\/\/itviec.com\/blog\/","name":"ITviec Blog","description":"IT Jobs &amp; People in Vietnam","publisher":{"@id":"https:\/\/itviec.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/itviec.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"vi"},{"@type":"Organization","@id":"https:\/\/itviec.com\/blog\/#organization","name":"ITviec","url":"https:\/\/itviec.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"vi","@id":"https:\/\/itviec.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/itviec.com\/blog\/wp-content\/uploads\/2018\/12\/itviec-black-square-facebook.png","contentUrl":"https:\/\/itviec.com\/blog\/wp-content\/uploads\/2018\/12\/itviec-black-square-facebook.png","width":1800,"height":1800,"caption":"ITviec"},"image":{"@id":"https:\/\/itviec.com\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/ITviec","https:\/\/x.com\/ITviec","https:\/\/www.linkedin.com\/company\/itviec","https:\/\/www.youtube.com\/channel\/UCYthAQ3bcGr57M_ag5gHDvQ"]},{"@type":"Person","@id":"https:\/\/itviec.com\/blog\/#\/schema\/person\/1595d671c49cfa2a48cd3c0a047a1298","name":"Tien Tran","image":{"@type":"ImageObject","inLanguage":"vi","@id":"https:\/\/itviec.com\/blog\/wp-content\/uploads\/2024\/05\/tien-tran-author-e1715658627643-100x100.jpg","url":"https:\/\/itviec.com\/blog\/wp-content\/uploads\/2024\/05\/tien-tran-author-e1715658627643-100x100.jpg","contentUrl":"https:\/\/itviec.com\/blog\/wp-content\/uploads\/2024\/05\/tien-tran-author-e1715658627643-100x100.jpg","caption":"Tien Tran"},"url":"https:\/\/itviec.com\/blog\/author\/tien-tran\/"}]}},"_links":{"self":[{"href":"https:\/\/itviec.com\/blog\/wp-json\/wp\/v2\/posts\/92439","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/itviec.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/itviec.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/itviec.com\/blog\/wp-json\/wp\/v2\/users\/203"}],"replies":[{"embeddable":true,"href":"https:\/\/itviec.com\/blog\/wp-json\/wp\/v2\/comments?post=92439"}],"version-history":[{"count":4,"href":"https:\/\/itviec.com\/blog\/wp-json\/wp\/v2\/posts\/92439\/revisions"}],"predecessor-version":[{"id":93376,"href":"https:\/\/itviec.com\/blog\/wp-json\/wp\/v2\/posts\/92439\/revisions\/93376"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/itviec.com\/blog\/wp-json\/wp\/v2\/media\/93363"}],"wp:attachment":[{"href":"https:\/\/itviec.com\/blog\/wp-json\/wp\/v2\/media?parent=92439"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/itviec.com\/blog\/wp-json\/wp\/v2\/categories?post=92439"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/itviec.com\/blog\/wp-json\/wp\/v2\/tags?post=92439"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}