🌱 Spring Core

Cheat sheet · Tổng ôn phỏng vấn

Spring Core
& IoC/DI trong một trang

Từ IoC container, bean, dependency injection, scope, vòng đời đến proxy, AOP, cấu hình và testing — mỗi mục là một ý cốt lõi kèm snippet chạy được, đủ để trả lời phỏng vấn và rà lại trước buổi phỏng vấn.

5.0 2017Reactive WebFlux · Kotlin
5.3 2020R2DBC · GraalVM hints
6.0 2022Java 17 · Jakarta · AOT
6.1 2023RestClient · JdbcClient
6.2 2024AOT & testing
/

Bấm / để tìm nhanh · Bấm câu hỏi để mở đáp án · Nút Đã ôn lưu tiến độ trên máy bạn.

Bộ cheatsheet: 🏠 Trang chủ ☕ Java Core 🌱 Spring Core 🟢 Spring Data JPA & Hibernate

Tổng quan & IoC/DI

Spring cốt lõi là một IoC container: thay vì tự new và nối dependency, bạn khai báo và để container tạo + wiring. Hiểu rõ điều này là hiểu toàn bộ Spring.

IoC và DI là gì — khác nhau ra sao

Khái niệmÝ nghĩaVai trò trong Spring
IoCĐảo ngược quyền kiểm soát: bạn không tự tạo object, giao cho framework/containerContainer quản lý vòng đời + wiring của bean
DIMột dạng của IoC: dependency được tiêm vào thay vì tự tìm/khởi tạoConstructor/setter/field injection
BeanObject do Spring container quản lýKhởi tạo 1 lần, có scope và lifecycle

Lợi ích: giảm coupling, dễ test (thay mock), quản lý scope/lifecycle, và là nền để AOP (@Transactional, @Async) hoạt động qua proxy.

Ba trụ cột cần nhớ

  • Container — BeanFactory / ApplicationContext: nơi chứa và quản lý bean
  • DI — cơ chế nối các bean lại với nhau
  • AOP — tách cross-cutting concern (log, tx, security) khỏi business code

Luồng gọi điển hình: AppConfig → container khởi tạo → nạp @Component/@Bean → inject → sẵn sàng dùng.

Module chính của Spring Framework

ModuleVai trò
spring-coreNền tảng, util, type conversion
spring-beansBeanFactory, định nghĩa bean
spring-contextApplicationContext, event, SpEL
spring-aopAOP proxy
spring-expressionSpEL
spring-testTest context, mock

“Spring Core” ở đây gồm core + beans + context + aop. Web, Data, Security là các module khác (file riêng).

Spring Framework 6 — điểm khác

  • Yêu cầu Java 17+
  • Namespace chuyển từ javax.* sang jakarta.*
  • @PostConstruct/@PreDestroy lấy từ jakarta.annotation, không còn javax.annotation
  • Hỗ trợ AOT + GraalVM native image (build-time processing)
  • Observation API + Micrometer cho tracing/metrics (Spring Boot 3)
  • @MockBean deprecate → dùng @MockitoBean (Spring Boot 3.4+)

Nếu dự án cũ dùng javax.annotation.PostConstruct, nâng lên Spring 6 sẽ không chạy init callback — lỗi phổ biến khi migrate.

Bốn câu mở đầu hay gặp

  1. Spring là gì, IoC và DI khác nhau thế nào?
  2. Bean là gì, làm sao khai báo?
  3. BeanFactory và ApplicationContext khác gì?
  4. Constructor, setter và field injection nên dùng loại nào?

Gợi ý: trả lời ngắn gọn rồi dẫn sang một ví dụ bạn đã dùng thật — điều này tạo ấn tượng hơn kể tên.

Container

Trái tim của Spring. Nắm BeanFactory vs ApplicationContext và cách khởi tạo context là trả lời được cả loạt câu hỏi nền tảng.

BeanFactory vs ApplicationContext

