I'm always excited to take on new projects and collaborate with innovative minds.
contact@niteshsynergy.com
https://www.niteshsynergy.com/
Here’s a complete and in-depth explanation of Spring Boot:
Spring Boot is a Java-based framework that simplifies the process of developing Spring applications, especially web applications and microservices . It makes it easier to get started with Spring by providing pre-configured dependencies, auto-configuration, and other features that streamline development. Spring Boot also allows for creating stand-alone applications with minimal configuration, making it easier to deploy and manage.
Spring Boot reduces the amount of configuration needed for Spring applications, allowing developers to focus on the application's logic rather than infrastructure details.
Spring Boot automatically configures many aspects of a Spring application, such as the web server (Tomcat, Jetty, Undertow) and data access libraries, based on the dependencies included in the project.
Spring Boot enables the creation of stand-alone, executable applications that can be run using java -jar
or traditional WAR deployments.
Spring Boot provides sensible defaults for various Spring features, allowing developers to get started quickly and only customize the application as needed.
Spring Boot allows embedding servers like Tomcat, Jetty, or Undertow directly into the application, eliminating the need for external server deployments.
Spring Boot includes features like metrics, health checks, and externalized configuration, which are useful for building production-ready applications.
Spring Boot generally does not require XML configuration or code generation, making it a more modern and efficient approach to Spring development.
Feature | Spring Framework | Spring Boot |
---|---|---|
Definition | A lightweight Java framework for building enterprise applications. | An extension of Spring that simplifies setup, configuration, and deployment. |
Setup | Requires manual configuration of dependencies, XML/Java config, servlet setup, etc. | Auto-configures everything; embedded servers like Tomcat/Jetty; minimal setup. |
Boilerplate | Needs a lot of boilerplate code. | Eliminates boilerplate code. |
Deployment | Requires deploying WAR to external servers. | Can be run as a standalone JAR. |
Focus | Flexibility and full control. | Convention over configuration. |
Use Case | Best for complex, fine-tuned enterprise apps. | Best for microservices and rapid application development. |
@EnableAutoConfiguration
):spring-boot-starter-web
is added, it sets up Spring MVC, Jackson, embedded Tomcat, etc.spring-boot-starter-data-jpa
, spring-boot-starter-security
.java -jar yourapp.jar
.
@SpringBootApplication
@SpringBootApplication
public class SpringbootstudyApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootstudyApplication.class, args);
}
}
SpringApplication.run()
is the entry point.@SpringBootApplication
annotation.
@SpringBootApplication
?@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(...)
public @interface SpringBootApplication {}
@SpringBootApplication
is a meta-annotation — a combination of three critical Spring annotations:
@SpringBootConfiguration
@SpringBootConfiguration
@Configuration
.@Bean
definitions.@Configuration
) — just like you used in traditional Spring.✅ It means: “This is the starting configuration class for Spring Boot.”
@EnableAutoConfiguration
@EnableAutoConfiguration
Core to Spring Boot's magic!
This annotation triggers auto-configuration, i.e., Spring Boot will automatically configure beans based on:
JAR dependencies (spring-boot-starter-data-jpa, spring-boot-starter-web, etc.)
application.properties / application.yml
Internally:
It uses @Import(AutoConfigurationImportSelector.class) to dynamically load configuration classes from:
META-INF/spring.factories (for Spring Boot 2.x)
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports (Spring Boot 3.x)
These contain hundreds of lines like:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
...
✅ It means: “Based on the classpath and properties, configure Spring Boot for me.”
Step 5: @ComponentScan(...)
Enables component scanning starting from the package of your main() class.
It looks for:
@Component
@Service
@Repository
@Controller, @RestController
Any custom stereotype annotations
So if your class is in com.nitesh.app, all subpackages (com.nitesh.app.*) will be scanned.
✅ It means: “Automatically discover and register Spring-managed beans.”
SpringApplication.run(...)
When you call:
SpringApplication.run(SpringbootstudyApplication.class, args);
AnnotationConfigApplicationContext
or ReactiveWebServerApplicationContext
)@SpringBootApplication
@EnableAutoConfiguration
, @ComponentScan
, @SpringBootConfiguration
ApplicationRunner
or CommandLineRunner
beans
Let’s say your project has:
spring-boot-starter-web
in pom.xml
@RestController
application.properties
has some custom portThen:
@EnableAutoConfiguration
sees spring-boot-starter-web
on the classpathDispatcherServletAutoConfiguration
, EmbeddedServletWebServerFactoryAutoConfiguration
, etc.@RestController
class
❌ Myth | ✅ Truth |
---|---|
SpringBootApplication just does component scan | It does much more: configures app context, beans, embedded server, auto-config |
You must always use @ComponentScan separately | Not required — @SpringBootApplication already includes it |
You must define beans manually | Not with Spring Boot — starters + properties + auto-config handles it |
It doesn’t work without XML | Modern Spring Boot doesn’t need XML at all. All Java-based config |
Final Note:
@SpringBootApplication
= @Configuration + @EnableAutoConfiguration + @ComponentScan
SpringApplication.run()
kicks off this chain by loading the class, reading the annotations, creating the context, and booting the app.
Feature | Dependency Injection (DI) | Dependency Management (DM) |
---|---|---|
Focus | Managing object creation and wiring dependencies. | Managing library versions and transitive dependencies. |
Spring Role | Core principle; uses @Autowired , @Component , etc. | Handled via Maven/Gradle using spring-boot-starter-* |
Analogy | Plug-and-play of components. | Handling which tools you need for your code. |
Type | Description | Example |
---|---|---|
Constructor Injection | Dependency passed via constructor. | public PlayerService(GameEngine engine) |
Setter Injection | Dependency passed via setter method. | setGameEngine(GameEngine engine) |
Field Injection | Uses @Autowired directly on fields. | @Autowired GameEngine engine; |
When two or more beans depend on each other in a way that creates a loop.
class A {
@Autowired
B b;
}
class B {
@Autowired
A a;
}
Spring fails with CircularDependencyException
unless you break the loop using @Lazy
, @PostConstruct
, or refactor.
→ Circular Dependency Sol:
Solution 1: Using @Lazy
@Component
public class A {
@Autowired
@Lazy
private B b;
}
@Component
public class B {
@Autowired
private A a;
}
Best for soft-coupled components and avoids eager loading.
@Component
public class A {
private B b;
@Autowired
private B tempB;
@PostConstruct
public void init() {
this.b = tempB;
}
}
@Component
public class B {
@Autowired
private A a;
}
→ Useful when circular dependency can't be avoided but delayed assignment is possible.
Solution 3: Using Refactor to break the cycle
Do your own code.
Feature | Setter | Constructor |
---|---|---|
Optional Dependencies | Good for optional | Constructor fails if missing |
Testability | Not good, harder to test | Better for testing |
Immutability | Mutable | Immutable |
Circular Dependency Risk | More tolerant | Safer; detects early |
Let’s design a simple backend for an online multiplayer RPG game:
GameEngine
: Core class that controls gameplay.PlayerService
: Manages player profiles and states.WeaponService
: Manages player weapons.InventoryService
: Manages player inventory.MatchService
: Handles match sessions between players.@Service
public class PlayerService {
private final GameEngine engine;
private final WeaponService weaponService;
public PlayerService(GameEngine engine, WeaponService weaponService) {
this.engine = engine;
this.weaponService = weaponService;
}
}
What if WeaponService
also needs PlayerService
?
@Service
public class WeaponService {
private final PlayerService playerService;
public WeaponService(@Lazy PlayerService playerService) {
this.playerService = playerService;
}
}
⚠️ This is Circular Dependency, so @Lazy
delays creation to break the loop.
Injection Type | Need @Autowired ? | Notes |
---|---|---|
Constructor (1 only) | ❌ No | Auto-injected by default |
Constructor (multiple) | ✅ Yes | Must specify which one |
Setter | ✅ Yes | Required for Spring to inject value |
Field | ✅ Yes | Used directly on the field |
spring-boot-starter-web
, spring-boot-starter-data-jpa
.PlayerController
handles API calls.PlayerService
into PlayerController
.PlayerService
calls WeaponService
, InventoryService
.java -jar
.
Imagine we are building a Multiplayer Game Backend in Spring Boot with:
GameEngine
: Core game loop logicPlayerService
: Manages player profilesWeaponService
: Handles weaponsInventoryService
: Manages itemsMatchService
: Starts/ends PvP battles
@Service
public class MatchService {
private final GameEngine gameEngine;
private final PlayerService playerService;
public MatchService(GameEngine gameEngine, PlayerService playerService) {
this.gameEngine = gameEngine;
this.playerService = playerService;
}
public void startMatch(String playerId) {
// Uses playerService & gameEngine to start match
}
}
GameEngine
and PlayerService
— no match can start otherwise.
Example Use Case in Gaming:
@Service
public class InventoryService {
private WeaponService weaponService;
@Autowired
public void setWeaponService(WeaponService weaponService) {
this.weaponService = weaponService;
}
public void showWeapons(String playerId) {
// Optional: weaponService may be null for basic players
}
}
InventoryService
can work without it.@Service
public class NotificationService {
@Autowired
private PlayerService playerService;
public void notifyPlayer(String playerId, String message) {
playerService.sendMessage(playerId, message);
}
}
final
— leads to mutable objectsInjection Type | Use When | Gaming Example | Pros | Cons |
---|---|---|---|---|
Constructor | Mandatory dependency | MatchService , GameEngine | Immutable, testable | Breaks with circular dependencies |
Setter | Optional dependency | InventoryService uses WeaponService optionally | Flexible, supports circular | Verbose, less safe |
Field | Fast dev, low priority | NotificationService uses PlayerService | Clean, fast | Poor testability, hidden deps |
@SpringBootApplication
Shortcut for:
@Configuration
@EnableAutoConfiguration
@ComponentScan
@SpringBootApplication
public class BankApp {
public static void main(String[] args) {
SpringApplication.run(BankApp.class, args);
}
}
Bootstraps your whole application — like starting a Banking Core System server.
@ComponentScan
Scans specified packages for Spring beans (@Component
, @Service
, @Controller
, etc.)
@SpringBootApplication
@ComponentScan(basePackages = "com.bank.loan")
public class BankApp { ... }
If LoanService
is in a different package (com.bank.loan
), you tell Spring to look there.
@EnableAutoConfiguration
Automatically configures beans based on dependencies (like DataSource, Web, etc.)
@EnableAutoConfiguration
public class BankAppConfig { ... }
@SpringBootConfiguration
Same as @Configuration
, but specific to Spring Boot.
When you want a class to be the root configuration, but not as the main class.
@SpringBootConfiguration
public class BankAppConfig {
// Bean definitions
}
@RestController
vs @Controller
Annotation | Used for | Returns | Example |
---|---|---|---|
@RestController | REST APIs | JSON/XML | /api/account/balance |
@Controller | MVC/Web Pages | HTML (view name) | /login , /dashboard |
@RestController
@RequestMapping("/api/account")
public class AccountRestController {
@GetMapping("/balance")
public double getBalance() {
return 10500.75;
}
}
@Controller
public class AccountWebController {
@GetMapping("/dashboard")
public String showDashboard() {
return "dashboard"; // returns dashboard.html
}
}
Feature | Path Param | Query Param | @RequestParam | @PathVariable |
---|---|---|---|---|
Definition | Part of the URL path | Appended to the URL after ? | Spring annotation to access query/form parameters | Spring annotation to access path-based variables |
URL Format | /users/101 | /users?id=101 | /users?id=101 | /users/101 |
Used For | Identify a specific resource | Provide optional or filtering data | Accept query/form data | Identify a specific resource |
Mandatory? | Usually yes | Optional | Optional by default | Required by default |
Annotation Required? | No | No | ✅ Yes | ✅ Yes |
Can Accept Map? | ❌ No | ✅ Yes | ✅ Yes | ✅ Yes |
Used In | RESTful API routing | Filtering, pagination, search | Form submissions, search filters | Routing, REST APIs |
✅ Sample Spring Boot Code | java @GetMapping("/users/{id}") public String getUser(@PathVariable int id) { return "User ID: " + id; } | java @GetMapping("/users") public String getUser(@RequestParam int id) { return "User ID: " + id; } | java @GetMapping("/search") public String search(@RequestParam String name, @RequestParam int age) { return "Name: " + name + ", Age: " + age; } | java @GetMapping("/products/{category}/{id}") public String getProduct(@PathVariable Map<String, String> pathVars) { return pathVars.toString(); } |
@PathVariable
Used to extract values from the URL path.
@GetMapping("/account/{accNo}")
public String getAccount(@PathVariable String accNo) {
return "Details for Account: " + accNo;
}
URL → /account/123456
→ accNo = 123456
@RequestParam
Used to extract query parameters from the URL.
@GetMapping("/loan/search")
public String searchLoan(@RequestParam String type, @RequestParam int duration) {
return "Searching for " + type + " loans for " + duration + " years";
}
@RequestBody
Used to accept full JSON body in POST
or PUT
.
@PostMapping("/account")
public String createAccount(@RequestBody Account account) {
return "Account created for " + account.getName();
}
@RequestHeader
Extract values from the header of the HTTP request.
@GetMapping("/validate")
public String validate(@RequestHeader("auth-token") String token) {
return "Token received: " + token;
}
Annotation | Extracts From | Example URL / Use |
---|---|---|
@PathVariable | URL path | /account/123 |
@RequestParam | Query string | /loan?type=home |
@RequestBody | JSON body | POST /account |
@RequestHeader | Headers | auth-token |
If you don't want Spring Boot to auto-configure a database (like H2, DataSource, or JPA):
exclude
in @SpringBootApplication
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class
})
public class BankApp { ... }
🧠 Used in Microservices that don’t use DB, or use custom DB clients (MongoTemplate, JDBC manually, etc.)
🔹 Way 2: Using application.properties
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
Spring Boot uses embedded Tomcat by default.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
Add Jetty dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
You can similarly use Undertow if you prefer lightweight, fast startups for reactive systems.
main()
methodpublic class MyBankApp {
public static void main(String[] args) {
SpringApplication.run(MyBankApp.class, args);
}
}
SpringApplication
instanceApplicationContext
main()
main()
is non-daemonbackground-preinitializer
Tomcat-Acceptor-*
Tomcat-Nio-*
Scheduling threads
(for @Scheduled
)AsyncExecutor threads
(if enabled)@Component
public class StartupTask implements CommandLineRunner {
@Override
public void run(String... args) {
System.out.println("Banking server booted at " + LocalDateTime.now());
}
}
Or:
@Bean
public ApplicationRunner customRunner() {
return args -> {
System.out.println("Executed after Spring Context ready.");
};
}
If you want to use Spring Boot without web (no server):
public static void main(String[] args) {
new SpringApplicationBuilder(MyBankApp.class)
.web(WebApplicationType.NONE)
.run(args);
}
Useful for:
Let's build a Spring Boot + H2 in-memory POC for a Bank Statement Feature, where a user can:
🔖 ENTITY: Transaction
@Entity
public class Transaction {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String accountNumber;
private String description;
private Double amount;
private LocalDate transactionDate;
private String transactionType; // CREDIT / DEBIT
// Getters, Setters, Constructors
}
📦 REPOSITORY: TransactionRepository
@Repository
public interface TransactionRepository extends JpaRepository<Transaction, Long> {
// 1. Transactions for current month
@Query("SELECT t FROM Transaction t WHERE MONTH(t.transactionDate) = MONTH(CURRENT_DATE) AND YEAR(t.transactionDate) = YEAR(CURRENT_DATE)")
List<Transaction> findCurrentMonthTransactions();
// 2. Transactions for a specific month & year
@Query("SELECT t FROM Transaction t WHERE MONTH(t.transactionDate) = :month AND YEAR(t.transactionDate) = :year")
List<Transaction> findByMonthAndYear(@Param("month") int month, @Param("year") int year);
// 3. Transactions between date range
List<Transaction> findByTransactionDateBetween(LocalDate from, LocalDate to);
}
⚙️ SERVICE: TransactionService
@Service
public class TransactionService {
@Autowired
private TransactionRepository transactionRepository;
public List<Transaction> getCurrentMonthTransactions() {
return transactionRepository.findCurrentMonthTransactions();
}
public List<Transaction> getByMonthAndYear(int month, int year) {
return transactionRepository.findByMonthAndYear(month, year);
}
public List<Transaction> getByDateRange(LocalDate start, LocalDate end) {
return transactionRepository.findByTransactionDateBetween(start, end);
}
}
🌐 CONTROLLER: TransactionController
@RestController
@RequestMapping("/api/transactions")
public class TransactionController {
@Autowired
private TransactionService transactionService;
// 1. Current month transactions
@GetMapping("/current")
public List<Transaction> getCurrentMonthTransactions() {
return transactionService.getCurrentMonthTransactions();
}
// 2. By specific month/year
@GetMapping("/month")
public List<Transaction> getByMonthYear(
@RequestParam int month,
@RequestParam int year) {
return transactionService.getByMonthAndYear(month, year);
}
// 3. By date range
@GetMapping("/range")
public List<Transaction> getByDateRange(
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate from,
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate to) {
return transactionService.getByDateRange(from, to);
}
}
GET /api/transactions/current
By month (e.g., March 2025)
GET /api/transactions/month?month=3&year=2025
By range:
GET /api/transactions/range?from=2025-03-01&to=2025-03-31
🛠 application.properties
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.h2.console.enabled=true
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create
spring.h2.console.path=/h2-console
🧪 Insert Test Data on Startup
@Component
public class DataSeeder implements CommandLineRunner {
@Autowired
private TransactionRepository repo;
@Override
public void run(String... args) throws Exception {
repo.save(new Transaction(null, "123456", "Salary", 50000.0, LocalDate.now().withDayOfMonth(5), "CREDIT"));
repo.save(new Transaction(null, "123456", "Grocery", -5000.0, LocalDate.now().withDayOfMonth(10), "DEBIT"));
repo.save(new Transaction(null, "123456", "Rent", -15000.0, LocalDate.of(2025, 3, 1), "DEBIT"));
repo.save(new Transaction(null, "123456", "Gift", 2000.0, LocalDate.of(2025, 2, 25), "CREDIT"));
}
}
Next →
Auto-Configuration in Spring Boot means that Spring Boot attempts to automatically configure your Spring application based on the dependencies and classes available on your classpath.
Enable Auto-Configuration:
When you use @SpringBootApplication
, it includes @EnableAutoConfiguration
which triggers Spring Boot auto-config.
Look for Auto-Configuration Classes:
Spring Boot scans META-INF/spring.factories files from all JAR dependencies.
It looks for all classes listed under the key:org.springframework.boot.autoconfigure.EnableAutoConfiguration
Evaluate Conditions:
Each auto-config class is annotated with conditional annotations like:
@ConditionalOnClass
(load only if a certain class is on the classpath)@ConditionalOnBean
(only if some bean is already defined)@ConditionalOnMissingBean
(only if no other bean of the same type exists)@ConditionalOnProperty
(enable only if specific properties are set)Apply Configurations:
If all conditions pass, the auto-config class creates beans and configures the context.
spring/org.springframework.boot.autoconfigure
spring.factories
?META-INF/
inside Spring Boot JARs and your dependencies.spring.factories
entry:# org.springframework.boot.autoconfigure.EnableAutoConfiguration= lists classes for auto-config
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
javax.sql.DataSource
class is present.DataSourceAutoConfiguration
from the spring.factories
.DataSource
bean if no other DataSource
is defined by you.spring.datasource.url
, username
, etc. from your application.properties
.
You can customize or disable auto-configuration:
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class MyApp { ... }
Spring Boot will back off if it finds user-defined beans of the same type (thanks to @ConditionalOnMissingBean
).
Concept | Description |
---|---|
Auto-Configuration | Spring Boot auto-magically configures beans based on classpath & properties. |
@EnableAutoConfiguration | Activates auto-configuration support |
spring.factories | File listing auto-configuration classes to load |
Conditional Annotations | Conditions on class presence, bean presence, properties, etc. to selectively apply auto-config |
Customization | Override by excluding auto-config classes or defining your own beans |
spring-boot-starter-web
, spring-boot-starter-data-jpa
, etc.).Create a new project (can be a separate maven module or jar) that will hold your auto-configuration.
Create a Java config class with @Configuration
. Annotate it with @ConditionalOnClass
, @ConditionalOnMissingBean
, or other conditional annotations to make it smart.
Example: Suppose you want to auto-configure a simple service bean MyService
only if the class com.example.service.MyService
is on the classpath.
package com.example.autoconfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@Configuration
@ConditionalOnClass(name = "com.example.service.MyService")
public class MyServiceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MyService myService() {
return new MyService();
}
}
spring.factories
fileCreate the file in your resources folder:
src/main/resources/META-INF/spring.factories
Add this content:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.autoconfig.MyServiceAutoConfiguration
MyService
bean if the conditions are met.Create a test Spring Boot app, add your jar, and check if the bean is injected automatically.
You can add @ConditionalOnProperty
to enable/disable your auto-config via properties.
@ConditionalOnProperty(name = "my.service.enabled", havingValue = "true", matchIfMissing = true)
This way, users can control your auto-config by adding:
my.service.enabled=false
Next →
Starters are convenient dependency descriptors provided by Spring Boot.
They bundle a set of related dependencies you usually need for a certain functionality into one single dependency.
Why Starters?
Starter Dependency | Purpose |
---|---|
spring-boot-starter-web | Build RESTful web applications with Spring MVC and embedded Tomcat |
spring-boot-starter-data-jpa | Spring Data JPA with Hibernate for DB access |
spring-boot-starter-security | Adds Spring Security for authentication/authorization |
spring-boot-starter-test | Testing libraries (JUnit, Mockito, etc.) |
Example: Adding spring-boot-starter-web
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
This adds everything to build REST controllers, handle JSON (Jackson), and embed Tomcat server.
Next →
Steps to Create a Custom Spring Boot Starter in Java
This project will be your starter module, e.g. mycompany-spring-boot-starter-foo
.
pom.xml
or build.gradle
, add dependencies that you want to expose to users.compile
or implementation
(to be transitive).spring-boot-autoconfigure
as a dependency with provided
or normal scope because you will create auto-config classes.Example in Maven pom.xml
:
<dependencies>
<!-- The library/dependencies you want to bundle -->
<dependency>
<groupId>com.example</groupId>
<artifactId>foo-library</artifactId>
<version>1.0.0</version>
</dependency>
<!-- Required for auto-configuration -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>${spring.boot.version}</version>
</dependency>
</dependencies>
@Configuration
and @ConditionalOnXXX
annotations (like @ConditionalOnClass
, etc.) to activate your config only if dependencies are present.@AutoConfigureAfter
or @AutoConfigureBefore
if needed.@EnableConfigurationProperties
if you want to bind properties.Example:
package com.example.foo.autoconfigure;
import com.example.foo.FooService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnClass(FooService.class) // Enable only if FooService is on classpath
public class FooAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public FooService fooService() {
return new FooService();
}
}
spring.factories
src/main/resources/META-INF/spring.factories
file.org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.foo.autoconfigure.FooAutoConfiguration
spring-configuration-metadata.json
@ConfigurationProperties
and generate metadata via spring-boot-configuration-processor
.mvn clean install
Just add your starter dependency:
<dependency>
<groupId>com.example</groupId>
<artifactId>mycompany-spring-boot-starter-foo</artifactId>
<version>1.0.0</version>
</dependency>
When your app runs, Spring Boot will pick your auto-config and configure your beans.
Next →
sdk install springboot
# or
brew install springboot
.properties
or .yml
format.Example: application.properties
game.leaderboard.max-players=100
game.leaderboard.timezone=UTC
application-dev.properties
, application-prod.yml
.-Dspring.profiles.active=dev
@Value
and @ConfigurationProperties
@Value
@Component
public class GameSettings {
@Value("${game.leaderboard.max-players}")
private int maxPlayers;
}
@ConfigurationProperties
Better for grouping related props.
@Component
@ConfigurationProperties(prefix = "game.leaderboard")
public class LeaderboardConfig {
private int maxPlayers;
private String timezone;
// getters & setters
}
You have a leaderboard service that needs to:
max-players
)timezone
)application-dev.yml
game:
leaderboard:
max-players: 10
timezone: "America/New_York"
application-prod.yml
game:
leaderboard:
max-players: 1000
timezone: "UTC"
Concept | Description | Example Use Case (Gaming) |
---|---|---|
Starters | Pre-bundled dependencies | Add web, JPA, security starters for leaderboard app |
CLI | Quick prototyping using command line | Run simple Groovy API for player scores |
application.properties | External config file for app settings | Set max leaderboard size, timezone |
Profiles | Env-specific config override | Different limits in dev and prod |
@Value | Inject single property | Inject maxPlayers directly into a bean |
@ConfigurationProperties | Inject multiple related properties grouped | Group leaderboard configs into a config bean |
7️⃣ Spring Boot Profiles →
Please visit on if you wish shortcut study →https://niteshsynergy.com/spring-boot-profile
Else→
application-{profile}.properties
or .yml
.@Profile("profileName")
so those beans only load when that profile is active.-Dspring.profiles.active=profileName
SPRING_PROFILES_ACTIVE=profileName
Create separate config files for each profile:
application-test.yml (MySQL)
spring:
datasource:
url: jdbc:mysql://localhost:3306/testdb
username: testuser
password: testpass
jpa:
hibernate:
ddl-auto: update
show-sql: true
application-prod.yml (MongoDB)
application-uat.yml (H2 In-memory DB)
spring:
datasource:
url: jdbc:h2:mem:uatdb;DB_CLOSE_DELAY=-1
driverClassName: org.h2.Driver
username: sa
password: password
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
@Profile
If you want to define separate beans depending on profile, annotate them:
@Configuration
public class DataSourceConfig {
@Bean
@Profile("test")
public DataSource mysqlDataSource() {
// configure and return MySQL DataSource
}
@Bean
@Profile("prod")
public MongoClient mongoClient() {
// configure and return MongoDB client
}
@Bean
@Profile("uat")
public DataSource h2DataSource() {
// configure and return H2 DataSource
}
}
You can activate profiles in multiple ways:
java -jar app.jar --spring.profiles.active=test
# or
java -jar app.jar -Dspring.profiles.active=prod
application.properties (default profile)
spring.profiles.active=uat
Environment variable
export SPRING_PROFILES_ACTIVE=prod
You want your banking app to fetch transactions from different databases depending on environment.
The service layer can stay the same, Spring injects the correct datasource/bean based on profile.
Code Example: Repository Interface
public interface TransactionRepository {
List<Transaction> findByUserIdAndDateRange(String userId, LocalDate start, LocalDate end);
}
MySQL Implementation (Test Profile)
@Repository
@Profile("test")
public class MysqlTransactionRepository implements TransactionRepository {
// Use JdbcTemplate or JPA with MySQL connection
}
MongoDB Implementation (Prod Profile)
@Repository
@Profile("prod")
public class MongoTransactionRepository implements TransactionRepository {
// Use MongoTemplate or MongoRepository here
}
H2 Implementation (UAT Profile)
@Repository
@Profile("uat")
public class H2TransactionRepository implements TransactionRepository {
// Use JdbcTemplate or JPA with H2 DB
}
Complete Spring Boot Example: Banking Transactions with Profiles & Multiple DBs
Project Structure:
src/main/java/com/example/banking
├─ controller
│ └─ TransactionController.java
├─ service
│ └─ TransactionService.java
├─ repository
│ ├─ TransactionRepository.java (interface)
│ ├─ MysqlTransactionRepository.java (@Profile test)
│ ├─ MongoTransactionRepository.java (@Profile prod)
│ └─ H2TransactionRepository.java (@Profile uat)
├─ model
│ └─ Transaction.java
├─ config
│ └─ DataSourceConfig.java
└─ BankingApplication.java
1. Model - Transaction.java
package com.example.banking.model;
import java.time.LocalDate;
public class Transaction {
private String id;
private String userId;
private LocalDate date;
private double amount;
private String description;
// getters and setters
public Transaction() {}
public Transaction(String id, String userId, LocalDate date, double amount, String description) {
this.id = id;
this.userId = userId;
this.date = date;
this.amount = amount;
this.description = description;
}
// Getters and setters below
}
2. Repository Interface
package com.example.banking.repository;
import com.example.banking.model.Transaction;
import java.time.LocalDate;
import java.util.List;
public interface TransactionRepository {
List<Transaction> findByUserIdAndDateRange(String userId, LocalDate start, LocalDate end);
}
3. MySQL Implementation (test profile)
package com.example.banking.repository;
import com.example.banking.model.Transaction;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.sql.ResultSet;
import java.time.LocalDate;
import java.util.List;
@Repository
@Profile("test")
public class MysqlTransactionRepository implements TransactionRepository {
private final JdbcTemplate jdbcTemplate;
public MysqlTransactionRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public List<Transaction> findByUserIdAndDateRange(String userId, LocalDate start, LocalDate end) {
String sql = "SELECT id, user_id, date, amount, description FROM transactions WHERE user_id = ? AND date BETWEEN ? AND ?";
return jdbcTemplate.query(sql, (ResultSet rs, int rowNum) -> new Transaction(
rs.getString("id"),
rs.getString("user_id"),
rs.getDate("date").toLocalDate(),
rs.getDouble("amount"),
rs.getString("description")
), userId, java.sql.Date.valueOf(start), java.sql.Date.valueOf(end));
}
}
4. MongoDB Implementation (prod profile)
package com.example.banking.repository;
import com.example.banking.model.Transaction;
import org.springframework.context.annotation.Profile;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Repository;
import java.time.LocalDate;
import java.util.List;
@Repository
@Profile("prod")
public class MongoTransactionRepository implements TransactionRepository {
private final MongoTemplate mongoTemplate;
public MongoTransactionRepository(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
@Override
public List<Transaction> findByUserIdAndDateRange(String userId, LocalDate start, LocalDate end) {
Query query = new Query();
query.addCriteria(Criteria.where("userId").is(userId)
.andOperator(Criteria.where("date").gte(start), Criteria.where("date").lte(end)));
return mongoTemplate.find(query, Transaction.class);
}
}
5. H2 Implementation (uat profile)
package com.example.banking.repository;
import com.example.banking.model.Transaction;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.sql.ResultSet;
import java.time.LocalDate;
import java.util.List;
@Repository
@Profile("uat")
public class H2TransactionRepository implements TransactionRepository {
private final JdbcTemplate jdbcTemplate;
public H2TransactionRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public List<Transaction> findByUserIdAndDateRange(String userId, LocalDate start, LocalDate end) {
String sql = "SELECT id, user_id, date, amount, description FROM transactions WHERE user_id = ? AND date BETWEEN ? AND ?";
return jdbcTemplate.query(sql, (ResultSet rs, int rowNum) -> new Transaction(
rs.getString("id"),
rs.getString("user_id"),
rs.getDate("date").toLocalDate(),
rs.getDouble("amount"),
rs.getString("description")
), userId, java.sql.Date.valueOf(start), java.sql.Date.valueOf(end));
}
}
6. Service Layer
package com.example.banking.service;
import com.example.banking.model.Transaction;
import com.example.banking.repository.TransactionRepository;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.util.List;
@Service
public class TransactionService {
private final TransactionRepository transactionRepository;
public TransactionService(TransactionRepository transactionRepository) {
this.transactionRepository = transactionRepository;
}
public List<Transaction> getTransactionsByUserAndDateRange(String userId, LocalDate start, LocalDate end) {
return transactionRepository.findByUserIdAndDateRange(userId, start, end);
}
}
7. Controller
package com.example.banking.controller;
import com.example.banking.model.Transaction;
import com.example.banking.service.TransactionService;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.util.List;
@RestController
@RequestMapping("/api/transactions")
public class TransactionController {
private final TransactionService transactionService;
public TransactionController(TransactionService transactionService) {
this.transactionService = transactionService;
}
// 1. Fetch transactions by month (year-month format)
@GetMapping("/month")
public List<Transaction> getTransactionsByMonth(
@RequestParam String userId,
@RequestParam int year,
@RequestParam int month) {
LocalDate start = LocalDate.of(year, month, 1);
LocalDate end = start.withDayOfMonth(start.lengthOfMonth());
return transactionService.getTransactionsByUserAndDateRange(userId, start, end);
}
// 2. Fetch transactions by custom date range
@GetMapping("/range")
public List<Transaction> getTransactionsByDateRange(
@RequestParam String userId,
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate start,
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate end) {
return transactionService.getTransactionsByUserAndDateRange(userId, start, end);
}
// 3. Fetch transactions for current month
@GetMapping("/currentMonth")
public List<Transaction> getTransactionsCurrentMonth(@RequestParam String userId) {
LocalDate now = LocalDate.now();
LocalDate start = now.withDayOfMonth(1);
LocalDate end = now.withDayOfMonth(now.lengthOfMonth());
return transactionService.getTransactionsByUserAndDateRange(userId, start, end);
}
}
8. DataSourceConfig
For MySQL and H2 datasource configuration:
package com.example.banking.config;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import javax.sql.DataSource;
@Configuration
public class DataSourceConfig {
@Bean
@Profile("test")
public DataSource mysqlDataSource() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/testdb");
ds.setUsername("testuser");
ds.setPassword("testpass");
return ds;
}
@Bean
@Profile("uat")
public DataSource h2DataSource() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("org.h2.Driver");
ds.setUrl("jdbc:h2:mem:uatdb;DB_CLOSE_DELAY=-1");
ds.setUsername("sa");
ds.setPassword("");
return ds;
}
@Bean
@Profile("prod")
public MongoClient mongoClient() {
return MongoClients.create("mongodb://prodUser:prodPass@prodHost:27017/proddb");
}
}
9. Main Application Class
package com.example.banking;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BankingApplication {
public static void main(String[] args) {
SpringApplication.run(BankingApplication.class, args);
}
}
10. application.yml
spring:
profiles:
active: uat # Change this to test, prod or uat to switch environment
# Common configs can go here
application.yml
or via command line:# Run with test profile (MySQL)
mvn spring-boot:run -Dspring-boot.run.profiles=test
# Run with prod profile (MongoDB)
mvn spring-boot:run -Dspring-boot.run.profiles=prod
# Run with uat profile (H2)
mvn spring-boot:run -Dspring-boot.run.profiles=uat
Next →
Add this dependency in your pom.xml
or build.gradle
:
Maven
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
Gradle
developmentOnly("org.springframework.boot:spring-boot-devtools")
target/classes
or build/classes
folders. When a class changes, the classloader reloads the application context.src/main/java
, resources
) by default.Configuring Restart:
You can exclude or include files from restart using spring.devtools.restart.exclude
and spring.devtools.restart.additional-paths
in application.properties
.
spring.devtools.restart.exclude=static/**,public/**
spring.devtools.restart.additional-paths=src/main/webapp
How to use:
Just enable the LiveReload plugin/extension in your browser (available for Chrome/Firefox), or install a LiveReload client.
spring.devtools.livereload.enabled=false
spring.thymeleaf.cache=false
Restart triggers:
DevTools restarts only on changes to .class
or .properties
files. If you add new files, you might need a manual restart.
Remote Restart:
DevTools supports remote restart where you can connect your IDE to a remote app and trigger restart.
Feature | Description | Developer Benefit |
---|---|---|
Hot Swapping | Reload changed classes without manual restart | Faster dev cycle, instant feedback |
Auto Restart | Automatically restarts on code/resource changes | Saves time, no manual restarts |
LiveReload | Auto refresh browser on resource changes | No manual refresh needed, better front-end dev |
Error Pages | Detailed stack trace with source in browser | Easier debugging and faster issue identification |
spring-boot-starter
).Logging levels define the severity and verbosity of logs. The levels in increasing order of severity are:
Level | Description | When to use |
---|---|---|
TRACE | Most detailed, logs everything including fine-grained events. | Debugging very detailed issues |
DEBUG | Less detailed than TRACE, used for debugging. | Debugging logic flow and state changes |
INFO | General application info like startup, shutdown, major events | Production useful info, app lifecycle |
WARN | Potential problems or important notices | Warnings that don’t stop app but should be noticed |
ERROR | Serious issues like exceptions | Errors that might cause failure |
OFF | Turns off logging | Disable logging |
You can configure logging level globally or per package/class using application.properties
or application.yml
.
Example: application.properties
# Global logging level
logging.level.root=INFO
# Package specific level
logging.level.com.sogdo.banking=DEBUG
# Set SQL logging to TRACE (very verbose)
logging.level.org.hibernate.SQL=TRACE
src/main/resources
:Framework | Config file name |
---|---|
Logback | logback-spring.xml or logback.xml |
Log4j2 | log4j2-spring.xml or log4j2.xml |
Java Util Logging | logging.properties |
Create logback-spring.xml
Use org.slf4j.Logger
to log in your classes.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Service
public class BankStatementService {
private static final Logger logger = LoggerFactory.getLogger(BankStatementService.class);
public List<Transaction> getTransactions(Long userId) {
logger.info("Fetching transactions for userId: {}", userId);
try {
// logic to fetch transactions
logger.debug("Querying database for transactions");
// ...
} catch (Exception e) {
logger.error("Error fetching transactions", e);
}
return List.of(); // example
}
}
logger.info("User {} logged in", userId)
) to avoid unnecessary string concatenation.Concept | Description | Example Use Case |
---|---|---|
Default Logging | Uses Logback out of the box | Console logs with timestamp and level |
Logging Levels | TRACE, DEBUG, INFO, WARN, ERROR | DEBUG for dev, INFO for production |
External Config | Custom logback-spring.xml to tailor logging | Different logging pattern per environment |
Logger API | Use SLF4J Logger for logging in classes | logger.info("User login: {}", userId) |
@RestController
and HTTP Method Annotations@RestController
= @Controller + @ResponseBody
@GetMapping
– For GET requests@PostMapping
– For POST requests@PutMapping
– For PUT requests@DeleteMapping
– For DELETE requests@RestController
@RequestMapping("/api/banking")
public class TransactionController {
@GetMapping("/transactions")
public List<Transaction> getAllTransactions() {
// Return all transactions (JSON response by default)
return transactionService.getAll();
}
@PostMapping("/transaction")
public Transaction createTransaction(@RequestBody Transaction transaction) {
// Accept JSON request body, create and return transaction
return transactionService.save(transaction);
}
}
Aspect | REST Controller | MVC Controller |
---|---|---|
Purpose | Expose RESTful APIs, return data (JSON/XML) | Return Views (HTML pages, JSP, Thymeleaf) |
Annotation | @RestController | @Controller |
Response Type | JSON/XML typically | HTML View templates |
Example Use Case | Mobile apps, SPA frontend consuming JSON | Server-side rendered web pages |
spring-boot-starter-xml
(which brings JAXB or Jackson-dataformat-xml).Accept
HTTP header sent by the client.@GetMapping(value = "/transaction/{id}", produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
public Transaction getTransaction(@PathVariable Long id) {
return transactionService.findById(id);
}
Accept: application/json
, response will be JSON.Accept: application/xml
, response will be XML.@RequestBody
to map HTTP request body (JSON/XML) to Java object.@ResponseBody
(or @RestController
) to automatically convert Java object to JSON/XML response.ResponseEntity<T>
for more control over HTTP status and headers.@PostMapping("/transaction")
public ResponseEntity<Transaction> createTransaction(@RequestBody Transaction transaction) {
Transaction savedTransaction = transactionService.save(transaction);
return ResponseEntity.status(HttpStatus.CREATED).body(savedTransaction);
}
Imagine you want to provide APIs for users to fetch bank statements by:
@RestController
@RequestMapping("/api/banking/statements")
public class BankStatementController {
@Autowired
private BankStatementService statementService;
// 1. Current month statements
@GetMapping("/current-month/{userId}")
public List<Transaction> getCurrentMonthStatement(@PathVariable Long userId) {
return statementService.getTransactionsForCurrentMonth(userId);
}
// 2. Specific month (year and month as params)
@GetMapping("/month/{userId}")
public List<Transaction> getStatementForMonth(
@PathVariable Long userId,
@RequestParam int year,
@RequestParam int month) {
return statementService.getTransactionsForMonth(userId, year, month);
}
// 3. Date range (startDate and endDate as query params)
@GetMapping("/range/{userId}")
public List<Transaction> getStatementForDateRange(
@PathVariable Long userId,
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate) {
return statementService.getTransactionsForDateRange(userId, startDate, endDate);
}
}
@PathVariable
— Used for dynamic parts of URL path, e.g., userId.@RequestParam
— Used to pass query parameters, e.g., year, month, dates.@DateTimeFormat
— Ensures dates in query params are parsed properly.
Annotation | Purpose | Example |
---|---|---|
@RestController | Marks class as REST controller (returns response body) | @RestController |
@GetMapping | Maps GET HTTP requests | @GetMapping("/transactions") |
@PostMapping | Maps POST HTTP requests | @PostMapping("/transaction") |
@RequestBody | Binds HTTP request body (JSON/XML) to Java object | public void save(@RequestBody Transaction txn) |
@ResponseBody | Returns Java object as response body | Usually implicit in @RestController |
@PathVariable | Extracts variable from URI path | /user/{id} , @PathVariable Long id |
@RequestParam | Extracts query parameters from URL | /transactions?year=2024&month=5 |
BasicErrorController
(which implements ErrorController
interface).Example: When a request hits an unknown URL or an exception is thrown, you get a default page like:
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
There was an unexpected error (type=Not Found, status=404).
No message available
ErrorController
to map /error
path to error handling logic.ErrorController
to override default behavior for all errors globally.@Controller
public class CustomErrorController implements ErrorController {
@RequestMapping("/error")
public String handleError(HttpServletRequest request) {
// You can retrieve error status and details from request attributes
Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
Exception exception = (Exception) request.getAttribute("javax.servlet.error.exception");
// Return your own error view name
return "customErrorPage";
}
@Override
public String getErrorPath() {
return "/error";
}
}
src/main/resources/templates/error/
named by HTTP status codes:
error/404.html
error/500.html
error/default.html
Spring Boot will automatically pick these pages for the respective errors.
@ControllerAdvice
or @RestControllerAdvice
with @ExceptionHandler
.@ControllerAdvice
(Core to Spring Boot Error Handling)@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(TransactionNotFoundException.class)
public ResponseEntity<ErrorResponse> handleTransactionNotFound(TransactionNotFoundException ex) {
ErrorResponse error = new ErrorResponse(HttpStatus.NOT_FOUND.value(),
ex.getMessage(),
System.currentTimeMillis());
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGenericException(Exception ex) {
ErrorResponse error = new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(),
"Something went wrong. Please try again.",
System.currentTimeMillis());
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@RestControllerAdvice
acts globally.@ExceptionHandler
specifies exception type to handle.public class ErrorResponse {
private int status;
private String message;
private long timeStamp;
// constructors, getters, setters
}
Add the dependency in pom.xml
:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
By default, some endpoints are enabled (like /actuator/health
, /actuator/info
), others are disabled or restricted.
Endpoint | Description | Default Exposure |
---|---|---|
/actuator/health | Shows app health status | Public |
/actuator/info | Custom application info | Public |
/actuator/metrics | Metrics like memory, CPU, etc. | Restricted |
/actuator/httptrace | Recent HTTP requests | Restricted |
/actuator/env | Environment properties | Restricted |
/actuator/health
endpoint gives you the health status of the app: UP
, DOWN
, OUT_OF_SERVICE
, or UNKNOWN
.@Component
public class BankDbHealthIndicator implements HealthIndicator {
@Override
public Health health() {
// Check if bank database is reachable
boolean dbUp = checkDatabaseConnection(); // your logic here
if (dbUp) {
return Health.up().withDetail("Bank DB", "Available").build();
} else {
return Health.down().withDetail("Bank DB", "Not reachable").build();
}
}
private boolean checkDatabaseConnection() {
// Implementation: ping DB or query a simple select 1
return true; // assuming OK here
}
}
/actuator/metrics
endpoint exposes key performance metrics./actuator/metrics/jvm.memory.used
.You can create your own actuator endpoints to expose custom info or operations.
@Component
@Endpoint(id = "transactionCount")
public class TransactionCountEndpoint {
private final TransactionService transactionService;
public TransactionCountEndpoint(TransactionService transactionService) {
this.transactionService = transactionService;
}
@ReadOperation
public int transactionsToday() {
return transactionService.countTransactionsForToday();
}
}
@Component
@Endpoint(id = "transactionCount")
public class TransactionCountEndpoint {
private final TransactionService transactionService;
public TransactionCountEndpoint(TransactionService transactionService) {
this.transactionService = transactionService;
}
@ReadOperation
public int transactionsToday() {
return transactionService.countTransactionsForToday();
}
}
/actuator/transactionCount
@WriteOperation
or @DeleteOperation
for other REST verbs.By default, sensitive actuator endpoints like /metrics
and /env
are not exposed publicly.
application.properties
:
management.endpoints.web.exposure.include=health,info,metrics,env,transactionCount
Minimal security example:
@Configuration
public class ActuatorSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole("ACTUATOR_ADMIN")
.and()
.httpBasic();
}
}
ACTUATOR_ADMIN
to access actuator endpoints.application.properties
or via other security mechanisms.
Next→
Spring Boot provides powerful testing support to validate layers (controller, service, repository) with minimal configuration using special annotations called Test Slices.
@SpringBootTest
@SpringBootTest
public class BankingIntegrationTest {
@Autowired
private TransactionService transactionService;
@Test
void testMonthlyStatementFetch() {
List<Transaction> list = transactionService.getTransactionsByMonth("2024-12");
assertEquals(10, list.size());
}
}
🔧 Use when: Testing multiple beans (controller + service + repo + config)
@WebMvcTest
@Service
, @Repository
loaded unless manually mocked.
@WebMvcTest(TransactionController.class)
public class TransactionControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private TransactionService transactionService;
@Test
void testGetCurrentMonthTransactions() throws Exception {
when(transactionService.getCurrentMonthTransactions()).thenReturn(mockedList());
mockMvc.perform(get("/transactions/current-month"))
.andExpect(status().isOk());
}
}
🔧 Use when: You want to test only Controller + Request/Response mapping.
@DataJpaTest
Loads only JPA repository and DB layer (uses embedded H2 by default).
Useful for DB query testing without loading full app.
@DataJpaTest
public class TransactionRepositoryTest {
@Autowired
private TransactionRepository transactionRepo;
@Test
void testFindByMonth() {
transactionRepo.save(new Transaction(...));
List<Transaction> result = transactionRepo.findByMonth("2025-01");
assertFalse(result.isEmpty());
}
}
🔧 Use when: Testing DB logic in isolation.
@ContextConfiguration
to load custom context in special cases.
By default, Spring Boot builds fat JAR with embedded server.
In pom.xml
:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
To build:
mvn clean package
To run:
java -jar target/banking-app-1.0.jar
If deploying to external Tomcat server (rare today):
war
SpringBootServletInitializer
configure()
method
Spring Boot supports overriding config without rebuilding the app:
java -jar banking-app.jar --spring.config.location=/opt/config/
or via env variables:
SPRING_DATASOURCE_URL=jdbc:mysql://... java -jar ...
Spring Boot supports:
Server | Dependency |
---|---|
Tomcat (default) | spring-boot-starter-web |
Jetty | spring-boot-starter-jetty |
Undertow | spring-boot-starter-undertow |
To switch:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope> <!-- disables embedded tomcat -->
</dependency>
CommandLineRunner
& ApplicationRunner
Used to execute logic at startup.
Use-case: Initializing DB or caching configs
@Component
public class StartupRunner implements CommandLineRunner {
@Override
public void run(String... args) {
System.out.println("Initializing core banking APIs...");
}
}
@Component
public class StartupRunner implements CommandLineRunner {
@Override
public void run(String... args) {
System.out.println("Initializing core banking APIs...");
}
}
ApplicationRunner
is similar, but provides ApplicationArguments
.
Spring Boot (2.3+) handles graceful shutdown using:
server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=30s
You can hook into shutdown:
@PreDestroy
public void onDestroy() {
// cleanup banking transactions or close DBs
}
SPring Banners last topic you can do it own.
end → Please support our work on https://razorpay.me/@niteshsynergy
Your email address will not be published. Required fields are marked *