Spring Boot - Part 1 (Annotations, Dependency Injection, Layered Architecture)
Spring Boot is a framework built on top of the Spring Framework that makes it easy to build production ready Java applications. Before Spring Boot, setting up a Spring application required a lot of manual configuration through XML files and boilerplate code. Spring Boot eliminates all of that by providing smart defaults and auto configuration — you just add annotations and everything works automatically.
Spring is the original framework — very powerful but required a lot of manual configuration. You had to write XML config files, manually set up servers, and configure everything yourself.
Spring Boot is built on top of Spring and hides all that complexity. It comes with sensible defaults, an embedded server, and auto configuration so you can focus on writing business logic instead of configuration.
Think of it like :
- Spring — raw ingredients, you cook everything from scratch
- Spring Boot — a meal kit, everything is pre-prepared, you just put it together
What Spring Boot gives you
Auto Configuration — Spring Boot looks at what dependencies you have added and automatically configures everything for you. Add a MySQL dependency and Spring Boot sets up the database connection automatically. Add Spring Security and it automatically secures all your endpoints.
Embedded Server — Spring Boot comes with an embedded Tomcat server built in. You do not need to install, configure, or deploy to a separate server. Just run the app and it is immediately live on port 8080.
Dependency Management — Spring Boot manages all library versions for you through Maven or Gradle. You just say "I need Spring Web" and Spring Boot downloads it and ensures all versions are compatible with each other.
Production Ready Features — Spring Boot comes with built in health checks, metrics, and monitoring tools out of the box through Spring Actuator.
Spring Ecosystem — Spring Boot gives you access to the entire Spring ecosystem — Spring Security for authentication, Spring Data JPA for databases, Spring Mail for emails, Spring Cache for caching and much more. All of these are first class citizens and integrate seamlessly.
Spring Boot Installation
Before starting make sure you have the following installed :
Setting up a Spring Boot project from scratch manually is painful. You would have to :
- - Manually create the folder structure
- - Write the entire `pom.xml` from scratch
- - Add all the right dependencies with correct versions
- - Create the main application class
- - Configure everything yourself
Spring Initializr at https://start.spring.io is an official tool by the Spring team that solves this. You just fill in your project details, select the dependencies you need, click Generate, and it gives you a perfectly structured zip file with everything set up correctly and ready to open in your IDE. Think of it exactly like create-react-app for React. Instead of manually setting up Webpack, Babel, folder structure and everything else, one tool generates the entire project for you in seconds.
What you fill in on Spring Initializer
- Project — choose Maven or Gradle as your build tool. Maven is recommended as most tutorials use it.
- Language — choose Java, Kotlin, or Groovy. Always choose Java.
- Spring Boot Version — choose the latest stable 3.3.x version. Avoid 4.x as it is too new and most resources online are written for 3.x.
- Group — your base package name, usually your name or company in reverse domain format like
com.deepesh. This becomes the root package for all your code. - Artifact — your project name like
myapp. This becomes the name of your generated zip file and jar file. - Packaging — choose Jar. This packages your app into a single runnable file.
- Java Version — choose 17 or higher.
- Dependencies — the libraries you want Spring Boot to include. You search and add them on the right side of the page. Common ones are Spring Web, Spring Data JPA, MySQL Driver etc.
Once your project is generated from Spring Initializr and opened in VS Code, you use Maven commands from the terminal to run, build, and manage your Spring Boot application.
mvn spring-boot:run— the most common command you will use during development. It compiles your code and starts the Spring Boot application. Your app will be live athttp://localhost:8080. Use this every time you want to run your app locally.mvn clean— deletes all compiled files and build output from thetarget/folder. Use this when you want a fresh start or when your build is behaving unexpectedly.mvn clean package— cleans first, then compiles your code, runs all tests, and packages everything into a single runnable.jarfile inside thetarget/folder. Use this when you want to build your app for deployment.mvn clean package -DskipTests— same as above but skips running tests. Use this when you want to build quickly without waiting for tests to finish.java -jar target/myapp-0.0.1-SNAPSHOT.jar— runs the packaged.jarfile directly. This is how your app runs in production on a server. You first build withmvn clean packageand then run the generated.jarfile with this command.mvn clean install— cleans, compiles, tests, packages, and installs the.jarinto your local Maven cache (~/.m2folder). Use this when other local projects depend on this project.
NOTE : During development you will almost always just use mvn spring-boot:run. The other commands are mainly used when building for deployment or troubleshooting build issues.
How Spring Boot works internally
When you run a Spring Boot application this is what happens behind the scenes :
The Spring Container also called Application Context is the heart of Spring Boot. It is a registry that holds all the objects Spring creates and manages. These objects are called Beans. Spring creates them once, stores them in the container, and reuses them wherever needed.
Every Spring Boot application starts from a single main class annotated with @SpringBootApplication :
Project Structure
Spring Boot does not enforce a folder structure but the entire community follows this convention called Layered Architecture. Each layer has one responsibility and only talks to the layer directly below it. Controller never touches the database. Repository never has business logic. This separation is called Separation of Concerns and makes your code clean, maintainable, and testable.
Build Tools
Spring Boot uses Maven or Gradle to manage dependencies and build the project. Both tools download libraries from Maven Central — the central online repository for Java libraries. Think of it like npm registry but for Java.
Maven uses a pom.xml config file with XML syntax. Most widely used in enterprise Java and Spring Boot tutorials. Gradle uses a build.gradle config file with Groovy/Kotlin syntax. Cleaner and faster than Maven.
Routes — how URLs are defined
Unlike Express.js where routes are defined in a single file, Spring Boot routes are defined directly on methods using annotations. Spring Boot scans all classes at startup and automatically registers every route it finds.
application.properties
This is the main configuration file for your Spring Boot app. It lives in src/main/resources/ and is where you configure everything — server port, database connection, JWT secrets, email settings etc.
Quick Start — minimal Spring Boot REST API
This is all you need to get a REST API running :
NOTE : This is the complete pattern you will follow for every feature you build in Spring Boot. Controller → Service → Repository. Every time, without exception.
-----------------------------------------------------------------------------------------------------------------------------
Spring Beans
A Bean is simply a Java object that is created and managed by Spring instead of you. That is it — nothing fancy, nothing special. The same Java object you have always worked with, except Spring creates it, stores it, and manages its entire lifecycle instead of you doing it manually.
In Spring Boot you never create objects manually. You just mark a class with an annotation and Spring creates the object for you at startup, stores it, and gives it to you whenever you need it :
Beans are Singletons
By default Spring creates only one instance of each Bean and reuses it everywhere in your app. No matter how many classes use UserService, there is always only one UserService object in the entire application.
Spring Boot Lifecycle — Beans, Container & Everything
Step 1 — You run the app
When you run mvn spring-boot:run the main() method executes and SpringApplication.run() kicks everything off. This one line starts the entire Spring Boot machinery — creating the container, scanning classes, wiring dependencies, and starting the server. Everything that happens next is triggered by this single line.
Step 2 — Application Context is Created
The very first thing Spring does is create the Application Context. This is the heart of Spring Boot — a container that will hold and manage all your Beans throughout the entire lifetime of your application. Think of it like an empty warehouse that is about to be stocked with objects. Every Bean Spring creates will be stored here and retrieved from here whenever needed.
Step 3 — Component Scanning
Spring now scans every class inside your root package and all subpackages looking for annotations like @RestController, @Service, @Repository, and @Component. This is why your folder structure matters — all your classes must be inside the same package as your main class otherwise Spring will not find them. This scanning process is what makes Spring Boot feel magical — you never manually register anything, Spring finds everything on its own.
Step 4 — Bean Creation
For every class Spring finds with those annotations, it creates an object and stores it in the Application Context. These objects are your Beans. Spring created them, not you. You never called new UserService() or new UserRepository() — Spring did that automatically during this step. The Application Context now holds one instance of every annotated class in your project.
Step 5 — Dependency Injection
Spring now looks at all the Beans it just created and checks if any of them have @Autowired fields. If a Bean needs another Bean to work, Spring finds the right Bean from the Application Context and injects it automatically. After this step every Bean has everything it needs — a Controller has its Service, a Service has its Repository. Everything is wired together without you writing a single line of wiring code.
Step 6 — Auto Configuration
Spring Boot now looks at what dependencies you added in pom.xml and automatically configures them for you. If you added a MySQL Driver and Spring Data JPA, Spring automatically sets up the database connection. If you added Spring Security, Spring automatically secures all your endpoints. If you added Spring Web, Spring sets up an embedded Tomcat server. You never write any of this configuration manually — Spring does it all based on what dependencies are present in your project.
Step 7 — Embedded Tomcat Starts
Spring Boot starts the embedded Tomcat server on port 8080. It registers all the routes it found from @GetMapping, @PostMapping, @PutMapping, @DeleteMapping etc. and starts listening for incoming HTTP requests.
Step 8 — App is Ready
Your app is now fully running and ready to handle requests. Every incoming HTTP request flows through the wired Beans — from Controller to Service to Repository and back — with Spring managing all the objects behind the scenes. You just write the business logic inside each layer and Spring handles the rest.
NOTE : The Application Context lives for the entire lifetime of your application. It is created when the app starts and destroyed when the app shuts down. All Beans inside it follow the same lifecycle.
NOTE : This entire process happens in just a few seconds when you run your app. Spring Boot is doing an enormous amount of work behind the scenes so that you do not have to.
-----------------------------------------------------------------------------------------------------------------------------
Spring Annotations
Annotations are the backbone of Spring Boot. Instead of writing hundreds of lines of XML configuration, you just put an annotation on a class or method and Spring handles everything automatically. Spring Boot annotations are grouped into categories based on what they do :
- Application Annotations — bootstrap and start the application.
- Layer Annotations — tell Spring which layer a class belongs to and to manage it as a Bean.
- REST API Annotations — define URL routes and map HTTP methods to Java methods.
- Request Handling Annotations — extract data from incoming HTTP requests like URL params, body, and headers.
- Dependency Injection Annotations — wire Beans together automatically without manually creating objects.
- Configuration Annotations — configure the application and define Beans manually.
- Database Annotations — map Java classes and fields to database tables and columns.
- Validation Annotations — validate incoming request data automatically before it reaches your business logic.
- Utility Annotations — add extra functionality like transactions, scheduling, async execution, and logging.
1] Application Annotation
These are used on the main class to bootstrap and start the application. You will only ever use one annotation here. @SpringBootApplication — the most important annotation in any Spring Boot project. Put it on your main class. It does 3 things in one :
- Marks this as a Spring Boot application
- Enables auto configuration
- Tells Spring to scan all classes in this package and subpackages for annotations like
@RestController,@Service,@Repositoryetc.
NOTE : @SpringBootApplication is actually 3 annotations combined into one — @SpringBootConfiguration, @EnableAutoConfiguration, and @ComponentScan. You will never need to use these 3 separately — @SpringBootApplication covers all of them.
NOTE : The package where your main class lives becomes the root package. Spring only scans classes inside this package and its subpackages. This is why all your folders — controller, service, repository etc. — must be inside the same package as your main class.
2] Layer Annotations
These tell Spring which layer a class belongs to and to manage it as a Bean. When Spring scans your project and finds these annotations it automatically creates an object of that class and stores it in the Application Context.
All 4 of these do the same thing technically — they all make a class a Spring Bean. The difference is that they communicate the role of that class to Spring and to other developers reading your code.
@Component— the generic parent annotation. All the other 3 below are specializations of@Component. Use this when your class does not fit into any specific layer — for example a utility class or a helper class.
@RestController— marks a class as a REST API controller. This is the entry point of every HTTP request. It handles incoming requests and automatically converts your Java return values into JSON responses. It is actually two annotations combined —@Controllerand@ResponseBody— merged into one for convenience.
@Service— marks a class as a service. This is where all your business logic lives — calculations, decisions, validations, calling external APIs, sending emails etc. The Controller calls the Service.
@Repository— marks a class as a repository. This is where all your database queries live. The Service calls the Repository. Spring also adds an extra feature to this annotation — it automatically catches database specific exceptions and converts them into Spring exceptions so your error handling stays consistent regardless of which database you use.
Why not just use @Component everywhere?
Technically you could mark everything with @Component and Spring Boot would still work perfectly. But using the specific annotations has 2 benefits :
- First it communicates intent — when another developer sees
@Servicethey immediately know this class contains business logic without reading a single line of code inside it. - Second it enables layer specific features —
@Repositoryautomatically translates database exceptions,@RestControllerautomatically converts return values to JSON. These features only work with the specific annotations.
NOTE : These 4 annotations are what make the entire Spring Boot ecosystem work. Without them Spring would not know which classes to manage and nothing would be injected or wired together. Every class in your project that needs to be managed by Spring must have one of these annotations.
3] REST API Annotations
These annotations go on methods inside a @RestController class to define your API routes. They tell Spring which HTTP method and which URL path a method should handle.
@RequestMapping— sets a base URL for all endpoints in a controller class. Instead of repeating/userson every single method you write it once on the class and all methods inside automatically inherit it.
@GetMapping— maps a method to handle HTTP GET requests. Used for fetching data. When a client wants to read something they send a GET request.
@PostMapping— maps a method to handle HTTP POST requests. Used for creating new data. When a client wants to create something they send a POST request with the data in the request body.
@PutMapping— maps a method to handle HTTP PUT requests. Used for updating existing data completely. The client sends the entire updated object.
@PatchMapping— maps a method to handle HTTP PATCH requests. Used for partial updates. Unlike PUT which replaces the entire object, PATCH only updates the specific fields that were sent.
@DeleteMapping— maps a method to handle HTTP DELETE requests. Used for deleting data.
NOTE : The combination of HTTP method and URL path must be unique in your entire application. You cannot have two @GetMapping("/users") methods — Spring would not know which one to call.
Before @GetMapping, @PostMapping etc. existed, developers used @RequestMapping for everything and had to specify the HTTP method manually :
Both ways do exactly the same thing. The new way is just cleaner and easier to read. You will always use the shortcut annotations in modern Spring Boot. You will only see @RequestMapping with a method specified in very old codebases.
4] Request Handling Annotations
These annotations extract data from incoming HTTP requests. When a client sends a request it can contain data in different places — in the URL path, in the URL query parameters, in the request body as JSON, or in the request headers. These annotations tell Spring where to look and how to extract that data and pass it to your method.
@RequestBody— extracts the JSON from the request body and automatically converts it into a Java object. Used in POST and PUT requests where the client sends data. Spring uses a library called Jackson behind the scenes to do this JSON to Java conversion automatically.
@PathVariable— extracts a dynamic value from the URL path. For example in/users/1the1is a path variable. You define it in the URL using curly braces like/{id}and Spring automatically extracts it and passes it to your method.
@RequestParam— extracts a query parameter from the URL. Query parameters come after the?in a URL. For example in/users?page=1&size=10thepageandsizeare query parameters. Commonly used for filtering, searching, and pagination.
@RequestHeader— extracts a value from the HTTP request header. Headers contain metadata about the request. The most common use case is extracting the JWT token from theAuthorizationheader to authenticate the user.
NOTE : Jackson library is what converts JSON to Java objects for @RequestBody and Java objects back to JSON for @RestController responses. It does this automatically — you never call Jackson directly. This is part of Spring Boot's auto configuration.
5] Dependency Injection Annotations
These annotations wire your Beans together automatically. Instead of manually creating objects and passing them around, you declare what you need and Spring finds the right Bean from the Application Context and injects it for you.
@Autowired— the most commonly used DI annotation. Put it on a field, constructor, or setter and Spring will automatically find the matching Bean from the Application Context and inject it. This is how every layer gets access to the layer below it — Controller gets its Service, Service gets its Repository.
@Qualifier— used when you have multiple Beans of the same type and Spring does not know which one to inject. You use@Qualifierto specify exactly which Bean you want by name.
@Primary— when you have multiple Beans of the same type, marking one with@Primarytells Spring to always inject this one by default unless@Qualifierspecifies otherwise.
Below are 3 different ways to use @Autowired in Spring Boot Application :
- 1] Field Injection — the simplest way. You put
@Autowireddirectly on the field and Spring injects the Bean automatically. This is the most commonly seen approach in tutorials because it is the least amount of code. However it is not recommended for real projects because it hides dependencies and makes testing harder.
- 2] Constructor Injection — the recommended approach in real projects. Instead of putting
@Autowiredon a field, you declare the dependency as a constructor parameter. Spring sees the constructor and automatically injects the right Bean when creating the object. If there is only one constructor you do not even need@Autowired— Spring handles it automatically. This approach makes dependencies explicit and makes testing much easier because you can pass any object through the constructor without needing Spring at all.
- 3] Setter Injection — rarely used in practice. You put
@Autowiredon a setter method and Spring calls that method to inject the Bean. This was more common in older Spring applications but has been largely replaced by constructor injection in modern Spring Boot projects.
NOTE : Constructor injection is recommended over field injection in real projects because it makes dependencies explicit — you can clearly see what a class needs just by looking at its constructor. It also makes testing easier because you can pass mock objects directly through the constructor without needing Spring at all.
NOTE : In modern Spring Boot if a class has only one constructor, Spring automatically injects the dependencies without needing @Autowired on the constructor. This is why you will see many Spring Boot projects with no @Autowired at all — they use constructor injection and Spring handles it automatically.
6] Utility Annotations
These annotations add extra functionality to your classes and methods. They are not tied to any specific layer — you can use them anywhere in your application.
@Transactional— wraps a method in a database transaction. A transaction means all the database operations inside that method either all succeed together or all fail together and get rolled back. For example if you are transferring money between two accounts — deducting from one and adding to another — if the second operation fails you do not want the first one to have happened.@Transactionalhandles this automatically.
@Scheduled— runs a method automatically on a schedule without any external trigger. You define when it should run using a cron expression or a fixed interval. Used for things like sending daily reports, cleaning up old data, syncing with external APIs etc.
@Async— runs a method in a background thread so it does not block the main thread. For example sending a welcome email after registration — you do not want the user to wait for the email to be sent before getting a response. With@Asyncthe email is sent in the background and the user gets the response immediately.
@Slf4j— from the Lombok library. Automatically adds a logger to your class so you can log messages without writing any boilerplate logging code. Extremely commonly used in real projects.
-----------------------------------------------------------------------------------------------------------------------------
Layers & Components
Every Spring Boot application follows a pattern called Layered Architecture. This means your application is divided into separate layers where each layer has one specific responsibility and only communicates with the layer directly below it. This is not a Spring Boot rule — it is a software engineering principle called Separation of Concerns. Spring Boot just makes it very easy to follow through its annotations.
Rules of Layered Architecture
These are not optional suggestions — they are rules every Spring Boot developer follows :
- Controller only calls Service — never Repository directly.
- Service only calls Repository — never another Service's Repository directly.
- Repository only talks to Database — never contains business logic.
- Model travels between all layers — it is the common language everyone speaks.
Why Spring Boot does not create folders automatically ?
Spring Boot generates only the bare minimum when you create a project through Spring Initializr — the main class, application.properties, and pom.xml. That is it.
It does not create controller, service, repository, or model folders because Spring Boot has no idea what your application does. It does not know if you are building a hospital management system, a fitness tracker, or an e-commerce platform. Each application has different features, different entities, and different layers of complexity. Creating folders upfront would be making assumptions about your business logic which Spring Boot deliberately avoids.
What you get from Spring Initializr :
What you create manually
You create folders and files based on the features your application needs. For a User management feature you would create :
NOTE : Spring Boot does not enforce this folder structure — you could technically put everything in one file and it would still work. But every professional Spring Boot developer follows this convention. When you open any Spring Boot project in any company in the world it will follow this exact structure. Consistency is what makes codebases maintainable across teams.
NOTE : Notice that every feature follows the same pattern — Controller, Service, Repository, Model. This predictability is the biggest advantage of layered architecture. A new developer joining your team can immediately understand the codebase because everything follows the same structure.
Controller Layer — @RestController
The Controller is the entry point of your application. It is the only layer that talks to the outside world. Its only job is to receive HTTP requests, extract the data from them, pass that data to the Service, and send the response back to the client. The Controller should contain zero business logic — it should be as thin as possible. Receive, delegate, respond. Nothing else.
@RestController — marks a class as the Controller layer. Handles incoming HTTP requests and automatically converts Java return values to JSON responses. This is the only layer that talks to the outside world.
Service Layer — @Service
The Service is the brain of your application. All your business logic lives here — calculations, decisions, validations, calling external APIs, sending emails, applying business rules etc. The Controller calls the Service and the Service calls the Repository. The Service should have no knowledge of HTTP — it should not know about requests, responses, or status codes. It just receives data, processes it, and returns a result.
@Service — marks a class as the Service layer. Spring Boot does not add any special behavior here beyond making it a Bean, but it communicates clearly that this class contains business logic.
Repository Layer — @Repository
The Repository is the data access layer. Its only job is to talk to the database — fetching, saving, updating, and deleting data. The Service calls the Repository whenever it needs to interact with the database. In Spring Boot the Repository is almost always an interface that extends JpaRepository. Spring automatically provides all basic database operations like findAll(), findById(), save(), deleteById() without you writing a single line of SQL.
@Repository — marks a class as the Repository layer. Spring adds an extra feature here — it automatically catches database specific exceptions and converts them into consistent Spring exceptions so your error handling works the same regardless of which database you use.
Model — The Data Class
The Model is not a layer — it is a plain Java class that represents your data and travels between all layers. It maps directly to a database table using JPA annotations.
NOTE : @Component — the generic parent of all the above annotations. Use this for classes that do not belong to any specific layer — utility classes, helper classes, email senders etc.
-----------------------------------------------------------------------------------------------------------------------------
Dependency Injection
In any application classes need other classes to work. For example a UserController needs a UserService to handle business logic. A UserService needs a UserRepository to talk to the database. These are called dependencies — one class depends on another to do its job. The question is — how does a class GET its dependencies?
In normal Java you create them yourself using the new keyword. This works but creates a big problem — your classes become tightly coupled. Every class is responsible for creating and managing its own dependencies. As your app grows with hundreds of classes this becomes impossible to manage, hard to test, and difficult to change.
Dependency Injection solves this by flipping the responsibility. Instead of a class creating its own dependencies, something external creates them and injects them into the class. You just declare what you need and the injector handles the rest.
In Spring Boot that injector is the Spring Container (Application Context). Spring creates all your objects, manages them as Beans, and injects them wherever needed automatically. You never use the new keyword manually for Spring managed classes. You just declare what you need with @Autowired and Spring does the rest.
@Autowired
You already know that Spring scans your project at startup, finds classes marked with @Service, @Repository, @RestController etc., creates objects for them, and stores them in the Application Context as Beans. Dependency Injection is the next step — once all Beans are created, Spring looks for @Autowired annotations anywhere in your code and injects the right Bean automatically.
@Autowired is the annotation that tells Spring — "I need this dependency, find the right Bean from the Application Context and inject it here."
NOTE : Without @Autowired Spring does not know you need that dependency and the field stays null. Calling any method on a null field crashes your app with a NullPointerException.
@Autowired is optional in one case
If a class has only one constructor Spring automatically injects dependencies through it without needing @Autowired. This is the basis of constructor injection which we will see next.
3 Ways to Inject Dependencies
1] Field Injection — the simplest way. Put @Autowired directly on the field and Spring injects the Bean automatically. Most commonly seen in tutorials because it requires the least amount of code. However it is not recommended for real projects because the dependencies are hidden inside the class — you cannot tell what a class needs without reading all its fields. It also makes testing harder because you cannot inject mock objects without Spring.
2] Constructor Injection — the recommended best practice in real projects. You declare dependencies as constructor parameters and Spring automatically injects the right Beans when creating the object. Dependencies are explicit — anyone can see what a class needs just by looking at its constructor. If there is only one constructor you do not even need @Autowired. Also notice the field is final which means it cannot be changed after injection — making the class safer and more predictable.
3] Setter Injection — put @Autowired on a setter method and Spring calls that method to inject the Bean. Was more common in older Spring applications but has been largely replaced by constructor injection in modern Spring Boot. Rarely seen in real projects today.
NOTE : In most real world Spring Boot projects you will see either field injection or constructor injection. Field injection for its simplicity during development, constructor injection for production quality code. Pick one style and stay consistent across your entire project.
Where @Autowired is Used
@Autowired is used in every layer of your application — not just controllers. The reason is simple — every layer depends on the layer below it to do its job. A Controller cannot work without a Service. A Service cannot work without a Repository. This chain of dependencies is what @Autowired wires together. Spring resolves this entire chain automatically at startup. Every @Autowired field gets the right Bean injected without you writing a single line of wiring code.
The Repository layer is the only layer that rarely uses @Autowired because it has no dependencies of its own — it is the last stop before the database. Every other layer will have at least one @Autowired field. In a real production Spring Boot app you will write @Autowired (or constructor injection) dozens of times across all layers. It is one of the most frequently used annotations in any Spring Boot project.
@Primary and @Qualifier
Sometimes you have multiple Beans of the same type and Spring does not know which one to inject and throws error. For example two classes implementing the same interface. Spring will throw an error because it cannot decide which one to use. @Primary and @Qualifier solve this.
@Primary— marks one Bean as the default. When Spring finds multiple Beans of the same type it automatically injects the one marked with@Primaryunless told otherwise.@Qualifier— used when you want a specific Bean that is not the primary one. You specify the exact Bean name you want injected.
-----------------------------------------------------------------------------------------------------------------------------
application.properties
application.properties is the main configuration file of your Spring Boot application. It lives in src/main/resources/ and is where you configure everything about your app — server port, database connection, JWT secrets, email settings, logging etc. Instead of hardcoding these values directly in your Java code, you put them here and inject them wherever needed.
All environment specific configuration lives in one place, separate from your code. In production you just swap the properties file or use environment variables and your code stays unchanged.
Injecting Values into your Code — @Value
You can inject any value from application.properties directly into your Java class using @Value. The @Value uses ${} syntax with the exact key from application.properties. If the key does not exist and no default is provided Spring will throw an error at startup.
You can provide a default value in case the property is not found in application.properties :
Profiles
Profiles allow you to have different configurations for different environments (development, testing, production) without changing code. So you dont need to keep changing values in same "app.properties" file. Profiles are a way to have different configurations for different environments without changing your code.
Think of profiles as:
dev → Development environment (your laptop)
test → Testing environment (CI/CD pipeline)
prod → Production environment (live users)
NOTE : Spring Boot automatically detects and loads profile-specific property files based on the naming convention.
Step-by-Step Example
Step 1]: Create the Files
application.properties (base config)
server.port=8080 app.name=MyApp
application-dev.properties (dev overrides)
server.port=8081 logging.level.root=DEBUG
application-prod.properties (prod overrides)
server.port=8080 logging.level.root=WARN
Step 2]: Activate a Profile
# Development
java -jar myapp.jar --spring.profiles.active=dev
# Development java -jar myapp.jar --spring.profiles.active=dev
Step 3]: What Spring Boot Does Automatically
When you run with --spring.profiles.active=dev:
1. Spring Boot scans resources folder
2. Finds application.properties → loads it
3. Looks for application-dev.properties → finds it → loads it
4. Merges both files (dev properties override base)
Final configuration:
server.port=8081 ← from dev file (overrides base)
app.name=MyApp ← from base file
logging.level.root=DEBUG ← from dev file
When you run with --spring.profiles.active=dev: 1. Spring Boot scans resources folder 2. Finds application.properties → loads it 3. Looks for application-dev.properties → finds it → loads it 4. Merges both files (dev properties override base) Final configuration: server.port=8081 ← from dev file (overrides base) app.name=MyApp ← from base file logging.level.root=DEBUG ← from dev file
What If Profile File Doesn't Exist?
If profile = "dev" but application-dev.properties doesn't exist: → Spring loads ONLY application.properties → No error, just uses base config → Good for optional profile configs
Priority Order (Higher to Lower)
| Priority | Source | Example |
|---|---|---|
| 1 | Command line | --spring.profiles.active=dev |
| 2 | Environment variable | SPRING_PROFILES_ACTIVE=dev |
| 3 | application-{profile}.properties | application-dev.properties |
| 4 | application.properties | Base config |
When Spring Boot has multiple sources of configuration, it needs to decide which one to use. The higher priority source overrides the lower priority ones.
Think of it as:
Higher priority = Closer to you, you can change it easily
Lower priority = Farther away, acts as default/fallback
-----------------------------------------------------------------------------------------------------------------------------
Comments
Post a Comment