Unravel โบ Java concepts โบ Spring and Boot
Spring beans and dependency injection
Spring creates your objects (beans) and hands each one the other objects it needs, so classes declare their dependencies and never build them with new.
At a glance
Instead of every chef shopping for their own ingredients, a kitchen manager (the Spring container) stocks each station with exactly what its recipe lists.
- 1Classes marked @Component, @Service, @Repository or @Controller, and methods marked @Bean inside a @Configuration class, are registered at startup as beansSpring creates one instance of each (a singleton by default) and keeps it in the application context.
- 2When a bean needs another, it declares it, ideally as a constructor parameterSpring finds a matching bean by type and passes it in. This is dependency injection.
- 3@Autowired on a field does the same by reflection but hides the dependency and makes testing harder; prefer a final field set in the constructor (Lombok's @RequiredArgsConstructor writes that constructor for you)
- 4@Service, @Repository and @Controller are @Component with a role name@Repository also translates persistence exceptions into Spring's exception hierarchy.
- Beans are singletons shared by all threads, so they must not hold per-request mutable state in fields
- Construction problems appear at startup, not at runtime: a missing bean gives NoSuchBeanDefinitionException, two candidates give NoUniqueBeanDefinitionException (resolve with @Primary or @Qualifier), and circular constructor dependencies fail outright
- At startup the container scans classpath packages (component scan), builds bean definitions, sorts them by dependency and instantiates them
@Servicepublic class OrderService { private final OrderRepository repository; // delivered by Spring public OrderService(OrderRepository repository) { this.repository = repository; } public Order find(long id) { return repository.findById(id).orElseThrow(); }}- line 1 uses Service, the core of Spring beans and dependency injection
- line 2 uses Service, the core of Spring beans and dependency injection
- line 3 delivered by Spring
- line 5 uses Service, the core of Spring beans and dependency injection
Read the full explanation (about 2 min)
Spring creates your objects (beans) and hands each one the other objects it needs, so classes declare their dependencies and never build them with new.
A mental model
Instead of every chef shopping for their own ingredients, a kitchen manager (the Spring container) stocks each station with exactly what its recipe lists. A class only states "I need an OrderRepository", and the manager delivers one.
How it works
Classes marked @Component, @Service, @Repository or @Controller, and methods marked @Bean inside a @Configuration class, are registered at startup as beans. Spring creates one instance of each (a singleton by default) and keeps it in the application context.
When a bean needs another, it declares it, ideally as a constructor parameter. Spring finds a matching bean by type and passes it in. This is dependency injection.
@Autowired on a field does the same by reflection but hides the dependency and makes testing harder; prefer a final field set in the constructor (Lombok's @RequiredArgsConstructor writes that constructor for you).
@Service, @Repository and @Controller are @Component with a role name. @Repository also translates persistence exceptions into Spring's exception hierarchy.
Going deeper
Beans are singletons shared by all threads, so they must not hold per-request mutable state in fields. A field like private User current; in a service is a data race waiting to happen.
Construction problems appear at startup, not at runtime: a missing bean gives NoSuchBeanDefinitionException, two candidates give NoUniqueBeanDefinitionException (resolve with @Primary or @Qualifier), and circular constructor dependencies fail outright.
Constructor injection lets you create the class in a plain unit test with new OrderService(fakeRepo), no Spring needed. That is the practical reason to prefer it.
Under the hood
At startup the container scans classpath packages (component scan), builds bean definitions, sorts them by dependency and instantiates them. Bean post-processors then wrap some beans in proxies, which is how @Transactional, @Async and @Cacheable get their behaviour.
Common mistakes
- Field injection that hides dependencies.
- Mutable per-request state inside a singleton bean.
- Calling
new MyService()yourself, which produces an object Spring knows nothing about (no injection, no proxies).
How Unravel recognises it
When you paste code, Unravel looks for these signs of spring beans and dependency injection. Spotting them yourself is a good habit when you read unfamiliar code:
- Annotations:
@Component@Service@Repository@Configuration@Bean@Autowired@Inject@SpringBootApplication@RequiredArgsConstructor
Part of a learning path
- How a Spring request really works ยท next: Spring proxies (how annotations work)
- Background work in Spring ยท next: Spring configuration properties
- Inside Spring Boot ยท next: Spring Boot auto-configuration and starters
- Structuring an enterprise service ยท next: Layered and hexagonal architecture
Related concepts
Builds on this
Frequently asked questions
What is Spring beans and dependency injection?
Spring creates your objects (beans) and hands each one the other objects it needs, so classes declare their dependencies and never build them with new.
What are the most common mistakes with Spring beans and dependency injection?
Field injection that hides dependencies. Mutable per-request state inside a singleton bean. Calling new MyService() yourself, which produces an object Spring knows nothing about (no injection, no proxies).
How can I see Spring beans and dependency injection in real code?
Paste your own Java into Unravel and it will point out where spring beans and dependency injection appears, explain how it works at the detail level you choose, and draw the flow of the code around it.