BeanFactoryApplicationContext
Khởi tạo singletonLazy — chỉ tạo khi getBean()Eager — tạo hết singleton lúc khởi động
BeanPostProcessorPhải đăng ký thủ côngTự động đăng ký + kích hoạt
i18n / resourceKhôngMessageSource, Resource loader
EventKhôngApplicationEventPublisher
Web contextKhôngCó (WebApplicationContext)

ApplicationContext extends BeanFactory và là lựa chọn chuẩn. Eager init giúp phát hiện lỗi cấu hình (thiếu bean, cycle) ngay lúc start thay vì lúc chạy.

Khởi tạo context

Trong Spring Boot, bạn hiếm khi tự new context — SpringApplication.run() làm việc đó, tạo ServletWebServerApplicationContext.

Context hierarchy

Thành phầnVai trò
Root contextBean dùng chung: service, repository, datasource
Child contextMỗi DispatcherServlet một context con (controller…)
  • Context con nhìn thấy bean của context cha
  • Context cha không thấy bean của con
  • Spring Boot thường chỉ dùng một context duy nhất

getBean và lỗi tương ứng

Gọi getBean() trực tiếp là “anti-pattern” trong app code — nên dùng DI. Nó chỉ hợp lúc bootstrap, test, hoặc truy cập bean động.

Bean registry — chuyện bên trong

  • Mỗi bean có một BeanDefinition (class, scope, lazy, init/destroy, dependencies…)
  • Bean được lưu trong DefaultListableBeanFactory
  • singletonObjects (level-1 cache) giữ instance đã khởi tạo xong
  • Bean name mặc định: tên method (@Bean) hoặc tên class viết thường chữ đầu (@Component)

Định nghĩa bean

Có hai cách khai báo bean: stereotype annotation trên class, và @Bean trong @Configuration. Biết khi nào dùng cái nào.

Stereotype annotation

AnnotationDùng cho
@ComponentComponent chung
@ServiceTầng nghiệp vụ (ngữ nghĩa rõ ràng)
@RepositoryDAO; dịch lỗi persistence → DataAccessException
@Controller / @RestControllerWeb (file Boot & Web)

Ba annotation kia đều được meta-annotated bằng @Component — về chức năng như nhau, khác ở ngữ nghĩa và xử lý riêng.

@Component vs @Bean

@Component@Bean
Vị tríTrên classTrên method trong config
Phát hiệnTự động qua component scanKhai báo tường minh
Phù hợpClass do bạn viếtClass thư viện bên ngoài, hoặc cần tuỳ biến lúc tạo

Class của bên thứ ba (DataSource, ObjectMapper, RestTemplate) không thể gắn @Component → phải dùng @Bean.

Java config mẫu

Tham số của method @Bean được Spring tự inject (theo type/name như bình thường). Không cần viết @Autowired trên method @Bean.

@Configuration full vs lite mode

Full mode: class @Configuration bị CGLIB subclass để chặn lời gọi @Bean chéo, đảm bảo trả về cùng singleton.

@ComponentScan

  • Mặc định quét từ package của class config trở xuống
  • @SpringBootApplication đã bọc sẵn @ComponentScan
  • Quét quá rộng → chậm start; quét hẹp → thiếu bean

Tên bean, alias và đụng độ

  • Tên mặc định: class CreditCardPayment → creditCardPayment
  • Hai bean cùng tên → bean định nghĩa sau ghi đè (hoặc lỗi nếu allow-bean-definition-overriding=false)
  • Hai bean cùng type mà không có @Primary/@Qualifier → lỗi lúc inject

Dependency Injection

Ba kiểu injection và cách Spring phân giải @Autowired khi có nhiều ứng viên — phần được hỏi nhiều nhất sau khái niệm IoC.

Ba kiểu injection

KiểuƯuNhược
ConstructorImmutable, dependency bắt buộc rõ ràng, dễ testConstructor dài khi nhiều dependency
SetterTùy chọn, thay đổi sau initKhông immutable, dễ sót dependency
FieldGọnKhó test, cần reflection, không final, dễ NPE

