When I started learning Spring, I noticed something about many of the tutorials I found.
They often showed how to use a feature, but not always what was happening behind the scenes or how the different pieces of the framework worked together.
I learn best by building things and documenting what I discover, so I started a project called spring-by-example.
The idea is simple: instead of building one large application, Iβm creating a collection of small, focused examples where each example explores a specific Spring concept with clear explanations, tests, and documentation.
Iβm happy to say that Iβve now completed Module 13 - Spring Internals. π
This module was particularly interesting because it went beyond using Spring features and focused on understanding what happens behind the scenes.
Instead of simply writing @Autowired, @Transactional, or @Component and letting Spring handle everything, I wanted to understand what Spring is actually doing with those annotations and how the different pieces of the container work together.
The module covers:
- How Beans Are Registered
- How Dependency Injection Works
- How Component Scanning Works
- How
@AutowiredWorks - How
@TransactionalWorks - How AOP Proxies Are Created
- How Bean Post Processors Work
- Understanding
DefaultListableBeanFactory - Understanding
ConfigurationClassPostProcessor
One of the first things I explored was how beans are registered.
I already knew that Spring manages beans, but looking at BeanDefinition and DefaultListableBeanFactory helped me understand an important distinction: registering a bean definition is not the same thing as creating the bean itself.
Spring can first work with metadata describing a bean and later use that metadata to create and manage the actual object.
That led naturally into dependency injection.
Instead of looking at dependency injection only from the perspective of annotations, I explored concepts such as DependencyDescriptor, MethodParameter, and resolveDependency().
This helped me see dependency injection as a process of identifying an injection point, resolving the required dependency, and obtaining the appropriate bean from the container.
Then I explored component scanning.
Using @Component is something I've done many times, but looking at ClassPathBeanDefinitionScanner made the process much clearer.
A class annotated with @Component doesn't magically become a Spring bean. Spring has to discover it during scanning, create a corresponding BeanDefinition, and register that definition with the container.
Then came @Autowired.
One of the most useful lessons here was understanding that @Autowired itself doesn't perform the injection.
It provides metadata that AutowiredAnnotationBeanPostProcessor detects and processes.
I also explored @Transactional.
Rather than hiding transaction management behind Spring's higher-level configuration, I worked directly with TransactionInterceptor, TransactionAttributeSource, AnnotationTransactionAttributeSource, and PlatformTransactionManager.
Another particularly interesting part was AOP proxy creation.
I used ProxyFactory and MethodInterceptor to see how a proxy can sit between the caller and the target object.
Then I looked at BeanPostProcessor.
This was another important piece of the puzzle because it showed how Spring can intercept and modify bean instances during their lifecycle.
I also explored how a post processor can return the original bean or even replace it with another object.
After working with all these individual mechanisms, I reached DefaultListableBeanFactory.
This was probably one of the most important pieces for putting everything together.
It helped me see why the bean factory is so central to Spring's internals.
It brings together responsibilities around bean definitions, bean lookup, dependency resolution, singleton management, and bean creation.
Finally, I explored ConfigurationClassPostProcessor.
This connected many of the concepts from earlier examples.
@Configuration, @Bean, @ComponentScan, and @Import are all configuration metadata that Spring needs to process.
ConfigurationClassPostProcessor participates in turning that configuration metadata into bean definitions that can ultimately be managed by the bean factory.
The bigger picture started to look something like this:
Configuration Metadata
β
βΌ
ConfigurationClassPostProcessor
β
βΌ
BeanDefinitions
β
βΌ
DefaultListableBeanFactory
β
βΌ
Dependency Resolution
β
βΌ
Bean Creation
β
βΌ
BeanPostProcessors
β
βββ @Autowired
βββ AOP-related processing
Of course, this is a simplified model of Spring's internals, but having this mental model makes the framework feel much less like magic.
And I think that's what made this module particularly valuable.
The goal wasn't just to learn more Spring classes or memorize internal APIs.
It was to understand the machinery underneath the features I've already been using.
Instead of thinking:
"Spring does this."
I can now start asking:
"Which part of Spring does this, and at what stage of the container lifecycle?"
That shift in perspective is probably the biggest thing I'm taking away from this module.
After completing modules covering Spring Core, dependency injection, bean lifecycle, events, AOP, transactions, Spring Boot, Spring MVC, testing, Advanced Spring, and now Spring Internals, the project continues to grow into something much bigger than I originally imagined.
The goal still isn't to simply collect Spring annotations and interfaces.
I'm trying to understand how Spring works, why these features exist, and how the different pieces of the framework fit together - one focused example at a time.
The project is open source, and I'm continuing to build it as I learn:
π GitLab: https://gitl.ab/gfDEyGH3
π GitHub: https://gith.ub/dsAQrAU5
Thereβs still a long way to go, but completing Module 13 - Spring Internals feels like another important milestone in this learning journey. π
And now comes the next stage.
Next up: Module 14 - OpenMRS Examples.
This time, instead of focusing only on isolated Spring concepts, I'll be looking at how these concepts are actually applied in a real-world project.
I'm especially looking forward to this module because it brings everything closer to the kind of code found in a real production system.
One focused example at a time. π
If you've worked with Spring internals before, what's one part of the Spring container that helped you understand the framework better?