00:00

Spring Boot vs Spring Framework

Last updated:

“Should I learn Spring or Spring Boot?” is one of the most common questions from Java developers, and it rests on a small misunderstanding. Spring Boot is not a competitor to the Spring Framework; it is built on top of it. Every Spring Boot application is a Spring application.

This tutorial explains what each one is, shows the same application built both ways so you can see the difference in real code, compares them feature by feature, and explains when each one makes sense.

1. What Is the Spring Framework?

The Spring Framework, first released in 2003, is the foundation of the Spring ecosystem. Its central idea is dependency injection: instead of objects creating the things they need, a container creates and connects them. Around that core, it offers modules for most enterprise needs:

ModuleProvides
Core containerDependency injection and the application context (beans)
AOPCode that runs around many methods, such as logging or security checks
Data accessJDBC support, transaction management, integration with JPA and Hibernate
Spring MVCBuilding web applications and REST APIs
Spring WebFluxReactive, non-blocking web applications
TestingSupport for testing Spring components

The Spring Framework is flexible and powerful, but it is unopinionated: it does not choose a web server, a JSON library or a connection pool for you, and it does not configure them. You decide and wire everything together yourself.

2. What Is Spring Boot?

Spring Boot, first released in 2014, is a project that makes the Spring Framework fast to use. It follows convention over configuration: if you accept sensible defaults, almost no setup is needed. It adds:

FeatureWhat it gives you
Auto-configurationConfigures Spring and its libraries based on what is on your classpath
StartersSingle dependencies that bring tested, compatible sets of libraries
Embedded serverTomcat (or Jetty) built into your application; run it with java -jar
Externalized configurationSettings in application.properties, environment variables or arguments
ActuatorHealth checks, metrics and monitoring endpoints out of the box
Developer toolsAutomatic restarts during development

For a beginner-friendly walkthrough, see the Spring Boot Introduction.

3. How They Fit Together

Think of a Spring Boot application as layers, each building on the one below:

Layers
┌──────────────────────────────────────────────┐
│  Your code: controllers, services, entities  │
├──────────────────────────────────────────────┤
│  Spring Boot: auto-configuration, starters,  │
│  embedded server, Actuator                   │
├──────────────────────────────────────────────┤
│  Spring Framework: dependency injection,     │
│  MVC, transactions, AOP, data access         │
├──────────────────────────────────────────────┤
│  Java (JDK)                                  │
└──────────────────────────────────────────────┘

Your code talks mostly to the Spring Framework, through annotations such as @Service, @RestController and @Transactional. Spring Boot works mostly behind the scenes, setting everything up so those annotations just work.

4. The Same Application, Built Both Ways

The clearest way to see the difference is to build the same small application twice: a REST endpoint that reads products from a PostgreSQL database.

4.1 The shared code

The controller and service are identical in both versions, because they use only Spring Framework features:

ProductController.java
@RestController
@RequestMapping("/api/products")
public class ProductController {

    private final ProductService productService;

    public ProductController(ProductService productService) {
        this.productService = productService;
    }

    @GetMapping
    public List<Product> list() {
        return productService.findAll();
    }
}
ProductService.java
@Service
public class ProductService {

    private final JdbcTemplate jdbcTemplate;

    public ProductService(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Transactional(readOnly = true)
    public List<Product> findAll() {
        return jdbcTemplate.query("SELECT id, name, price FROM products",
                (rs, row) -> new Product(rs.getLong("id"), rs.getString("name"), rs.getBigDecimal("price")));
    }
}

4.2 With the plain Spring Framework

First, you choose every library and a compatible version yourself:

pom.xml (plain Spring)
<packaging>war</packaging>

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>6.2.0</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>6.2.0</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.18.0</version>
    </dependency>
    <dependency>
        <groupId>com.zaxxer</groupId>
        <artifactId>HikariCP</artifactId>
        <version>6.0.0</version>
    </dependency>
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>42.7.4</version>
    </dependency>
    <dependency>
        <groupId>jakarta.servlet</groupId>
        <artifactId>jakarta.servlet-api</artifactId>
        <version>6.0.0</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

Next, a class that registers Spring’s DispatcherServlet with the external server:

WebAppInitializer.java
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] { AppConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { WebConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }
}

Then the web configuration and the application configuration, where you create the infrastructure beans by hand:

WebConfig.java
@Configuration
@EnableWebMvc
@ComponentScan("com.javabytechie.shop.web")
public class WebConfig implements WebMvcConfigurer {
}
AppConfig.java
@Configuration
@EnableTransactionManagement
@ComponentScan("com.javabytechie.shop.service")
@PropertySource("classpath:app.properties")
public class AppConfig {

    @Bean
    public DataSource dataSource(Environment env) {
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setJdbcUrl(env.getProperty("db.url"));
        dataSource.setUsername(env.getProperty("db.username"));
        dataSource.setPassword(env.getProperty("db.password"));
        return dataSource;
    }

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

Finally, you build a WAR file and deploy it to a Tomcat server that you have installed and configured separately.

4.3 With Spring Boot

Two starters replace the whole list of dependencies, and Spring Boot chooses compatible versions:

pom.xml (Spring Boot)
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.5.0</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>

One small class replaces the initializer and both configuration classes:

ShopApplication.java
@SpringBootApplication
public class ShopApplication {