@Autowired phân giải thế nào

  1. Tìm bean theo type
  2. Nhiều ứng viên → khớp theo name (tên field/tham số)
  3. Vẫn mơ hồ → dùng @Primary hoặc @Qualifier
  4. Không có bean → NoSuchBeanDefinitionException (trừ khi required=false)

Spring chọn @Primary khi không có @Qualifier; nếu có @Qualifier thì qualifier thắng primary.

@Qualifier và @Primary

@Qualifier nhận tên bean (mặc định là tên class viết thường chữ đầu). Có thể tạo qualifier tùy biến bằng @Qualifier trên annotation khác.

Optional, collection và lazy

Muốn tiêm theo thứ tự, đánh @Order(n) trên từng bean — Spring sắp xếp collection theo đó.

Vì sao constructor injection tốt hơn

  • Field có thể final → immutable, an toàn
  • Dependency bắt buộc hiện rõ trong chữ ký
  • Test bằng new OrderService(mock) không cần Spring
  • Lỗi thiếu dependency nổ ngay lúc khởi động
  • Tránh reflection và NPE của field injection

Field injection khiến class khó kiểm thử độc lập và che giấu sự phình to dependency. Nếu constructor quá dài, đó là tín hiệu nên tách class.

@Resource và @Inject (JSR)

@AutowiredSpring, theo type rồi name
@ResourceJSR-250, theo name trước (có thể chỉ định)
@InjectJSR-330, theo type; kèm @Named thay cho qualifier

Trong dự án Spring, cứ dùng @Autowired + constructor cho nhất quán; JSR annotation chỉ khi cần chuẩn chung với CDI.

Bean scope

Scope quyết định vòng đời của bean. Câu hỏi kinh điển: prototype tiêm vào singleton thì tạo mấy lần?

Các scope

ScopeHành vi
singletonMặc định — 1 instance cho cả container
prototypeMỗi lần yêu cầu/inject tạo instance mới
request1 instance cho mỗi HTTP request
session1 instance cho mỗi HTTP session
application1 instance cho mỗi ServletContext
websocket1 instance cho mỗi WebSocket session

Bẫy: prototype tiêm vào singleton

Dependency được inject một lần duy nhất lúc singleton khởi tạo, nên prototype không được tạo mới cho mỗi lần dùng. Đây là nhầm lẫn phổ biến nhất về scope.

Web scope

Web scope cần proxy vì singleton (controller) tham chiếu tới bean ngắn hạn. Nếu không có proxy sẽ lỗi ngay lúc khởi tạo.

Singleton có thread-safe không?

  • Singleton là 1 instance chia sẻ cho mọi thread
  • Spring không tự đồng bộ — thread-safety là trách nhiệm của bạn
  • Nguyên tắc: bean singleton nên stateless (không giữ state mutable)
  • Có state → dùng scope ngắn hơn hoặc tự đồng bộ/immutable

Giữ private int counter; rồi tăng trong method của bean singleton là race condition điển hình.

Vòng đời bean

Thứ tự: constructor → inject → @PostConstruct → sẵn sàng → @PreDestroy. BeanPostProcessor là “phép màu” đằng sau mọi thứ.

Sơ đồ vòng đời

Constructor new instance Inject dependencies @PostConstruct afterPropertiesSet Ready sử dụng bình thường @PreDestroy dispose / shutdown BeanPostProcessor quấn quanh bước khởi tạo postProcessBeforeInitialization → @PostConstruct → postProcessAfterInitialization (nơi sinh proxy AOP) mọi bean đều đi qua

Vì BeanPostProcessor chạy cho mọi bean, nó là nền tảng của @Autowired, @Transactional, proxy AOP. Prototype bean không nhận @PreDestroy (container không giữ reference).

Callback khởi tạo / hủy

