Beans, injection, and where @Transactional belongs
The ApplicationContext is a dependency graph
Spring's ApplicationContext is not a magic container — it is an explicit directed graph of objects (beans). When the app starts, Spring scans for @Component, @Service, @Repository, @Configuration, and @Bean factory methods, builds the graph, and resolves constructor parameters by type.
Prefer constructor injection
@Service
class OrderService {
private final OrderRepository repo;
private final PaymentProcessor payments;
OrderService(OrderRepository repo, PaymentProcessor payments) {
this.repo = repo;
this.payments = payments;
}
@Transactional
public Order place(OrderRequest req) { /* ... */ }
}Constructor injection makes dependencies obvious in the signature, supports final fields, and enables trivially testable instantiation in unit tests without Spring.
Scopes: who owns the instance?
- Singleton (default): one bean for the whole application.
- Prototype: a new instance per injection — rarely needed; watch for subtle lifecycle bugs.
- Request / Session: tie lifetime to a web conversation.
@Transactional lives at the service boundary
Transactions should wrap a business operation, not an individual SQL statement. Put @Transactional on the service method that commits-or-rolls-back as a unit. Repositories should stay dumb (SQL in, rows out); controllers should not know about transactions at all.
Takeaways
- Beans form a graph; the container resolves it by type at startup.
- Constructor-inject, keep fields
final, and test without Spring where you can. - Transactions go on service methods — one business operation, one transaction.
Enjoying This Lesson?
Your support helps create more comprehensive courses and lessons like this one. Help me build better learning experiences for everyone.
Support Awashyak