Skip to content

iq-dev-lab/spring-core-deep-dive

Repository files navigation

🌱 Spring Core Deep Dive

"스프링 컨테이너가 Bean을 관리하는 모든 비밀"


"스프링을 사용하는 것과, 스프링이 어떻게 작동하는지 아는 것은 다르다"

IoC 컨테이너 구조부터 AOP 바이트코드, 트랜잭션 프록시, SpEL 파싱까지
왜 이렇게 설계됐는가 라는 질문으로 Spring 내부를 끝까지 파헤칩니다


GitHub Java Spring Docs License


🎯 이 레포에 대하여

Spring에 관한 자료는 넘쳐납니다. 하지만 대부분은 "어떻게 쓰나" 에서 멈춥니다.

일반 자료 이 레포
"@Transactional을 붙이면 트랜잭션이 시작됩니다" CGLIB 프록시가 생성되고 TransactionInterceptor가 Advice 체인에 끼어드는 과정
"@Autowired로 의존성을 주입받습니다" AutowiredAnnotationBeanPostProcessor가 리플렉션으로 필드를 채우는 내부 코드
"순환 참조는 에러가 납니다" 3단계 캐시(singletonObjects / earlySingletonObjects / singletonFactories)로 해결되는 메커니즘
"private 메서드에는 AOP가 안 됩니다" JDK Proxy와 CGLIB의 바이트코드 차이, 왜 오버라이딩 기반인가
"@Configuration@Component와 비슷합니다" CGLIB 프록시가 씌워져 @Bean 메서드 호출을 가로채는 Full Mode vs Lite Mode
이론 나열 실행 가능한 코드 + Spring 소스코드 직접 추적 + javap 바이트코드 분석

🚀 빠른 시작

각 챕터의 첫 문서부터 바로 학습을 시작하세요!

IoC Container DI Internals Bean Lifecycle AOP Component Scan Configuration Events SpEL


📚 전체 학습 지도

💡 각 섹션을 클릭하면 상세 문서 목록이 펼쳐집니다


🔹 Chapter 1: IoC Container Architecture

핵심 질문: 스프링 컨테이너는 어떻게 Bean을 찾고, 만들고, 관리하는가?

BeanFactory부터 ApplicationContext까지, 컨테이너 내부 해부 (6개 문서)
문서 다루는 내용
01. BeanFactory vs ApplicationContext 인터페이스 계층 구조, getBean() 내부 동작, Lazy vs Eager 초기화 결정 지점
02. BeanDefinition과 Bean 메타데이터 BeanDefinition 인터페이스 필드 전체, XML / 어노테이션 / Java Config → BeanDefinition 변환 과정
03. BeanPostProcessor 체인 postProcessBeforeInitialization / After 호출 순서, @Autowired@Transactional이 이 체인에서 처리되는 방식
04. ApplicationContext 계층 구조 Parent-Child 컨텍스트 분리 이유, Spring MVC의 Root / Servlet Context 구조
05. Environment & PropertySource 우선순위 PropertySource 체인, OS 환경변수 / JVM 프로퍼티 / application.yml 우선순위 결정 메커니즘
06. Resource Abstraction 패턴 Resource 인터페이스 계층, classpath: / file: / http: prefix 처리, ResourceLoader 전략 패턴

🔹 Chapter 2: Dependency Injection Internals

핵심 질문: @Autowired는 어떻게 타입을 찾고, 어떻게 주입하는가?

주입 방식의 차이와 의존성 해결 메커니즘 완전 분석 (7개 문서)
문서 다루는 내용
01. 생성자 vs 필드 vs 세터 주입 바이트코드 비교 javap로 생성자 주입 vs 리플렉션 필드 주입 바이트코드 차이 실측, 불변성과 테스트 용이성의 기술적 근거
02. 순환 참조 해결 — 3단계 캐시 singletonObjects / earlySingletonObjects / singletonFactories 각 캐시의 역할, 생성자 주입에서 왜 해결이 안 되는가
03. @Autowired vs @Inject vs @Resource JSR-330 vs Spring 어노테이션, 탐색 순서(타입 → 이름) 차이, AutowiredAnnotationBeanPostProcessor 처리 경로
04. @Qualifier & @Primary 우선순위 동일 타입 Bean 다수 존재 시 해결 알고리즘, @Qualifier@Primary → Bean 이름 폴백 순서
05. Optional Dependency 처리 required=false / Optional<T> / @Nullable / ObjectProvider<T> 각각의 내부 처리 차이
06. Constructor Binding의 장점 불변 객체 보장 원리, @ConfigurationProperties에서 생성자 바인딩이 권장되는 이유
07. Lazy Initialization 동작 원리 @Lazy 어노테이션이 만드는 Proxy, DefaultListableBeanFactory의 Lazy 처리 경로