Thứ tự chi tiết

  1. Bean được instatiate (constructor)
  2. Inject dependencies (field/setter)
  3. Gọi các Aware interface (BeanNameAware, ApplicationContextAware…)
  4. BeanPostProcessor.postProcessBeforeInitialization
  5. @PostConstruct / afterPropertiesSet()
  6. BeanPostProcessor.postProcessAfterInitialization
  7. Bean sẵn sàng; khi shutdown → @PreDestroy / destroy()

Không gọi logic phụ thuộc dependency chưa inject trong constructor — để vào @PostConstruct.

BeanPostProcessor

Nếu muốn xử lý định nghĩa bean (trước khi instance tồn tại), dùng BeanFactoryPostProcessor — ví dụ PropertySourcesPlaceholderConfigurer xử lý ${...}.

Khi nào callback không chạy

  • Prototype bean: @PreDestroy không được gọi tự động
  • Dùng @Bean(initMethod=...) + @Component có cả hai → chạy cả hai
  • Nhầm package javax.annotation (cũ) → callback bị bỏ qua âm thầm
  • Bean lười (@Lazy) chỉ chạy init khi được dùng lần đầu

Cấu hình & properties

Ba cách lấy cấu hình: @Value (đơn giản), @ConfigurationProperties (gõ mạnh, có nhóm), và SpEL. Kèm theo @Profile và @Conditional.

@Value và SpEL

${...} là property placeholder, #{...} là SpEL. Trộn được: #{'${app.env}' + '!'}.

@ConfigurationProperties (gõ mạnh)

Ưu tiên @ConfigurationProperties hơn nhiều @Value rải rác: gõ mạnh, có nhóm, validate được bằng Bean Validation, và relaxed binding (app.my-host ↔ myHost).

Thứ tự ưu tiên properties

  1. Command line arguments (--app.name=x)
  2. SPRING_APPLICATION_JSON
  3. Java System properties
  4. OS environment variables
  5. application-{profile}.yml
  6. application.yml
  7. @PropertySource / giá trị mặc định

(Đầy đủ nhất khi dùng Spring Boot; bản chất là các PropertySource xếp chồng, nguồn trên ghi đè nguồn dưới.)

@Profile

Kích hoạt: spring.profiles.active=dev hoặc --spring.profiles.active=dev; trong test dùng @ActiveProfiles("dev").

@Conditional

Spring Boot mở rộng: @ConditionalOnProperty, @ConditionalOnClass, @ConditionalOnMissingBean — nền tảng của auto-configuration.

Environment & PropertySource

  • Environment = tập PropertySource + active profiles
  • PropertySource: file yml/properties, system, env vars…
  • Tiêm Environment env rồi env.getProperty("app.name")
  • @PropertySource("classpath:custom.properties") thêm nguồn riêng

Circular dependency

A cần B, B cần A. Spring có thể “chữa” một phần nhờ 3-level cache, nhưng tốt nhất là đừng tạo ra nó.

Vấn đề

Vòng phụ thuộc qua constructor gần như không thể giải — Spring ném BeanCurrentlyInCreationException. Vòng qua setter/field có thể được “vá” nhờ tham chiếu sớm.

Spring xử lý bằng 3-level cache

CacheChứa gì
Level 1 — singletonObjectsBean đã khởi tạo xong
Level 2 — earlySingletonObjectsTham chiếu sớm (bean đang tạo)
Level 3 — singletonFactoriesObjectFactory để tạo tham chiếu sớm

Khi B cần A mà A chưa xong, Spring đưa tham chiếu sớm của A (level 3 → level 2) vào B. Vì vậy setter/field cycle mới tồn tại được.

Cách xử lý

Nên tránh

  • Cycle là mùi thiết kế: hai class dính chặt vào nhau
  • Spring Boot 2.6+ cấm circular reference mặc định
  • Giải pháp đúng là cấu trúc lại: tách responsibility, đưa logic chung ra bean thứ ba
  • @Lazy chỉ là phương án tình thế, không phải kiến trúc

