🌱 Spring Internals (important parts only)

1. Startup: SpringApplication.run() β†’ refresh()

  1. Create the Environment (property sources: args, env vars, application.yml, profiles)
  2. Create the ApplicationContext
  3. refresh() (AbstractApplicationContext) ⭐:
    • invokeBeanFactoryPostProcessors β†’ ConfigurationClassPostProcessor parses @Configuration, @ComponentScan, @Import, @Bean β†’ registers BeanDefinitions (recipes, not objects yet)
    • Auto-configuration is imported here (see Β§3)
    • registerBeanPostProcessors (e.g. AutowiredAnnotationBeanPostProcessor, the AOP auto-proxy creator)
    • finishBeanFactoryInitialization β†’ instantiate all non-lazy singletons
    • onRefresh β†’ start the embedded web server (Tomcat/Netty)
    • finishRefresh β†’ publish ContextRefreshedEvent; ApplicationReadyEvent comes later

2. Bean lifecycle (AbstractAutowireCapableBeanFactory.doCreateBean)

Instantiate (constructor resolution) β†’ populate (field/setter injection) β†’ Aware callbacks β†’ BeanPostProcessor.before β†’ @PostConstruct / afterPropertiesSet / init-method β†’ BeanPostProcessor.after ← AOP proxies are created here β†’ ready β†’ on shutdown, @PreDestroy

  • The singleton registry has 3 caches (singletons, early references, factories) to resolve circular setter/field references. Circular references are disallowed by default in modern Boot β†’ use constructor injection and redesign

3. Auto-configuration

  • @SpringBootApplication = @Configuration + @ComponentScan + @EnableAutoConfiguration
  • AutoConfigurationImportSelector (a deferred import selector, so it runs after your config) loads the class names listed in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  • Each auto-config is guarded by conditions: @ConditionalOnClass, @ConditionalOnMissingBean (your bean wins), @ConditionalOnProperty…
  • Debug with --debug (conditions report) or /actuator/conditions

4. AOP & proxies

  • A proxy wraps your bean: CGLIB subclass (Boot default) or JDK dynamic proxy (interfaces)
  • Calls go proxy β†’ interceptor chain (advisors) β†’ target
  • Self-invocation (this.method()) bypasses the proxy β†’ @Transactional, @Async, @Cacheable silently don’t apply. Final/private methods can’t be proxied
  • @Transactional β†’ TransactionInterceptor β†’ PlatformTransactionManager.getTransaction() β†’ binds the connection/EntityManager to a ThreadLocal (TransactionSynchronizationManager) β†’ commit/rollback. By default it rolls back only on unchecked exceptions

5. Web request flow (Spring MVC)

Tomcat NIO connector accepts β†’ worker thread (or a virtual thread) β†’ servlet filter chain (incl. DelegatingFilterProxy β†’ FilterChainProxy β†’ Security filters) β†’ DispatcherServlet.doDispatch() β†’ HandlerMapping (finds the controller method) β†’ HandlerInterceptor.preHandle β†’ HandlerAdapter β†’ argument resolvers + HttpMessageConverter (Jackson) β†’ your controller β†’ return value handlers β†’ message converter writes the response β†’ exceptions go to HandlerExceptionResolver / @ControllerAdvice

6. Spring Security internals

  • SecurityContextHolder (ThreadLocal by default) holds the Authentication
  • The filter order matters: … β†’ BearerTokenAuthenticationFilter β†’ JwtAuthenticationProvider β†’ JwtDecoder (JWKS fetched and cached) β†’ AuthorizationFilter
  • ProviderManager iterates AuthenticationProviders; method security = AOP around @PreAuthorize

7. Spring Data JPA & Hibernate

  • Repository interfaces β†’ runtime proxies; query methods are parsed from method names (PartTree)
  • Persistence context = first-level cache + identity map + dirty checking (snapshot comparison at flush)
  • Flush order via the action queue (inserts β†’ updates β†’ deletes); FlushMode.AUTO flushes before queries
  • Lazy associations = proxies/bytecode-enhanced fields β†’ LazyInitializationException outside a transaction; N+1 appears when you iterate over them

πŸ”¬ Prove it

  • Breakpoint in AbstractApplicationContext.refresh() and doCreateBean(); write down the order for your app’s beans
  • Write a BeanPostProcessor that logs each bean’s creation time and whether it’s a proxy (AopUtils.isAopProxy)
  • Reproduce the @Transactional self-invocation bug; fix it 3 ways (separate bean, self-injection, TransactionTemplate)
  • Override an auto-configured bean (e.g. ObjectMapper) and confirm in the conditions report
  • Log the Security filter chain at startup; send a request with a bad JWT and trace which filter rejects it
  • Enable Hibernate statistics; show dirty checking issuing an UPDATE without calling save()
  • Lab: build a mini DI container β†’ Assignments - Phase 2

Interview questions interview-q

Bean lifecycle Β· how auto-config works Β· why @Transactional fails on self-calls Β· JDK vs CGLIB proxies Β· the request flow through DispatcherServlet Β· how Spring Security validates a JWT Β· dirty checking Β· the N+1 root cause