On this page

What is the Spring Framework?

What does Spring Boot add?

Auto-configuration and how @Conditional works

Starters

Dependency management and opinionated defaults

The embedded server and the executable jar

Externalized configuration and profiles

Actuator

DevTools

Test slices

The same REST service, set up both ways

How to override auto-configuration

What Spring Boot does not change

When plain Spring still makes sense

Spring vs Spring Boot compared

Frequently asked questions

Related reading

Spring vs Spring Boot: What Boot Actually Adds

Image
Arslan Ahmad
Spring is the framework that creates and connects your objects. Spring Boot is a layer on top that adds automatic configuration, starters, an embedded server, and production tools.
Image

What is the Spring Framework?

What does Spring Boot add?

Auto-configuration and how @Conditional works

Starters

Dependency management and opinionated defaults

The embedded server and the executable jar

Externalized configuration and profiles

Actuator

DevTools

Test slices

The same REST service, set up both ways

How to override auto-configuration

What Spring Boot does not change

When plain Spring still makes sense

Spring vs Spring Boot compared

Frequently asked questions

Related reading

Spring is a Java framework that creates your application's objects and connects them to each other. Spring Boot is a layer built on top of Spring that configures those objects for you, packages the application with its own web server, and adds tools for running it in production. Every Spring Boot application is a Spring application, but a plain Spring application needs much more setup before it can serve its first request.

The difference matters because the two names are often used as if they were two competing frameworks, and they are not. Boot does not replace any part of Spring. It removes the setup work that most teams wrote by hand, and it chooses sensible defaults that you can override one at a time.

This article explains what the Spring Framework does on its own, then walks through each thing Boot adds with a before-and-after comparison. It ends with the same small service built both ways, a comparison table, and the cases where plain Spring is still the right choice.

What is the Spring Framework?

The Spring Framework is a set of Java libraries whose central part is a container that builds your objects and hands each one the other objects it needs. The container is called the inversion of control container (IoC container), because your code no longer creates its own collaborators. The container creates them and passes them in.

Passing collaborators in from the outside is called dependency injection. A class declares what it needs, usually as constructor parameters, and the container supplies matching objects. The objects the container manages are called beans.

@Service public class OrderService { private final PaymentClient payments; public OrderService(PaymentClient payments) { // the container supplies this this.payments = payments; } }

The benefit is that OrderService never writes new PaymentClient(...). In a test you can pass a fake client, and in production the container passes the real one. The same rule applies across hundreds of classes, which keeps large codebases testable and loosely connected.

Around the container, Spring provides modules for the common jobs of a server application:

  • Spring MVC handles web requests. A class marked @RestController maps URLs to Java methods, and Spring converts the returned objects to JSON.
  • Data access and transactions give you JdbcTemplate, integration with JPA, and the @Transactional annotation, which starts and commits a database transaction around a method.
  • AOP (aspect-oriented programming) lets Spring wrap a method with extra behavior, such as a transaction or a security check, without changing the method itself.
  • Related projects such as Spring Security, Spring Data, and Spring Kafka build on the same container.

What the framework does not do is decide anything for you. You choose which modules to include, which versions of each library to use, which beans to declare, and how to deploy the result. That freedom is useful, but in practice most teams made the same choices every time and wrote the same configuration classes in every project.

What does Spring Boot add?

Spring Boot is a project from the same team that sits on top of the Spring Framework. It adds no new programming model. Your controllers, services, and repositories look the same in both. What Boot adds is a set of features that remove repeated setup and make an application ready to run on its own.

Spring Boot adds a layer between your application code and the Spring Framework: auto-configuration, starters, embedded server, externalized configuration, Actuator, DevTools, and test slices

The sections below take each feature in turn. Each one shows what you write without Boot and what you write with it.

Auto-configuration and how @Conditional works

Auto-configuration is Boot's ability to create common beans for you, based on what it finds on the classpath and in your settings. If the Jackson library is present, Boot configures JSON conversion. If a database driver is present and you have set a database URL, Boot creates a connection pool for you.

Without Boot, you declare those beans yourself. A typical plain Spring project has a configuration class like this one for the database alone:

@Configuration @EnableTransactionManagement public class DataConfig { @Bean public DataSource dataSource() { HikariDataSource ds = new HikariDataSource(); ds.setJdbcUrl("jdbc:postgresql://localhost:5432/shop"); ds.setUsername("shop"); ds.setPassword(System.getenv("DB_PASSWORD")); ds.setMaximumPoolSize(10); return ds; } @Bean public PlatformTransactionManager transactionManager(DataSource ds) { return new DataSourceTransactionManager(ds); } }

With Boot, the same result comes from three lines of properties, and the configuration class disappears:

spring.datasource.url=jdbc:postgresql://localhost:5432/shop spring.datasource.username=shop spring.datasource.password=${DB_PASSWORD}

Auto-configuration is not hidden code generation. Each auto-configuration is an ordinary @Configuration class shipped inside Boot, listed in a file named META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. What makes these classes different is that every bean in them is guarded by conditions.

A condition is an annotation that tells Spring to create a bean only when a test passes. The base mechanism, @Conditional, comes from the Spring Framework itself. Boot adds a family of ready-made conditions on top of it:

  • @ConditionalOnClass creates the bean only if a given class is on the classpath, such as a database driver.
  • @ConditionalOnMissingBean creates the bean only if you have not already declared one of the same type.
  • @ConditionalOnProperty creates the bean only if a setting has a given value.
  • @ConditionalOnWebApplication creates the bean only if the application is a web application.

@ConditionalOnMissingBean is the most important of these, because it makes every default easy to replace. If you declare your own DataSource bean, Boot's default data source backs off and yours is used. You never need to work around the default, because the default exists only while you have not provided one.

How one auto-configured bean is decided: is the library on the classpath, is the property set, has the application already declared its own bean, then either create the default or back off

When you want to know why a bean was or was not created, start the application with the --debug flag. Boot prints a condition evaluation report that lists every auto-configuration, whether it matched, and which condition decided it. This report answers most questions about Boot's behavior faster than reading source code.

Starters

A starter is a single dependency that pulls in a tested set of libraries for one job. Instead of listing the web framework, the JSON library, the validation library, and the server separately, you add one starter and get all of them at versions known to work together.

Without Boot, a web project's build file lists each library and each version:

<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>7.0.0</version> </dependency> <dependency> <groupId>tools.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>3.0.0</version> </dependency> <!-- plus validation, logging, and a servlet container, each with a version -->

With Boot, one starter replaces that list:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webmvc</artifactId> </dependency>

In Spring Boot 4 the web starter is published as spring-boot-starter-webmvc. Projects on Spring Boot 3 use spring-boot-starter-web for the same purpose. Other common starters cover data access with JPA, security, testing, and Actuator. A starter contains almost no code of its own. Its value is the list of libraries it brings in, and the fact that adding those libraries also switches on the matching auto-configuration.

Dependency management and opinionated defaults

Boot publishes a bill of materials (BOM), which is a file that fixes one tested version for each of several hundred common libraries. When your project inherits from spring-boot-starter-parent, or imports the spring-boot-dependencies BOM, you can leave out the version on every library that Boot manages.

This solves a real problem. A plain Spring project that uses Spring, Hibernate, Jackson, a connection pool, and a logging library must choose five versions that are compatible with each other. A wrong combination often fails at runtime with a NoSuchMethodError, which is one of the slowest errors to trace. With the BOM, upgrading Boot upgrades the whole set to versions the Spring team has tested together.

The defaults go further than versions. Boot picks Tomcat as the web server, HikariCP as the connection pool, Logback for logging, and Jackson for JSON. It also sets reasonable values for settings such as the server port (8080) and the error page. These choices are called opinionated defaults, and each one can be changed by a property or by swapping a dependency.

Grokking Spring Boot: Build and Understand Spring Services builds one service step by step and shows what each of these defaults does as it is switched on.

The embedded server and the executable jar

A traditional Spring web application is packaged as a WAR file (web application archive) and copied into a separately installed server such as Tomcat. Someone has to install that server, keep its version in line with the application, and configure it on every machine.

Boot reverses that arrangement. The server becomes a library inside the application, called an embedded server. The build produces one executable jar file that contains your code, every dependency, and the server, and you start it with a single command:

./mvnw package java -jar target/shop-0.0.1.jar

Without Boot, the same deployment needs an installed Tomcat, a web.xml file or a WebApplicationInitializer class to start Spring inside it, and a deploy step that copies the WAR into the server's folder.

The executable jar fits containers well. A Docker image needs only a Java runtime and the jar, and every environment runs the same server version because the server ships with the code. Spring Boot 4 supports Tomcat and Jetty as embedded servers, because Undertow does not yet implement the Servlet 6.1 specification that Boot 4 requires. Boot can still build a WAR if you must deploy into an existing server.

Externalized configuration and profiles

Externalized configuration means the same jar can run in development, staging, and production with different settings supplied from outside the code. Boot reads settings from many sources in a fixed order, so a value from a higher source replaces the same value from a lower one.

A simplified version of that order, from highest to lowest, is:

