Building the Spring Boot Application
Beans and the Application Context
আপনি একটি free preview lesson দেখছেন।
আগের lesson-এ আমরা Dependency Injection শিখেছি।
আমাদের application flow:
Handler
↓
UseCase
↓
Repository
Third-party dependency থাকলে:
Handler
↓
UseCase
├── Repository
└── External Service
Example:
public CreateOrderHandler(
CreateOrderUseCase createOrderUseCase
) {
this.createOrderUseCase = createOrderUseCase;
}
এখানে CreateOrderHandler তার dependency নিজে তৈরি করছে না।
কিন্তু নতুন প্রশ্ন আসে:
CreateOrderUseCaseobject-টি কে তৈরি করবে?
আর CreateOrderUseCase যদি তিনটি repository-এর উপর depend করে:
public CreateOrderUseCase(
ProductRepository productRepository,
InventoryRepository inventoryRepository,
OrderRepository orderRepository
) {
}
তাহলে repository objects-গুলো কে তৈরি করবে?
Spring এই object construction এবং wiring manage করতে পারে।
Spring যে objects manage করে, সেগুলোকে বলা হয়:
Beans
আর যে container Beans create, configure এবং connect করে, সেটি হলো:
Application Context
What Is a Spring Bean?
Simple definition:
A Spring Bean is an object whose creation and lifecycle are managed by Spring.
Suppose:
public class CreateOrderUseCase {
}
এটি একটি normal Java class।
আমরা যদি করি:
CreateOrderUseCase useCase =
new CreateOrderUseCase(...);
তাহলে object-টি আমরা manually create করছি।
Spring এটি manage করছে না।
কিন্তু Spring যদি object-টি create এবং manage করে, সেই instance একটি Bean।
Conceptually:
Java Class
↓
Spring creates instance
↓
Spring Bean
Class and Bean Are Different
A class is a Java type definition.
A Bean is a Spring-managed object instance।
For example:
public class CancelOrderUseCase {
}
class।
Spring runtime-এ এর একটি managed instance তৈরি করতে পারে।
That instance is a Bean।
Not Every Object Should Be a Bean
আমাদের application-এ অনেক object থাকবে:
Order
OrderItem
Product
Inventory
CreateOrderCommand
OrderResponse
এগুলো automatically Spring Bean নয়।
For example:
Order order =
new Order(customerId, items);
এটি একটি business object।
Spring-এর কাজ every Order instance manage করা নয়।
Which Objects Usually Become Beans?
আমাদের architecture অনুযায়ী natural Bean candidates:
Handlers
UseCases
Repositories
External Service implementations
Security components
Configuration components
For example:
CreateOrderHandler
CreateOrderUseCase
PostgresOrderRepository
ProviderPaymentService
এসব long-lived application collaborators।
Domain Objects Usually Remain Plain Java Objects
Domain objects:
Order
OrderItem
Product
Inventory
specific business state represent করে।
Suppose:
Order #1001
Order #1002
Order #1003
প্রতিটি আলাদা business entity।
তাই:
@Component
public class Order {
}
আমাদের model-এর জন্য ঠিক হবে না।
Question:
Spring কোন Order create করবে?
Domain entities workflow-এর মধ্যে create বা repository থেকে reconstruct হবে।
Bean vs Domain Instance
Think of:
CreateOrderUseCase
as reusable application behaviour।
One UseCase instance may handle many operations।
But:
Order #5001
is one specific domain entity।
Conceptually:
CreateOrderUseCase Bean
↓
handles many requests
Order A
Order B
Order C
What Is the Application Context?
Spring-এর runtime container হলো:
ApplicationContext
Useful mental model:
Application Context contains and manages the application's Spring Beans and their dependency relationships.
Conceptually:
ApplicationContext
├── CreateOrderHandler
├── CreateOrderUseCase
├── ProductRepository
├── InventoryRepository
├── OrderRepository
└── ProviderPaymentService
Spring এই graph create করে application startup-এর সময়।
What Happens During Startup?
Our application starts from:
SpringApplication.run(
OrderManagementApplication.class,
args
);
Conceptually:
JVM starts
↓
main(...)
↓
SpringApplication.run(...)
↓
ApplicationContext created
↓
Bean definitions discovered
↓
Beans created
↓
Dependencies resolved
↓
Application ready
This is why dependency problems often appear during startup।
Spring Builds a Dependency Graph
Suppose:
CreateOrderHandler
↓
CreateOrderUseCase
├── ProductRepository
├── InventoryRepository
└── OrderRepository
Spring cannot create CreateOrderHandler until CreateOrderUseCase exists।
And CreateOrderUseCase cannot be created until required repositories exist।
Conceptually:
Repositories
↓
CreateOrderUseCase
↓
CreateOrderHandler
Spring resolves this graph।
Constructor Injection and the Application Context
Example:
public class CreateOrderHandler {
private final CreateOrderUseCase useCase;
public CreateOrderHandler(
CreateOrderUseCase useCase
) {
this.useCase = useCase;
}
}
And:
public class CreateOrderUseCase {
private final OrderRepository orderRepository;
public CreateOrderUseCase(
OrderRepository orderRepository
) {
this.orderRepository = orderRepository;
}
}
Spring can:
find OrderRepository
↓
create CreateOrderUseCase
↓
create CreateOrderHandler
This is Dependency Injection at runtime।
How Does Spring Know What to Manage?
Two important mechanisms:
Component Scanning
Explicit Bean Configuration
Component Scanning
Spring can scan packages for classes marked as managed components।
Common annotations include:
@Component
@Repository
@Controller
@RestController
Spring also has @Service, but in our project terminology Service is reserved for external/third-party capabilities rather than internal application workflows।
A UseCase as a Bean
For example:
@Component
public class CreateOrderUseCase {
private final OrderRepository orderRepository;
public CreateOrderUseCase(
OrderRepository orderRepository
) {
this.orderRepository = orderRepository;
}
}
@Component tells Spring:
Create and manage this application component.
The class name tells us its application responsibility:
CreateOrderUseCase
These are separate concerns।
A Handler as a Bean
Conceptually:
@Component
public class CreateOrderHandler {
private final CreateOrderUseCase useCase;
public CreateOrderHandler(
CreateOrderUseCase useCase
) {
this.useCase = useCase;
}
}
Later, when we build REST APIs, framework-specific HTTP annotations may define the exact Handler implementation।
But architecturally:
Handler → UseCase
remains the flow।
Repository as a Bean
Persistence implementation may look conceptually like:
@Repository
public class PostgresOrderRepository
implements OrderRepository {
}
This tells Spring:
this is a managed persistence component
while:
OrderRepository
represents the persistence boundary used by the UseCase।
Exact JPA implementation comes later।
External Service as a Bean
Suppose:
public interface PaymentService {
PaymentResult pay(
PaymentRequest request
);
}
Provider implementation:
@Component
public class ProviderPaymentService
implements PaymentService {
@Override
public PaymentResult pay(
PaymentRequest request
) {
return null;
}
}
Then:
public class PayOrderUseCase {
private final PaymentService paymentService;
public PayOrderUseCase(
PaymentService paymentService
) {
this.paymentService = paymentService;
}
}
Spring can inject the provider implementation through the PaymentService boundary।
Why We Don't Call Internal Classes Services
Our internal terminology remains:
Handler
UseCase
Repository
Service is reserved for external/third-party capabilities such as:
PaymentService
IdentityService
NotificationService
So instead of:
OrderService
ProductService
we use:
CreateOrderUseCase
CreateProductUseCase
CancelOrderUseCase
This makes responsibility clearer।
Component Scanning Starts From the Application Package
Our main class lives at:
io.liveklass.ordermanagement
@SpringBootApplication
public class OrderManagementApplication {
}
Spring Boot normally scans its package and subpackages।
So components under:
io.liveklass.ordermanagement.order
io.liveklass.ordermanagement.product
io.liveklass.ordermanagement.inventory
can be discovered automatically।
Why Package Placement Matters
Suppose an application component accidentally lives at:
com.example.payment.ProviderPaymentService
outside the default scan path।
Spring may not discover it।
Then PayOrderUseCase requiring:
PaymentService
may fail during startup।
Correct package structure prevents unnecessary custom scanning।
Prefer Correct Structure Over Broad Scanning
You could configure:
@ComponentScan(...)
to scan many packages।
But if the component belongs to the application, better place it under:
io.liveklass.ordermanagement
unless there is a real reason otherwise।
Use framework conventions where they align naturally with our architecture।
What Happens if a Bean Is Missing?
Suppose:
@Component
public class CreateOrderUseCase {
public CreateOrderUseCase(
OrderRepository orderRepository
) {
}
}
but Spring knows no OrderRepository Bean।
Then startup fails।
Conceptually:
CreateOrderUseCase
↓
OrderRepository
↓
missing
This is useful।
Fail Fast
A required dependency being missing should usually prevent application startup।
Better:
startup fails
than:
application starts
↓
first production request fails
Spring's container helps detect broken composition early।
How to Debug Missing Beans
When Spring cannot construct a component, ask:
Which Bean failed?
Which constructor dependency is missing?
Does Spring know an implementation?
Is the implementation inside the scan path?
Was it registered as a Bean?
Are there multiple candidates?
Think in terms of dependency graph rather than annotation magic।
Multiple Bean Candidates
Suppose:
public interface PaymentService {
}
and Spring finds:
ProviderAPaymentService
ProviderBPaymentService
Then:
public PayOrderUseCase(
PaymentService paymentService
) {
}
is ambiguous।
Spring sees:
Need:
1 PaymentService
Found:
2 implementations
It cannot safely guess which one the application wants।
Resolving Multiple Candidates
Spring provides mechanisms such as:
@Qualifier
@Primary
explicit configuration
But our current v1 has only one payment provider।
So we do not need this complexity now।
Avoid Creating Multiple Implementations Without a Requirement
Don't add:
DefaultPaymentService
DemoPaymentService
BackupPaymentService
just to demonstrate DI।
Production code should reflect actual system requirements।
Test doubles stay in tests।
Explicit Bean Configuration
Component scanning is not the only way to create a Bean।
Spring configuration can explicitly define one।
Example:
@Configuration
public class PaymentConfiguration {
@Bean
public PaymentService paymentService() {
return new ProviderPaymentService();
}
}
The object returned by:
paymentService()
becomes a Spring Bean।
Why Use @Bean?
Explicit Bean configuration is useful when:
we don't own the class
construction needs runtime configuration
third-party clients need setup
composition should be explicit
For example, a Payment Service may need:
base URL
API key
timeout
That construction may belong naturally in configuration।
Component Scanning vs Explicit Configuration
Component scanning
@Component
public class CreateOrderUseCase {
}
Spring discovers it।
Explicit configuration
@Bean
public CreateOrderUseCase createOrderUseCase(
OrderRepository orderRepository
) {
return new CreateOrderUseCase(
orderRepository
);
}
Both approaches create a Bean।
Which Should We Use?
For our own straightforward application components:
Handlers
UseCases
component scanning may be convenient।
For infrastructure and third-party integration construction:
external clients
configured adapters
explicit configuration may be clearer।
The goal is predictable composition, not loyalty to one technique।
@Configuration
A class:
@Configuration
public class PaymentConfiguration {
}
contains application composition/configuration logic।
It should not contain business workflows।
Bad:
@Bean
public Order createOrder() {
// business workflow
}
Bean configuration defines reusable components, not request-specific operations।
Beans Can Depend on Other Beans
Example:
@Bean
public PaymentService paymentService(
PaymentClient paymentClient
) {
return new ProviderPaymentService(
paymentClient
);
}
Spring resolves PaymentClient and passes it into the Bean factory method।
Same Dependency Injection principle।
Bean Scope
By default, most Spring Beans are singleton-scoped within one Application Context।
Meaning:
one managed instance
is typically reused across application requests।
For example:
CreateOrderUseCase Bean
may handle many requests।
Why UseCases Should Be Stateless
Bad:
@Component
public class CreateOrderUseCase {
private CustomerId currentCustomer;
public void execute(
CustomerId customerId
) {
this.currentCustomer = customerId;
}
}
If the Bean is shared, concurrent requests may overwrite the same field।
Keep Request Data Local
Better:
public Order execute(
CustomerId customerId,
CreateOrderCommand command
) {
return null;
}
Request-specific values remain:
method parameters
local variables
domain objects
Stable collaborators remain constructor fields।
Stable Dependency vs Request State
Constructor fields:
OrderRepository
PaymentService
Method parameters:
CustomerId
OrderId
CreateOrderCommand
Domain objects:
Order
OrderItem
Inventory
This distinction helps prevent shared mutable state bugs।
Singleton Bean Does Not Mean Global Singleton Pattern
Spring singleton means approximately:
one Bean instance per ApplicationContext
It does not mean:
one universal JVM object forever
And it has nothing to do with domain uniqueness।
Bean Identity vs Domain Identity
Spring Bean:
CreateOrderUseCase
Domain entity:
Order #5001
These are entirely different concepts।
Spring manages application components।
Domain IDs identify business records।
Do Not Store Business State in Beans
Bad production repository:
@Repository
public class OrderRepository {
private final Map<Long, Order> orders =
new HashMap<>();
}
if PostgreSQL is supposed to be the production source of truth।
A Bean being long-lived does not mean business state should live in its fields।
Test Fakes Are Different
A test-only:
FakeOrderRepository
may legitimately keep objects in memory।
That is controlled test infrastructure।
Production business state still belongs in PostgreSQL।
Plain Java Domain Objects Are a Good Thing
A strong Spring application should still contain many normal Java objects।
For example:
Order order =
new Order(customerId, items);
order.cancel();
No Spring needed।
Spring manages composition around the domain, not the domain itself।
A Healthy Runtime Separation
Conceptually:
Spring-managed components
Handler
↓
UseCase
↓
Repository / External Service
During execution:
UseCase
↓
creates/loads
Order
Inventory
Product
This keeps framework composition separate from business state।
Application Context Is Not a Service Locator
Because Application Context contains Beans, someone might do:
applicationContext.getBean(
CreateOrderUseCase.class
);
inside business code।
We avoid this।
Why?
Because dependencies become hidden।
Prefer Constructor Injection
Bad:
public class CreateOrderHandler {
private final ApplicationContext context;
}
Better:
public class CreateOrderHandler {
private final CreateOrderUseCase useCase;
public CreateOrderHandler(
CreateOrderUseCase useCase
) {
this.useCase = useCase;
}
}
Application Context should perform composition from outside the business code।
Don't Inject ApplicationContext Into Domain Code
Especially avoid:
public class Order {
private ApplicationContext context;
}
Domain logic should not know Spring exists।
That would unnecessarily couple business behaviour to the framework।
Bean Lifecycle
At a high level:
Bean definition discovered
↓
Bean constructed
↓
dependencies supplied
↓
Bean initialized
↓
Bean used
↓
application shutdown
Spring can provide lifecycle hooks, but we don't need them for normal business workflows।
Bean Lifecycle Is Not Order Lifecycle
Spring Bean lifecycle:
created
used
destroyed
Order lifecycle:
UNPAID
↓
PAID
or:
UNPAID
↓
CANCELLED
These are completely different।
Do not model domain lifecycle using Spring component lifecycle।
Avoid Business Side Effects During Bean Creation
Bad:
public ProviderPaymentService() {
chargeCustomer();
}
Constructors/configuration should create valid components।
They should not execute arbitrary business operations।
Startup should remain predictable।
Spring Data Repositories Later
When we add Spring Data JPA, we may define repository interfaces that Spring provides implementations for।
Conceptually:
public interface OrderJpaRepository
extends JpaRepository<OrderEntity, Long> {
}
Spring Data can create a runtime implementation and register it as a Bean।
We will cover that in the persistence module।
For now, remember:
Repository contract
↓
runtime implementation
↓
Spring Bean
contextLoads() Now Makes More Sense
Our bootstrap test:
@SpringBootTest
class OrderManagementApplicationTests {
@Test
void contextLoads() {
}
}
starts an Application Context।
If:
Bean is missing
dependency is ambiguous
configuration is invalid
the test may fail before the test method does anything।
What contextLoads() Does Not Prove
Passing context startup does not prove:
Order rules are correct
API behaviour is correct
SQL is correct
security is correct
It only gives confidence that application composition/startup works।
Use Plain Tests When Spring Is Irrelevant
If testing:
Paid Order cannot be cancelled
Spring is unnecessary।
Example:
Order order = ...;
order.markPaid();
assertThrows(
IllegalStateException.class,
order::cancel
);
This should remain a normal domain test।
Use Spring Tests When Spring Behaviour Matters
Examples:
Does Spring create the Handler?
Does it inject the UseCase?
Does repository configuration work?
Does security configuration work?
Then starting an Application Context makes sense।
Auto-Configuration
Spring Boot also provides auto-configuration।
At a high level, it looks at things such as:
dependencies on the classpath
application configuration
existing Beans
and automatically configures common infrastructure where appropriate।
Later, when we add:
Spring Web
Spring Data JPA
Spring Security
we will see auto-configuration in practice।
Auto-Configuration Is Not Magic
When Spring creates something automatically, useful questions are:
Which dependency enabled this?
Which configuration influenced it?
Which Bean was created?
Can our own configuration override it?
Understanding these questions makes production debugging much easier।
Spring Is the Composition Mechanism
Our architecture decides:
Handler
UseCase
Repository
External Service
Domain
Spring decides:
how reusable application components
are constructed and connected
Don't reverse this relationship।
Spring should support the architecture, not define it।
Example Object Graph
Eventually:
ApplicationContext
CreateOrderHandler
↓
CreateOrderUseCase
├── ProductRepository
├── InventoryRepository
└── OrderRepository
PayOrderHandler
↓
PayOrderUseCase
├── OrderRepository
└── PaymentService
During execution those UseCases work with:
Product
Inventory
Order
OrderItem
which are business objects rather than application-wide Beans।
When Should a Class Become a Bean?
Ask:
Is this a reusable application collaborator?
Does it have dependencies Spring should compose?
Does framework-managed lifecycle/configuration help?
Or is this simply business state/value?
Examples:
CreateOrderUseCase:
Yes → likely Bean
OrderRepository implementation:
Yes → likely Bean
ProviderPaymentService:
Yes → likely Bean
Order:
No → normal domain object
OrderItem:
No → normal domain object
Common Mistake 1 — Everything Becomes a Bean
Avoid:
@Component
class Order {
}
just because Spring exists।
Common Mistake 2 — Nothing Uses the Container
Avoid manually constructing infrastructure everywhere:
new CreateOrderUseCase(
new PostgresProductRepository(),
new PostgresInventoryRepository(),
new PostgresOrderRepository()
);
inside Handlers।
Centralized composition is one of Spring's main benefits।
Common Mistake 3 — ApplicationContext Lookup
Avoid:
context.getBean(...)
inside UseCases।
Constructor dependencies should remain explicit।
Common Mistake 4 — Request State in Beans
Avoid:
private OrderId currentOrderId;
inside shared UseCase Beans।
Keep operation-specific state local।
Common Mistake 5 — Bean for Every Rule
Avoid creating Spring components such as:
OrderTotalCalculator
OrderCancellationValidator
when behaviour naturally belongs to domain objects।
Common Mistake 6 — Broad Component Scanning
Don't fix poor package organization by scanning the entire company namespace।
Place application components deliberately।
Common Mistake 7 — Multiple Beans Without Intent
If two implementations satisfy one dependency, there must be a deliberate selection strategy।
Spring should not be expected to infer business intent।
Common Mistake 8 — Context Test as Business Test
contextLoads() is not evidence that business features are correct।
Testing must match the behaviour being protected।
How This Connects to Dependency Injection
Dependency Injection lesson:
A component receives its collaborators.
This lesson:
Spring's Application Context
creates and supplies those collaborators.
Together:
Bean definitions
↓
ApplicationContext
↓
Constructor Injection
↓
Handler → UseCase → Repository
Engineering Principle
The core principle:
A Spring Bean is a reusable application object managed by Spring, while the Application Context is the container responsible for creating and connecting those managed objects.
Another:
Spring should manage application collaborators, not every business object in the domain.
And:
Use the container to make dependencies explicit and predictable, not to hide them behind dynamic lookups.
Summary
In this lesson, we learned that:
- A Java class and a Spring Bean are different concepts.
- A Bean is an object managed by Spring.
- The Application Context is Spring's runtime container.
- During startup Spring discovers Bean definitions, creates Beans, and resolves their dependencies.
Handler → UseCase → Repositorymaps naturally to a Spring dependency graph.- External Services can also participate in that graph.
- Handlers, UseCases, Repositories, external Service implementations, configuration, and security components are natural Bean candidates.
- Domain objects such as
Order,OrderItem,Product, andInventoryusually remain plain Java objects. - Spring can discover Beans through component scanning.
- Spring can also create Beans explicitly using
@Configurationand@Bean. - The application's root package affects component scanning.
- Correct package placement is preferable to unnecessarily broad scanning.
- Missing required Beans should normally cause startup to fail.
- Multiple Bean candidates require an explicit selection decision.
- Most application Beans are singleton-scoped by default within the Application Context.
- Therefore UseCases and similar components should avoid mutable request-specific state.
- Bean lifecycle and domain lifecycle are unrelated concepts.
- Business state should not live in singleton Bean fields.
ApplicationContextshould not be used as a service locator inside business code.- Plain Java domain objects remain important even inside a Spring application.
contextLoads()verifies application composition, not complete business correctness.- Spring Boot auto-configuration will become more visible as we add Web, JPA, and Security dependencies.
- Spring is our composition mechanism; application architecture still determines the correct responsibilities and dependencies.
Next lesson:
Configuration and Application Properties
There we will learn how runtime values such as database settings, external Service URLs, credentials, and timeouts enter the application without being hardcoded inside Handlers or UseCases.