    public static void main(String[] args) {
        SpringApplication.run(ShopApplication.class, args);
    }
}

And the database connection is just three properties:

application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/shop
spring.datasource.username=shop
spring.datasource.password=secret

Run it with ./mvnw spring-boot:run or java -jar. There is no server to install.

4.4 What changed

Plain SpringSpring Boot
Dependencies to choose and version6, each with a version3, no versions
Configuration classes3 (initializer, web, app)1 (the main class)
Infrastructure beans written by handDataSource, JdbcTemplate, transaction managerNone
ServerInstalled and configured separatelyBuilt in
Controller and service codeSameSame

The business code did not change at all. Spring Boot removed only the setup around it.

5. Core Differences Explained

5.1 Configuration

With the plain Spring Framework, you configure infrastructure yourself, usually with Java configuration classes; older projects often use XML. With Spring Boot, auto-configuration creates sensible defaults, and you change only what you need through properties or your own beans.

5.2 Dependency management

With plain Spring, you pick a version for every library and make sure they work together. A wrong combination can cause confusing runtime errors. Spring Boot’s starters and its managed version list give you combinations that are tested together.

5.3 Running and deploying

A classic Spring web application is packaged as a WAR file and deployed to an external server such as Tomcat or WebLogic. A Spring Boot application usually includes its server and runs as one JAR file, which fits containers and cloud platforms naturally:

Terminal
java -jar target/shop-0.0.1-SNAPSHOT.jar

5.4 Auto-configuration

Plain Spring creates only the beans you declare. Spring Boot looks at your classpath, your properties and your existing beans, and creates the missing infrastructure automatically, stepping back whenever you define something yourself. See Spring Boot Auto-Configuration for the details.

5.5 Production readiness

With plain Spring, you add health checks and metrics yourself, for example by integrating Micrometer by hand. Spring Boot Actuator adds health, metrics and info endpoints with one dependency. See the Spring Boot Actuator tutorial.

5.6 Testing

Spring Framework provides the core test support. Spring Boot adds convenient test annotations, such as @SpringBootTest, @WebMvcTest and @DataJpaTest, plus a single spring-boot-starter-test dependency that brings JUnit 5, Mockito and AssertJ.

6. Feature Comparison Table

FeatureSpring FrameworkSpring Boot
Dependency injection and AOPYes (provides them)Yes (uses Spring’s)
Spring MVC, transactions, data accessYes (provides them)Yes (configures them)
Auto-configurationNoYes
Starter dependenciesNoYes
Managed library versionsSpring’s own modules onlyHundreds of libraries
Embedded web serverNo (possible, but manual)Yes, Tomcat by default
Runnable JARNoYes
Externalized configurationBasic (@PropertySource)Rich: profiles, YAML, environment variables, type-safe properties
Health checks and metricsManual setupActuator
Developer toolsNoDevTools
Setup effortHighLow
Fit for microservices and containersPossible, more workExcellent

7. What Stays the Same

Because Spring Boot sits on top of the Spring Framework, everything you learn about Spring applies directly:

  • Dependency injection, beans and the application context.
  • Stereotype annotations: @Component, @Service, @Repository, @Controller.
  • Web annotations: @RestController, @GetMapping, @RequestBody and the rest.
  • Transactions with @Transactional.
  • AOP, events, scheduling, caching and validation.

The Spring Boot Annotations tutorial covers these, and almost all of them come from the Spring Framework itself.

8. When to Use Each

SituationBetter choiceWhy
A new REST API or web applicationSpring BootWorking app in minutes, production features included
Microservices or cloud deploymentSpring BootOne runnable JAR per service; easy to containerize
Maintaining an existing classic Spring appKeep plain Spring, or migrate graduallyRewriting working configuration has little value on its own
Company requires deployment to an existing application serverEither; Spring Boot can also build a WARSpring Boot supports WAR packaging too
Building a library used inside other applicationsSpring Framework modulesA library should not bring its own server or auto-configuration choices
Learning Spring todaySpring BootStart fast, then learn the Spring concepts underneath

For almost all new applications, the answer is Spring Boot. Plain Spring remains relevant for existing systems and for libraries.

9. Moving a Spring Application to Spring Boot

Existing Spring applications can move to Spring Boot step by step:

  1. Upgrade to a recent Spring Framework version that matches the Spring Boot version you plan to use.
  2. Add the Spring Boot parent and starters, and remove the individual dependency versions they now manage.
  3. Add a main class with @SpringBootApplication in the top-level package.
  4. Keep your existing configuration working: Java configuration classes keep working, and XML files can be loaded with @ImportResource.
  5. Remove configuration that Spring Boot now provides, one piece at a time, such as the DataSource or the DispatcherServlet setup, testing after each step.
  6. Move settings into application.properties.
  7. Decide on packaging: switch to a runnable JAR, or keep a WAR by extending SpringBootServletInitializer.
LegacyShopApplication.java
@SpringBootApplication
@ImportResource("classpath:legacy-beans.xml")   // existing XML keeps working during the move
public class LegacyShopApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(LegacyShopApplication.class, args);
    }
}

10. Summary

The Spring Framework is the foundation: dependency injection, web support, transactions and data access. Spring Boot is the layer that assembles and configures that foundation for you, with auto-configuration, starters, an embedded server and production-ready features. The side-by-side example shows the difference clearly: the business code is identical, but Spring Boot removes the dependency list, the configuration classes and the server setup. Learn Spring Boot to be productive quickly, and learn the Spring Framework concepts underneath to understand what it does for you.