Bật lại bằng spring.main.allow-circular-references=true là che giấu vấn đề, đừng làm trong code mới.

Proxy & AOP

@Transactional, @Async, @Cacheable đều hoạt động nhờ proxy AOP. Hiểu proxy và bẫy self-invocation là hiểu vì sao chúng “đôi khi không chạy”.

Thuật ngữ AOP

AspectMô-đun chứa cross-cutting concern
AdviceHành động (before/after/around)
PointcutBiểu thức chọn các join point
Join pointĐiểm thực thi (method call…)
TargetObject bị áp dụng
ProxyObject thay thế, bọc lời gọi
WeavingGhép aspect vào code

Các loại advice

@Around mạnh nhất (đo thời gian, retry, cache) nhưng phải gọi pjp.proceed() và trả về đúng giá trị, nếu không method gốc không chạy.

Pointcut thường dùng

execution(* c.a.s.*.*(..))mọi method public của service
within(c.a.s..*)mọi type trong package service
@annotation(Transactional)method có annotation đó
bean(*Service)bean có tên kết thúc “Service”
args(String, ..)tham số đầu là String

execution là linh hoạt nhất: return-type class.method(arguments); .. = bất kỳ tham số, * = bất kỳ tên.

JDK dynamic proxy vs CGLIB

JDK dynamic proxyCGLIB
Cơ chếjava.lang.reflect.Proxy theo interfaceSinh class con bằng bytecode
Yêu cầuBean phải có interfaceClass không được final; method không final/private
Khi chọnproxyTargetClass=false (mặc định @EnableAspectJAutoProxy)proxyTargetClass=true; Spring Boot bật sẵn

Spring Boot đặt proxyTargetClass=true mặc định nên hầu hết bean được proxy bằng CGLIB — vì vậy gọi qua interface hay class đều “bắt” được advice.

Bẫy self-invocation — kinh điển

Proxy chỉ can thiệp khi lời gọi từ bên ngoài đi vào. Gọi this.method() là gọi thẳng đối tượng thật, bỏ qua mọi advice.

@Transactional nhìn từ AOP

  • @Transactional được xử lý bởi TransactionInterceptor — một Advice
  • Mở tx trước method, commit/rollback sau — tất cả qua proxy
  • Vì vậy self-invocation, method private, gọi trên new đều mất transaction
  • Chi tiết propagation/isolation/rollback → xem file Spring Data JPA & Hibernate

Events

Dùng event để nới lỏng coupling: publish từ nơi xảy ra, listen ở nơi quan tâm — không cần biết nhau.

Publish & listen

Không cần implement ApplicationListener nữa — @EventListener là đủ. Một listener có thể nhận nhiều kiểu event.

@TransactionalEventListener

  • AFTER_COMMIT — phổ biến nhất (gửi mail, đẩy queue sau khi lưu xong)
  • AFTER_ROLLBACK, BEFORE_COMMIT, AFTER_COMPLETION
  • Nếu không có transaction, event bị bỏ qua (fallbackExecution=true để vẫn chạy)

Đồng bộ hay bất đồng bộ

  • Mặc định: đồng bộ — cùng thread, cùng transaction với nơi publish
  • Bất đồng bộ: thêm @Async + @EnableAsync
  • Async listener chạy thread khác → không còn nằm trong transaction gốc
  • Thứ tự nhiều listener cùng event: @Order

Khi nào nên dùng event

  • Mở rộng hành vi mà không sửa code gốc (audit, thông báo, thống kê)
  • Nới lỏng coupling giữa các module
  • Tránh dùng cho luồng chính: event làm logic khó theo dõi, khó debug
  • Không lạm dụng async event cho dữ liệu cần nhất quán tức thời

Testing

Spring Test cung cấp context cho test và cache context để chạy nhanh. Biết cái này để viết test gọn và không bị “test chậm”.

Setup context cho test