🔹 Chapter 3: Bean Lifecycle Deep Dive

핵심 질문: new MyBean()이 아닌 스프링이 Bean을 만들 때, 정확히 어떤 순서로 무슨 일이 벌어지는가?

Instantiation부터 Destruction까지, Bean의 일생 전체 추적 (7개 문서)
문서 다루는 내용
01. Bean 생성 전체 과정 Instantiation → Populate Properties → Aware 체인 → BeanPostProcessor → Init Method 전체 흐름, AbstractAutowireCapableBeanFactory 소스 추적
02. @PostConstruct vs InitializingBean 실행 순서 @PostConstruct(JSR-250) vs InitializingBean.afterPropertiesSet() vs init-method 실행 순서와 이유
03. @PreDestroy vs DisposableBean 정리 과정 컨테이너 종료 시 소멸 콜백 순서, ShutdownHook 등록 원리, 소멸이 보장되지 않는 경우
04. Aware 인터페이스 체인 BeanNameAware / BeanFactoryAware / ApplicationContextAware 호출 시점, 컨테이너 인프라 접근의 트레이드오프
05. Bean Scope와 프록시 Singleton / Prototype / Request / Session Scope 생명주기 차이, Prototype을 Singleton에 주입할 때의 함정과 Scope Proxy 해결 원리
06. FactoryBean vs ObjectProvider FactoryBean<T> 패턴의 존재 이유, &로 FactoryBean 자체를 가져오는 방법, ObjectProvider로 지연/선택적 조회
07. 순환 의존 내부 해결 과정 3단계 캐시 + ObjectFactory 조합으로 순환 참조가 해결되는 단계별 시나리오 코드 추적

🔹 Chapter 4: AOP Implementation

핵심 질문: @Transactional은 어떻게 트랜잭션을 시작하고 커밋하는가? 왜 private에서는 안 되는가?

JDK Proxy부터 CGLIB까지, 프록시 기반 AOP의 비밀 (9개 문서)
문서 다루는 내용
01. JDK Dynamic Proxy vs CGLIB 바이트코드 비교 Proxy.newProxyInstance() vs ASM 바이트코드 생성, javap로 두 프록시의 클래스 구조 비교
02. ProxyFactoryBean 내부 구조 ProxyFactoryBeanProxyFactoryAopProxy 생성 체인, AdvisedSupport 메타데이터
03. @AspectJ 어노테이션 처리 과정 AnnotationAwareAspectJAutoProxyCreator@AspectAdvisor로 변환하는 과정
04. Pointcut Expression 파싱과 매칭 execution() / within() / @annotation() 파싱 원리, AspectJExpressionPointcut 내부 매처
05. Advice 체인 실행 순서 AroundBeforeAfterReturning / AfterThrowing 실행 순서, ReflectiveMethodInvocation.proceed() 재귀 호출 구조
06. @Transactional이 프록시인 이유 TransactionInterceptor가 Advice 체인에 들어가는 과정, PlatformTransactionManager 위임 구조
07. private 메서드에 AOP가 안 되는 이유 오버라이딩 불가 → 프록시 적용 불가의 바이트코드 수준 설명, 같은 클래스 내부 메서드 호출(Self-Invocation) 함정
08. Proxy 성능 비교 JDK Proxy vs CGLIB vs AspectJ Weaving 성능 실측 (JMH), Spring Boot 기본값이 CGLIB인 이유
09. @Cacheable의 AOP 내부 구조 CacheInterceptor가 Advice 체인에 들어가는 과정, @Transactional과 동일한 AOP 패턴 재사용, CacheAspectSupport SpEL 키 평가, @CachePut / @CacheEvict 동작 차이

