π± Spring Internals (important parts only)
1. Startup: SpringApplication.run() β refresh()
- Create the
Environment(property sources: args, env vars,application.yml, profiles) - Create the
ApplicationContext refresh()(AbstractApplicationContext) β:invokeBeanFactoryPostProcessorsβConfigurationClassPostProcessorparses@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 singletonsonRefreshβ start the embedded web server (Tomcat/Netty)finishRefreshβ publishContextRefreshedEvent;ApplicationReadyEventcomes 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+@EnableAutoConfigurationAutoConfigurationImportSelector(a deferred import selector, so it runs after your config) loads the class names listed inMETA-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,@Cacheablesilently 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 theAuthentication- The filter order matters: β¦ β
BearerTokenAuthenticationFilterβJwtAuthenticationProviderβJwtDecoder(JWKS fetched and cached) βAuthorizationFilter ProviderManageriteratesAuthenticationProviders; 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.AUTOflushes before queries - Lazy associations = proxies/bytecode-enhanced fields β
LazyInitializationExceptionoutside a transaction; N+1 appears when you iterate over them
π¬ Prove it
- Breakpoint in
AbstractApplicationContext.refresh()anddoCreateBean(); write down the order for your appβs beans - Write a
BeanPostProcessorthat logs each beanβs creation time and whether itβs a proxy (AopUtils.isAopProxy) - Reproduce the
@Transactionalself-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