Dùng JUnit 5 + spring-test. @ContextConfiguration chỉ load đúng config bạn khai báo — nhanh hơn full context.

Mock bean

@MockBean bị deprecate từ Spring Boot 3.4 → dùng @MockitoBean. Mỗi mock khác nhau tạo một context cache khác nhau, nên đặt mock nhất quán giữa các test.

Context caching

  • Spring cache context theo key: config class + profiles + properties…
  • Các test dùng cùng key → dùng lại context → nhanh hơn nhiều
  • Test làm “bẩn” context (đổi bean, đổi state) → @DirtiesContext để reset
  • Context chỉ tạo lại khi key khác nhau

Đây là lý do nên giữ cấu hình test ổn định: càng ít biến thể context, test càng nhanh.

Tùy biến cho riêng test

Core vs Boot testing

@ContextConfigurationChỉ load config chỉ định — Spring Core
@SpringBootTestLoad full application context — Boot (file riêng)
@WebMvcTestChỉ tầng web + MockMvc — Boot & Web

Trong dự án Boot thực tế, bạn sẽ dùng @SpringBootTest là chủ yếu; mục này để phân biệt ranh giới khái niệm.

Bẫy & checklist

Tổng hợp lỗi mà người phỏng vấn hay cài vào để kiểm tra bạn có thực sự hiểu cơ chế proxy và scope hay không.

Mười bẫy kinh điển

BẫyChuyện xảy raXử lý
Prototype trong singletonPrototype chỉ tạo 1 lầnproxyMode hoặc ObjectProvider
Self-invocationthis.txMethod() mất @TransactionalTách class / self-injection
Field injectionKhó test, không final, dễ NPEConstructor injection
Nhiều bean cùng typeNoUniqueBeanDefinitionException@Primary / @Qualifier
javax.annotation@PostConstruct không chạy trên Spring 6Chuyển sang jakarta.annotation
@Configuration liteGọi @Bean chéo tạo nhiều instanceDùng @Configuration full mode
Singleton có stateRace condition giữa các threadBean stateless / immutable
Constructor cycleBeanCurrentlyInCreationExceptionRefactor, @Lazy
Async thiếu @EnableAsync@Async vẫn chạy đồng bộThêm @EnableAsync
Gọi getBean() trong serviceBỏ qua DI, code khó testTiêm qua constructor

Nên

Dùng constructor injection, field final.
Bean singleton để stateless.
@Configuration cho config, @Component cho class của bạn.
@ConfigurationProperties thay nhiều @Value rải rác.
Tách class khi constructor quá 5–6 tham số.
Dùng @Qualifier/@Primary khi nhiều implementation.
Đặt logic khởi tạo vào @PostConstruct, không phải constructor.
Tách cross-cutting concern bằng AOP thay vì copy-paste.
Giữ cấu hình test ổn định để tận dụng context cache.

Tránh

Field injection (trừ legacy code).
Circular dependency qua constructor.
Prototype tiêm trực tiếp vào singleton mà không proxy.
Gọi this.@Transactional() nội bộ.
Bean singleton giữ state mutable.
Bắt NoUniqueBeanDefinitionException bằng cách đoán tên.
@Lazy để “chữa” mọi cycle thay vì refactor.
Gọi ctx.getBean() trong business code.
Quét component scan quá rộng.
Dùng @Value cho object phức tạp (nên @ConfigurationProperties).

Checklist tự đánh giá

MứcBạn cần trả lời được
JuniorIoC/DI là gì, bean là gì, @Component vs @Bean, constructor vs field injection, @Autowired/@Qualifier, default scope
MiddleBeanFactory vs ApplicationContext, vòng đời bean, BeanPostProcessor, prototype trap, @Configuration full/lite, circular dependency, proxy JDK/CGLIB + self-invocation
SeniorThiết kế context, @Conditional/@Profile chiến lược, AOP pointcut tinh chỉnh, event + transaction boundary, chiến lược testing & context cache, trade-off khi chọn giải pháp