🔹 Chapter 5: Component Scanning

핵심 질문: @ComponentScan은 어떻게 수백 개의 클래스 중 Bean 후보를 찾아내는가?

클래스패스 스캔부터 BeanDefinition 등록까지 (6개 문서)
문서 다루는 내용
01. @ComponentScan 동작 원리 ClassPathBeanDefinitionScanner 스캔 흐름, ASM 기반 메타데이터 리더로 클래스 로딩 없이 어노테이션 탐색하는 방법
02. ClassPathScanningCandidateComponentProvider 후보 컴포넌트 필터링 알고리즘, @Component 계층 어노테이션(@Service, @Repository) 인식 방식
03. TypeFilter Include & Exclude includeFilters / excludeFilters 적용 순서, AssignableTypeFilter / AnnotationTypeFilter / Custom Filter 구현
04. @Conditional 평가 과정 ConditionEvaluator@Conditional을 평가하는 시점과 순서, @ConditionalOnClass 내부 구현
05. BeanDefinition 등록 과정 스캔 결과가 BeanDefinitionRegistry에 등록되는 과정, 중복 등록 충돌 해결 규칙
06. 컴포넌트 인덱스를 통한 스캔 최적화 spring.components 인덱스 파일, @Indexed 어노테이션, 대규모 프로젝트에서 스캔 시간을 줄이는 원리

🔹 Chapter 6: Configuration Classes

핵심 질문: @Configuration@Component와 다른 이유는 무엇인가? CGLIB는 왜 필요한가?

Java Config의 내부 구현과 CGLIB 프록시의 역할 (6개 문서)
문서 다루는 내용
01. @Configuration vs @Component Full Mode vs Lite Mode 차이, @Bean 메서드를 직접 호출하면 생기는 일
02. CGLIB 프록시가 적용되는 이유 ConfigurationClassEnhancer가 CGLIB 서브클래스를 만드는 과정, $$EnhancerBySpringCGLIB 클래스 분석
03. @Bean 메서드 호출 가로채기 BeanMethodInterceptor가 이미 등록된 Bean을 반환하는 메커니즘, Singleton 보장이 이루어지는 지점
04. Lite Mode vs Full Mode 트레이드오프 CGLIB 없는 Lite Mode의 성능상 이점과 Singleton 보장 불가 트레이드오프, 선택 기준
05. @Import의 3가지 방식 일반 클래스 Import / ImportSelector / ImportBeanDefinitionRegistrar 각각의 처리 시점과 활용 사례
06. ImportSelector & ImportBeanDefinitionRegistrar AutoConfigurationImportSelector(Spring Boot 자동 설정)가 동작하는 원리, 조건부 Bean 등록 패턴

🔹 Chapter 7: Event & Listener

핵심 질문: ApplicationEvent는 어떻게 발행되고, 리스너는 어떻게 호출되는가?

스프링 이벤트 시스템의 발행-구독 메커니즘 (5개 문서)
문서 다루는 내용
01. ApplicationEvent 발행-구독 메커니즘 ApplicationEventMulticaster 내부 구조, publishEvent() 호출 시 리스너 탐색 및 호출 흐름
02. @EventListener vs ApplicationListener EventListenerMethodProcessor@EventListener 메서드를 ApplicationListener로 변환하는 과정
03. 비동기 이벤트 처리 @Async @Async와 결합된 비동기 이벤트의 스레드 분리 원리, SimpleApplicationEventMulticaster 스레드 풀 설정
04. 이벤트 실행 순서 보장 @Order / Ordered 인터페이스로 리스너 순서 제어, 순서가 보장되지 않는 경우
05. 트랜잭션 바운드 이벤트 @TransactionalEventListener의 Phase(AFTER_COMMIT 등) 동작 원리, 트랜잭션 컨텍스트에서 이벤트 발행의 함정

🔹 Chapter 8: SpEL & Type Conversion

핵심 질문: @Value("#{...}")는 어떻게 표현식을 평가하는가?