  1. Command-line arguments, such as --server.port=9090.
  2. Environment variables, such as SERVER_PORT=9090.
  3. Profile-specific files, such as application-prod.properties.
  4. The main application.properties or application.yml file.

A profile is a named set of settings that you switch on for one environment. Starting the application with spring.profiles.active=prod loads application-prod.properties on top of the main file. Boot also maps environment variable names to property names, so SERVER_PORT sets server.port without extra code.

Plain Spring has the building blocks for this, including @PropertySource, the Environment interface, and profiles. What it lacks is the agreed file names, the fixed order, and the binding of settings to typed objects. Boot adds that binding through @ConfigurationProperties:

@ConfigurationProperties(prefix = "shop.payments") public record PaymentSettings(String baseUrl, Duration timeout, int maxRetries) {}
shop.payments.base-url=https://payments.internal shop.payments.timeout=2s shop.payments.max-retries=3

Boot converts 2s into a Duration and checks the types at startup. A missing or wrong value then fails at startup rather than in the middle of a request, which is the better place to learn about it.

Actuator

Actuator is a Boot module that adds operational endpoints to your application. These are HTTP endpoints that report health, metrics, configuration, and other runtime facts, which monitoring systems and load balancers can call.

Adding the spring-boot-starter-actuator dependency is the whole setup for the basic case. By default only the health endpoint is exposed over HTTP, at /actuator/health. You expose more with one property:

management.endpoints.web.exposure.include=health,info,metrics

The health endpoint checks more than "the process is running". It also checks the parts the application depends on, such as the database and disk space, and reports DOWN if any of them fails. A load balancer can use that result to stop sending traffic to an instance whose database connection has broken.

Metrics come from Micrometer, a library that records timers and counters and sends them to systems such as Prometheus. Boot records request timings and connection pool usage without any code from you. Without Boot, each of these endpoints and meters is something a team writes, wires, and maintains on its own.

Some Actuator endpoints reveal sensitive details, such as environment values and the list of beans. Expose only what your monitoring needs, and put the management endpoints behind authentication or on a separate port in production.

DevTools

DevTools is a development-only module that restarts the application automatically when you change code. It loads your own classes in one class loader and the libraries in another. On a change, it throws away only the class loader with your code, so a restart is usually much faster than a full cold start.

DevTools also turns off template caching during development, so a changed page shows on the next refresh. It switches itself off when the application runs as a fully packaged jar, so it cannot change production behavior by accident. Plain Spring has no equivalent, and teams without Boot usually rely on a full restart or a commercial reloading tool.

Test slices

A test slice is a test annotation that starts only the part of the application one layer needs. Starting the full application for every test is slow, while starting nothing means you are not testing Spring's behavior at all. Slices sit between those two.

  • @WebMvcTest starts only the web layer: controllers, JSON conversion, and validation. It does not create services or repositories, so you replace those with mocks.
  • @DataJpaTest starts only the JPA layer with an in-memory database, and rolls back each test's changes when the test ends.
  • @JsonTest starts only JSON conversion, to check how an object is serialized.
  • @SpringBootTest starts the full application when you do need everything.
@WebMvcTest(OrderController.class) class OrderControllerTest { @Autowired MockMvc mvc; @MockitoBean OrderService orders; @Test void returnsOrder() throws Exception { when(orders.find(42L)).thenReturn(new Order(42L, "PAID")); mvc.perform(get("/orders/42")) .andExpect(status().isOk()) .andExpect(jsonPath("$.status").value("PAID")); } }

@MockitoBean comes from the Spring Framework and replaces a bean with a Mockito mock. It takes the place of Boot's older @MockBean, which current versions of Boot no longer provide. Without Boot, you can build the same kind of test with the Spring TestContext framework, but you must list by hand which configuration classes each test loads.

The same REST service, set up both ways

To see the full difference, take one small service: a single endpoint, GET /orders/{id}, that reads an order from PostgreSQL and returns it as JSON. The controller, the service, and the repository are identical in both versions. Only the setup around them changes.

The same orders service set up with plain Spring and with Spring Boot: the build file, the configuration classes, the startup class, and how each one is deployed

With plain Spring, the setup has five parts:

  1. A build file that lists Spring MVC, Spring JDBC or JPA, Jackson, HikariCP, the PostgreSQL driver, and a logging library, each with a version you chose.
  2. A WebConfig class marked @EnableWebMvc and @ComponentScan that switches on Spring MVC and finds your controllers.
  3. A DataConfig class like the one shown earlier, which declares the data source and the transaction manager.
  4. An initializer class that extends AbstractAnnotationConfigDispatcherServletInitializer, so that Tomcat starts Spring when it loads the WAR.
  5. An installed Tomcat 11 server on each machine, and a deploy step that copies the WAR into it.

With Spring Boot, the setup has three parts:

  1. A build file that inherits from spring-boot-starter-parent and lists two starters, the web starter and the JPA starter, plus the PostgreSQL driver, with no versions.
  2. One class with a main method and the @SpringBootApplication annotation.
  3. An application.properties file with the three database settings.
@SpringBootApplication public class ShopApplication { public static void main(String[] args) { SpringApplication.run(ShopApplication.class, args); } }

@SpringBootApplication combines three annotations: @Configuration, @ComponentScan, and @EnableAutoConfiguration. The last of these is what turns on everything described in this article.

In an illustrative project of this size, the plain Spring version carries roughly 80 to 100 lines of configuration code and build entries, while the Boot version carries about 20. The exact numbers vary with each project, but the gap grows as you add security, metrics, and messaging, because each of those brings another configuration class in plain Spring and usually only a starter in Boot.

How to override auto-configuration

Boot's defaults are a starting point, and there are four ways to change them, from the lightest to the heaviest.

  • Set a property. Most defaults have a matching setting. server.port=9090 changes the port, and spring.datasource.hikari.maximum-pool-size=20 changes the pool size. This is the right choice for most changes.
  • Declare your own bean. Because of @ConditionalOnMissingBean, a bean you declare replaces Boot's default of the same type. Use this when a property is not enough, such as a data source that needs custom setup.
  • Customize instead of replace. Many parts of Boot accept customizer beans, such as WebServerFactoryCustomizer, that adjust the default object rather than replacing it. You keep every default and change only the part you need.
  • Exclude an auto-configuration. @SpringBootApplication(exclude = DataSourceAutoConfiguration.class), or the spring.autoconfigure.exclude property, switches off one auto-configuration completely.

Exclusion is the heaviest option, and teams often use it first when Boot does something unexpected. Read the condition evaluation report first, because the cause is usually a library on the classpath that the team did not know was there.

What Spring Boot does not change

Boot changes how an application is set up, not how it is written. The container, the bean lifecycle, dependency injection, @Transactional, Spring MVC, and Spring Security all work exactly as they do in plain Spring, because they are the same libraries.

This has three practical consequences:

  • Knowledge transfers in both directions. An engineer who understands the Spring container can read any Boot application. An engineer who knows only Boot's defaults struggles when a default does not fit, because the fix is usually plain Spring configuration.
  • Runtime behavior is the same. A request handled by a Boot application goes through the same DispatcherServlet and the same beans as in plain Spring. Boot adds a little startup work to evaluate conditions, and nothing on each request.
  • Boot is not a different framework to choose against Spring. The real choice is whether you want Boot's setup layer on top of Spring, not which of the two frameworks to use.

When plain Spring still makes sense

For a new service, Spring Boot is the default choice, and the Spring team's own guides start with Boot. There are still a few cases where plain Spring, without Boot, is the better fit.

  • You are writing a library. A library that other teams include should depend on the Spring modules it needs, not on Boot, so it does not force Boot's versions and defaults onto every application that uses it. If you want Boot users to get automatic setup, publish a separate auto-configuration module.
  • You deploy into a shared application server. Some organizations run many applications inside one managed server. Boot can build a WAR for this, but a plain Spring WAR is sometimes simpler to fit into an existing process.
  • You maintain a large existing application. A mature plain Spring codebase with years of XML or Java configuration may not gain enough from a migration to justify the risk. Moving to Boot is worth planning, but it is not required for the code to keep working.
  • You need full control of every bean. A small number of systems, such as low-level infrastructure, prefer to declare every bean explicitly so that nothing is created by a condition. This is rare, and it is usually better handled by excluding specific auto-configurations.

Spring vs Spring Boot compared

ConcernPlain SpringSpring Boot
Programming modelContainer, DI, MVCSame, unchanged
Bean setupYou declare each beanAuto-configured, then overridden
Library versionsYou choose each oneOne tested BOM
DependenciesListed one by oneStarters
Web serverInstalled separately, WAREmbedded, executable jar
ConfigurationBuilding blocks onlyFixed order, profiles, typed binding
Health and metricsWritten by the teamActuator and Micrometer
Fast restartsNot includedDevTools
Layer testsManual context setupTest slices
Best fitLibraries, legacy appsNew services

The table shows the pattern of this whole article. In each row the left column provides the mechanism, and the right column adds an agreed default on top of the same mechanism.

Frequently asked questions

What is the main difference between Spring and Spring Boot? Spring is the framework that creates and connects your objects through dependency injection. Spring Boot is built on Spring and adds automatic configuration, starters, an embedded server, and production tools. Your application code looks the same in both.

Is Spring Boot a replacement for Spring? No. Every Spring Boot application runs on the Spring Framework. Boot removes repeated setup work and chooses defaults, but the container, Spring MVC, and transactions are the same Spring libraries.

What does @SpringBootApplication do? It combines @Configuration, @ComponentScan, and @EnableAutoConfiguration. The class is a source of bean definitions, Spring scans its package for components, and Boot's auto-configuration is switched on.

How does Spring Boot auto-configuration work? Boot ships ordinary configuration classes whose beans are guarded by conditions, such as @ConditionalOnClass and @ConditionalOnMissingBean. A bean is created only when the library is present, the settings allow it, and you have not declared your own.

Can I use Spring without Spring Boot? Yes. You declare your beans and library versions yourself and deploy the application to a server such as Tomcat. This is common for libraries and for older applications.

Does Spring Boot make an application slower? Not when the application is handling requests, because requests pass through the same Spring code. Boot does a small amount of extra work at startup to evaluate its conditions.

How do I see which auto-configurations are active? Start the application with the --debug flag to print the condition evaluation report. With Actuator, the /actuator/conditions endpoint returns the same report, so expose it only where it is safe.

Want to understand the Spring code behind every Boot default? Grokking Spring Boot: Build and Understand Spring Services builds a complete service from the container up, so you can change any default with confidence.

Coding Interview

What our users say

Eric

I've completed my first pass of "grokking the System Design Interview" and I can say this was an excellent use of money and time. I've grown as a developer and now know the secrets of how to build these really giant internet systems.

Tonya Sims

DesignGurus.io "Grokking the Coding Interview". One of the best resources I’ve found for learning the major patterns behind solving coding problems.

Roger Cruz

The world gets better inch by inch when you help someone else. If you haven't tried Grokking The Coding Interview, check it out, it's a great resource!

More From Designgurus
Annual Subscription
Get instant access to all current and upcoming courses for one year.

Access to 50+ courses

New content added monthly

Certificate of completion

$31.08

/month

Billed Annually

Recommended Course
Grokking the Coding Interview: Patterns for Coding Questions

Grokking the Coding Interview: Patterns for Coding Questions

198,171+ students

4.6

The 24 essential patterns behind every coding interview question. Available in Java, Python, JavaScript, C++, C#, and Go. The most comprehensive coding interview course with 543 lessons. A smarter alternative to grinding LeetCode.

View Course
Join our Newsletter

Get the latest system design articles and interview tips delivered to your inbox.

Read More

Low Level Design (LLD) Interview: What It Is and How to Answer One

Arslan Ahmad

Arslan Ahmad

Microsoft Interview Guide: Process, Questions, and How to Prepare

Arslan Ahmad

Arslan Ahmad

Netflix Interview Guide: Process, Questions, and How to Prepare

Arslan Ahmad

Arslan Ahmad

Mastering the UMPIRE Interview Strategy in Coding: A Step-by-Step Guide

Arslan Ahmad

Arslan Ahmad

Design Gurus logo
One-Stop Portal For Tech Interviews.
About Us
Our Team
Careers
Partners
Contact Us
Become Affiliate
Copyright © 2026 Design Gurus, LLC. All rights reserved.