Hỏi nhanh đáp gọn

Tự trả lời trước khi mở. Câu nào phải mở mới nhớ, đánh dấu lại mục tương ứng ở trên.

IoC (Inversion of Control) là gì?

Đảo ngược quyền kiểm soát: thay vì code tự new và quản lý object, bạn giao việc đó cho framework/container. Spring container tạo object (bean), nối dependency, quản lý vòng đời — code chỉ khai báo và nhận.

DI khác IoC thế nào?

IoC là nguyên tắc chung (đừng tự tạo object). DI là một cách hiện thực của IoC: dependency được truyền vào từ ngoài (constructor/setter/field) thay vì tự tìm hay khởi tạo bên trong.

BeanFactory và ApplicationContext khác gì?

BeanFactory là interface nền, lazy (tạo bean khi getBean), ít tính năng. ApplicationContext extends nó, eager-init singleton lúc start, tự kích hoạt BeanPostProcessor, có i18n, event, resource loading, web context. Dùng ApplicationContext là chuẩn.

Bean là gì, khai báo thế nào?

Bean là object do Spring container quản lý. Khai báo bằng stereotype annotation (@Component/@Service/@Repository) cho class của bạn, hoặc @Bean trong @Configuration cho class thư viện ngoài.

@Component khác @Bean ở đâu?

@Component đặt trên class, được component scan tự phát hiện. @Bean đặt trên method trong config, khai báo tường minh — dùng khi class không phải của bạn hoặc cần logic tạo phức tạp.

Constructor, setter hay field injection?

Ưu tiên constructor: field có thể final, dependency bắt buộc rõ ràng, dễ test bằng new. Setter cho dependency tùy chọn. Field injection gọn nhưng khó test, không final, dễ NPE — tránh dùng.

@Autowired phân giải thế nào khi nhiều bean cùng type?

Theo thứ tự: type → nếu nhiều thì khớp theo tên field/param → nếu vẫn mơ hồ dùng @Primary (ưu tiên mặc định) hoặc @Qualifier (chọn tường minh, thắng primary). Không có bean → NoSuchBeanDefinitionException.

@Primary và @Qualifier khác gì?

@Primary đánh dấu bean ưu tiên khi nhiều ứng viên cùng type, áp dụng tự động khi không có chỉ định. @Qualifier("tên") chọn đúng bean theo tên tại điểm inject và ghi đè @Primary.

@Service, @Repository khác @Component thế nào?

Cả ba đều được meta-annotate bằng @Component, chức năng như nhau. Khác ở ngữ nghĩa và một xử lý riêng: @Repository dịch lỗi persistence thành DataAccessException; @Service/@Controller chủ yếu để phân tầng rõ ràng.

Scope mặc định là gì, prototype trap ra sao?

Mặc định singleton (1 instance/container). Prototype tạo mới mỗi lần yêu cầu, nhưng nếu tiêm prototype vào singleton thì chỉ tạo 1 lần khi singleton init. Fix bằng proxyMode hoặc ObjectProvider.

Vòng đời bean gồm những bước nào?

Constructor → inject dependency → Aware callbacks → BeanPostProcessor.before → @PostConstruct → BeanPostProcessor.after → sẵn sàng → khi shutdown @PreDestroy. Prototype không nhận @PreDestroy.

@PostConstruct vs InitializingBean vs init-method?

@PostConstruct (jakarta.annotation) là cách chuẩn, không coupling Spring. InitializingBean.afterPropertiesSet() và @Bean(initMethod=...) tương đương nhưng gắn chặt vào Spring/XML. Ưu tiên @PostConstruct.

@Configuration full mode và lite mode khác gì?

Full mode (class có @Configuration) bị CGLIB subclass nên lời gọi @Bean chéo trả về cùng instance singleton. Lite mode (@Component/method @Bean) không proxy, gọi chéo tạo instance mới.

Circular dependency xử lý thế nào?

Constructor cycle → BeanCurrentlyInCreationException. Setter/field cycle có thể vá nhờ 3-level cache (tham chiếu sớm). Nhưng cycle là mùi thiết kế: nên refactor, hoặc dùng @Lazy/ObjectProvider tạm thời. Boot 2.6+ cấm mặc định.

Proxy cơ chế: JDK vs CGLIB, self-invocation là gì?

Spring AOP tạo proxy: JDK dynamic proxy theo interface, hoặc CGLIB subclass (không proxy final/private). Self-invocation là khi gọi this.method() bên trong class — lời gọi không qua proxy nên advice/@Transactional mất tác dụng.

AOP dùng khi nào, @Around khác @Before?

AOP cho cross-cutting concern: log, đo thời gian, transaction, security. @Before chỉ chạy trước method; @Around bao quanh, kiểm soát proceed() (đo thời gian, retry, cache) — mạnh nhất nhưng phải gọi proceed().

@Transactional hoạt động qua cái gì?

Qua proxy AOP (TransactionInterceptor): mở transaction trước method, commit/rollback sau. Vì vậy self-invocation, method private, hoặc gọi trên new đều không có transaction. Chi tiết propagation/isolation xem file JPA/Hibernate.

@Value và @ConfigurationProperties khác gì?

@Value("${...}") đọc từng giá trị, đơn giản nhưng rời rạc. @ConfigurationProperties(prefix=...) gom nhóm vào class/record gõ mạnh, có relaxed binding và validate được — ưu tiên cho cụm cấu hình.

@Profile và @Conditional dùng để làm gì?

@Profile("dev") kích hoạt bean theo profile đang chạy (spring.profiles.active). @Conditional tổng quát hơn, quyết định bằng điều kiện tự viết; Boot mở rộng thành @ConditionalOnProperty/@ConditionalOnClass…

Singleton bean có thread-safe không?

Không tự động. Singleton là 1 instance chia sẻ mọi thread; Spring không đồng bộ giúp bạn. Vì vậy singleton nên stateless; nếu có state phải tự đồng bộ, dùng immutable, hoặc chuyển scope ngắn hơn.

BeanPostProcessor vs BeanFactoryPostProcessor?

BeanPostProcessor chạy sau khi instance tồn tại, quanh bước khởi tạo của mọi bean — nền của @Autowired, proxy. BeanFactoryPostProcessor chạy trước, sửa bean definition (ví dụ xử lý ${...}).

Events dùng thế nào, @TransactionalEventListener?

Publish bằng ApplicationEventPublisher.publishEvent, lắng nghe bằng @EventListener. @TransactionalEventListener(AFTER_COMMIT) chỉ chạy sau khi transaction commit — hợp cho gửi mail/đẩy queue sau khi lưu dữ liệu.

Context caching trong test là gì?

Spring Test cache context theo key (config + profiles + properties). Các test cùng key dùng lại context thay vì tạo mới → chạy nhanh. Test làm bẩn context thì đánh @DirtiesContext để tạo lại.

@MockBean và @MockitoBean khác gì?

Cùng mục đích: thay bean thật bằng mock trong context test. @MockBean bị deprecate từ Spring Boot 3.4 → chuyển sang @MockitoBean. Đổi mock làm đổi key context cache.

Spring 6 thay đổi gì quan trọng?

Yêu cầu Java 17+, namespace chuyển javax.* → jakarta.* (nên @PostConstruct lấy từ jakarta.annotation), hỗ trợ AOT/GraalVM native, quan sát bằng Micrometer. Migrate nhầm package là lỗi phổ biến.

Luyện trắc nghiệm

12 câu đúng kiểu bẫy phỏng vấn. Chọn đáp án để xem giải thích ngay — điểm và kỷ lục được lưu trên máy bạn.

Kiểm tra nhanh

Đúng 0/12 · Kỷ lục 0
Chọn đáp án — giải thích sẽ hiện bên dưới câu hỏi.