Spring Expression Language와 타입 변환 시스템 (5개 문서)
문서 다루는 내용
01. SpEL 파싱 과정 ExpressionParserExpressionEvaluationContext 구조, Tokenizer / AST 파싱 단계
02. @Value("${...}") vs @Value("#{...}") Property Placeholder와 SpEL의 처리 시점 차이, PropertySourcesPlaceholderConfigurer vs ExpressionEvaluator
03. PropertyEditor vs Converter 변환 JavaBeans PropertyEditor(구)와 Spring Converter<S,T>(신) 설계 차이, 타입 변환 순서
04. ConversionService 등록과 우선순위 DefaultConversionService 내장 Converter 목록, 커스텀 Converter 등록 방법과 우선순위 결정
05. Custom Type Converter 작성 Converter<S,T> / GenericConverter / ConverterFactory 세 가지 구현 방식과 사용 시점

🗺️ 목적별 학습 경로

🟢 "@Autowired는 어떻게 동작하나?" — 스프링 입문자 / 면접 준비 (2주)

Week 1 — 컨테이너와 의존성 주입 원리

Ch1-01  BeanFactory vs ApplicationContext
Ch3-01  Bean 생성 전체 과정
Ch2-01  생성자 vs 필드 vs 세터 주입 바이트코드 비교
Ch2-02  순환 참조 해결 — 3단계 캐시

Week 2 — AOP · 설정 · 실무 혼동 포인트

Ch4-01  JDK Proxy vs CGLIB 바이트코드 비교
Ch4-06  @Transactional이 프록시인 이유
Ch4-07  private 메서드에 AOP가 안 되는 이유
Ch6-01  @Configuration vs @Component (Full / Lite Mode)
Ch7-05  트랜잭션 바운드 이벤트 (@TransactionalEventListener — 면접 단골)
Ch8-02  @Value("${...}") vs @Value("#{...}") (실무 혼동 포인트)
🔵 Spring 내부를 원리로 이해하고 싶은 개발자 (6주)
Week 1  Chapter 1 전체 — IoC 컨테이너 구조
Week 2  Chapter 2 전체 — DI 메커니즘
Week 3  Chapter 3 전체 — Bean 생명주기
Week 4  Chapter 4 전체 — AOP + @Transactional + @Cacheable 내부 구조
Week 5  Chapter 5 전체 + Chapter 6 전체 — 컴포넌트 스캔 + Java Config
Week 6  Chapter 7 전체 + Chapter 8 전체 — 이벤트 시스템 + SpEL + 타입 변환
🔴 Spring 소스코드 레벨 이해 목표 (3개월)
1개월차  Chapter 1~4 전체 순서대로 + 각 문서의 실험 코드 직접 실행
         → Spring Framework GitHub에서 연관 소스 직접 추적
         → 직접 BeanPostProcessor 구현해보기

2개월차  Chapter 5~6 전체
         → ImportBeanDefinitionRegistrar로 커스텀 @EnableXxx 어노테이션 만들기
         → @ComponentScan 필터 직접 작성해보기

3개월차  Chapter 7~8 전체
         → @TransactionalEventListener + @Async 조합 실무 패턴 실습
         → 커스텀 GenericConverter + ConditionalConverter 구현
         → 전체 Bean 생명주기 플로우 직접 그려보기

📖 각 문서 구성 방식

모든 문서는 동일한 구조로 작성됩니다.

섹션 설명
🎯 핵심 질문 이 문서를 읽고 나면 답할 수 있는 질문
🔍 왜 이게 존재하는가 문제 상황과 설계 배경
😱 흔한 오해 또는 잘못된 사용 Before — 많은 개발자가 틀리는 방식
올바른 이해와 사용 After — 원리를 알고 난 후의 올바른 접근
🔬 내부 동작 원리 Spring 소스코드 직접 추적 + 다이어그램
💻 실험으로 확인하기 직접 실행 가능한 코드 + 예상 결과
🤔 트레이드오프 이 설계의 장단점, 언제 다른 방법을 택할 것인가
📌 핵심 정리 한 화면 요약
🤔 생각해볼 문제 개념을 더 깊이 이해하기 위한 질문 + 해설

🙏 Reference


⭐️ 도움이 되셨다면 Star를 눌러주세요!

Made with ❤️ by Dev Book Lab


"스프링을 사용하는 것과, 스프링이 어떻게 작동하는지 아는 것은 다르다"

About

@component 하나가 Bean이 되기까지, IoC 컨테이너 내부를 끝까지 파헤칩니다

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors