HomeSpring BootSpring vs Spring Boot: Differences, Configuration, Dependencies and When to Use Each
Spring Boot

Spring vs Spring Boot: Differences, Configuration, Dependencies and When to Use Each

Spring Framework and Spring Boot are not competing technologies. Spring Boot is built on top of Spring Framework, adding auto-configuration, starter dependencies, an embedded server and production tooling. This guide compares the two with side-by-side code for configuration, dependency management and deployment, explains what @SpringBootApplication actually does, clarifies Spring Boot vs Spring MVC, and tells you when plain Spring Framework is still the right choice.

The Relationship: Spring Boot Is Built on Spring

Spring Boot is not a replacement for Spring Framework. It is built on top of it and uses all the same underlying mechanisms: the IoC container, dependency injection, AOP, Spring MVC, Spring Data and Spring Security. A Spring Boot application is a Spring application. The difference is in how you configure and start those features. Spring Framework gives you the building blocks and leaves the assembly to you. Spring Boot adds four things on top: auto-configuration that sets up Spring based on what is on your classpath, starter dependencies that bring in compatible sets of libraries, an embedded web server so the application runs as a plain Java program, and production tooling such as Actuator for health checks and metrics. An analogy: Spring Framework is the engine and the parts. Spring Boot is the same engine already assembled into a car with sensible defaults, so you can drive immediately and still open the hood when you need to change something. The versions are linked. Spring Boot 3.x is built on Spring Framework 6 and requires Java 17 or later. Spring Boot 4 is built on Spring Framework 7. Always check the compatibility notes for the Boot version you choose.

What @SpringBootApplication Actually Does

The single annotation on a Boot application class is shorthand for three annotations, and understanding them removes most of the mystery. @SpringBootConfiguration marks the class as a configuration class, the same as @Configuration. @ComponentScan scans the package of this class and everything below it for @Component, @Service, @Repository and @Controller classes, which is why the main class should sit in the root package. @EnableAutoConfiguration is the key one: Spring Boot reads a list of auto-configuration classes shipped inside the libraries you added, and each one registers beans only if its conditions are met, for example "the DataSource class is on the classpath" and "the user has not defined their own DataSource bean". That last condition is why auto-configuration is safe to override. When you define your own bean of the same type, the auto-configured one backs off. You can also exclude an auto-configuration entirely, or start the application with the --debug flag to print a report listing which auto-configurations were applied and why others were skipped.
Java
// What @SpringBootApplication stands for:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

// Override an auto-configured bean: define your own and the default backs off.
@Configuration
public class JacksonConfig {

    @Bean
    public ObjectMapper objectMapper() {
        return new ObjectMapper()
            .registerModule(new JavaTimeModule())
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    }
}

// Exclude an auto-configuration you do not want:
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class NoDatabaseApplication { }

// See what was applied or skipped:
//   java -jar myapp.jar --debug
//   (prints the "CONDITIONS EVALUATION REPORT")

Configuration: Spring Framework with XML (Legacy Style)

The most fundamental difference is configuration. With plain Spring Framework you declare the infrastructure yourself. The traditional approach used XML files, still common in older codebases: a web.xml to register the DispatcherServlet, an application context for beans, and an MVC context for web settings. This is the minimum for a JPA application with a data source and transactions. Note how much of it is infrastructure rather than business logic: the entity manager factory needs a vendor adapter and the package to scan, and annotation-driven transactions need an explicit transaction manager bean. The driver class shown is the current MySQL Connector/J class, com.mysql.cj.jdbc.Driver; the older com.mysql.jdbc.Driver is deprecated.
XML
<!-- 1. web.xml: registers the Spring MVC DispatcherServlet -->
<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

<!-- 2. applicationContext.xml: beans, data source, JPA, transactions -->
<context:component-scan base-package="com.example"/>

<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
    <property name="username" value="app"/>
    <property name="password" value="${DB_PASSWORD}"/>
</bean>

<bean id="entityManagerFactory"
      class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="packagesToScan" value="com.example.model"/>
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
    </property>
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven/>

<!-- 3. dispatcher-servlet.xml: Spring MVC configuration -->
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/"/>
    <property name="suffix" value=".jsp"/>
</bean>

Configuration: Spring Framework with Java Config (No Boot)

It is a common misconception that plain Spring means XML. Modern Spring Framework applications can be configured entirely in Java, and this is the fair comparison with Spring Boot. A Servlet 3.0+ container does not need web.xml at all: an AbstractAnnotationConfigDispatcherServletInitializer registers the DispatcherServlet in code. Even so, you still declare each piece of infrastructure: the data source, the entity manager factory, the transaction manager, and the annotations that switch on MVC, transactions and Spring Data repositories. This is the work Spring Boot's auto-configuration does for you.
Java
@Configuration
@ComponentScan("com.example")
@EnableWebMvc
@EnableTransactionManagement
@EnableJpaRepositories("com.example.repository")
public class AppConfig {

    @Bean
    public DataSource dataSource() {
        HikariDataSource ds = new HikariDataSource();
        ds.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
        ds.setUsername("app");
        ds.setPassword(System.getenv("DB_PASSWORD"));
        return ds;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
        LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
        emf.setDataSource(dataSource);
        emf.setPackagesToScan("com.example.model");
        emf.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        return emf;
    }

    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
        return new JpaTransactionManager(emf);
    }
}

// Replaces web.xml: registers the DispatcherServlet in code
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

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

    @Override
    protected Class<?>[] getServletConfigClasses() { return null; }

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

Configuration: Spring Boot

With Spring Boot, the same application needs one annotated class and a few properties. Boot sees the JPA, MySQL driver and web libraries on the classpath and creates the data source (with a HikariCP connection pool), the entity manager factory, the transaction manager, the DispatcherServlet and the embedded server. "Zero configuration" is a slogan, not a fact. You still must add the MySQL driver dependency, supply the connection URL and credentials, and set things like the schema strategy. What disappears is the boilerplate wiring. Reading the password from an environment variable, as below, keeps secrets out of source control.
Java
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

# application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=app
spring.datasource.password=${DB_PASSWORD}
spring.jpa.hibernate.ddl-auto=validate

Dependency Management: Manual Versions

With plain Spring Framework you choose every library and every version, and you are responsible for keeping them compatible: Spring modules with each other, Hibernate with your Spring version, Jackson with both. A mismatch shows up as confusing runtime errors such as NoSuchMethodError or ClassNotFoundException rather than a clean build failure. You can reduce the pain with the Spring Framework BOM (spring-framework-bom), which aligns the versions of the Spring modules, but you still manage Hibernate, Jackson, the connection pool and everything else yourself. The version numbers below are illustrative; use releases that your Spring version supports.
XML
<!-- Spring Framework: you manage versions yourself -->
<properties>
    <spring.version>6.2.0</spring.version>
    <hibernate.version>6.6.0.Final</hibernate.version>
    <jackson.version>2.18.0</jackson.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>${spring.version}</version>   <!-- must match the other Spring modules -->
    </dependency>
    <dependency>
        <groupId>org.hibernate.orm</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>${hibernate.version}</version>   <!-- must be compatible with this Spring version -->
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>${jackson.version}</version>
    </dependency>
    <!-- ...plus the connection pool, validation API, logging, servlet API, test libraries... -->
</dependencies>

Dependency Management: Spring Boot Starters and Parent POM

Spring Boot ships a curated, tested set of compatible versions. The spring-boot-starter-parent POM (or the spring-boot-dependencies BOM if you already have a different parent) manages the versions of Spring, Hibernate, Jackson, Tomcat, the connection pool and many other libraries, so dependencies in your pom carry no version number. Starters are single dependencies that pull in everything for a feature: spring-boot-starter-web brings Spring MVC, Jackson and embedded Tomcat; spring-boot-starter-data-jpa brings Spring Data JPA, Hibernate and HikariCP. Pick the latest patch release of the Boot line you use, and upgrade the single parent version to move the whole stack.
XML
<!-- Spring Boot: the parent POM manages all versions -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.5.6</version>   <!-- use the latest patch of the Boot line you choose -->
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>          <!-- no version needed -->
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>   <!-- needed for @Valid -->
    </dependency>
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>                <!-- version managed by Boot -->
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>     <!-- optional: health and metrics -->
    </dependency>
</dependencies>

Deployment: Executable JAR vs WAR

The traditional Spring Framework deployment packages the application as a WAR file and deploys it to an external application server such as Tomcat. Someone must install and configure that server, keep its version compatible with your application, and deal with the server and application classpaths interacting. The application usually runs under a context path such as /myapp. (Strictly, plain Spring can also run with an embedded server if you write the code to start one. The point is that Boot makes this the default.) A Spring Boot application is normally packaged as an executable JAR with an embedded server, Tomcat by default and Jetty as an alternative. You start it with java -jar. The server version is just a dependency you control, the same artifact runs on a laptop, in staging and in production, and it fits naturally in Docker and Kubernetes. The jar is typically tens of megabytes. Boot can also build a container image directly with the Maven plugin.
Shell
# Spring Framework (WAR on an external server)
mvn package                          # produces myapp.war
# install and configure Tomcat, then copy myapp.war to tomcat/webapps/
# URL: http://localhost:8080/myapp/

# Spring Boot (executable JAR with embedded server)
mvn package                          # produces myapp.jar
java -jar target/myapp.jar           # URL: http://localhost:8080/
mvn spring-boot:run                  # run directly during development
mvn spring-boot:build-image          # build a container image (Cloud Native Buildpacks)

Deployment: Building a WAR with Spring Boot

Spring Boot can still produce a WAR when you must deploy to an existing application server. Extend SpringBootServletInitializer, set the packaging to war, and mark the embedded Tomcat starter as provided so it is not bundled into the WAR. Spring Boot 3 is built on Jakarta EE, so the target server must support Jakarta EE 9 or later (for example Tomcat 10.1 or newer); older javax-based servers will not work.
Java
@SpringBootApplication
public class Application extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(Application.class);
    }

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

<!-- pom.xml -->
<packaging>war</packaging>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <scope>provided</scope>
</dependency>

Feature Comparison at a Glance

Configuration: Spring Framework uses XML or Java configuration that you write; Spring Boot auto-configures from the classpath and lets you override any default. Dependencies: with Spring Framework you pick and align versions yourself; with Spring Boot, starters and the parent POM provide a tested, compatible set. Server: Spring Framework applications are traditionally deployed to an external server such as Tomcat; Spring Boot embeds Tomcat (or Jetty) in the application. Packaging: WAR is the traditional format for Spring Framework; an executable JAR is the default for Spring Boot, with WAR still possible. Startup code: Spring Framework needs an initializer and configuration classes or XML; Spring Boot needs one class with @SpringBootApplication and a main method. Properties: Spring Framework reads whatever property sources you register; Spring Boot loads application.properties or application.yml automatically, with profile-specific files and environment variable overrides. Testing: both use Spring's test support, but Spring Boot adds ready-made slices such as @SpringBootTest, @WebMvcTest and @DataJpaTest that set up only what a test needs. Monitoring: Spring Framework leaves it to you; Spring Boot provides Actuator for health, metrics and other endpoints, added through the spring-boot-starter-actuator dependency (it is not included automatically). Learning curve: Spring Framework requires you to understand the infrastructure up front; Spring Boot lets you start quickly, though understanding the underlying Spring concepts still pays off. Flexibility: both can be customized fully. Spring Framework starts from nothing and you add everything; Spring Boot starts from opinionated defaults you override where needed. Typical use: Spring Framework fits libraries, legacy systems and very specialized setups. Spring Boot fits new applications, REST APIs, microservices, batch jobs and event-driven systems.

When to Use Spring Framework Directly

Spring Boot is the right default for almost every new application, but plain Spring Framework still has a place. Libraries and frameworks: if you publish a library, depend on the specific Spring modules you need and not on Boot. Applications that use your library can then choose their own setup. (A library that wants to plug into Boot can ship its own auto-configuration and starter.) Legacy applications: an existing XML-configured Spring application keeps working, and many teams migrate it to Boot step by step rather than rewriting it. Special constraints: if you must run inside a specific environment with strict control over every component, or you need a very small footprint with no auto-configuration at all, plain Spring gives you complete control. For everything else (new projects, REST APIs, microservices, batch processing, event-driven applications), start with Spring Boot.

Spring MVC vs Spring Boot

This comparison comes up often, but the two are not alternatives. Spring MVC is a module of Spring Framework: the web framework that provides controllers, request mapping, the DispatcherServlet, message conversion and view resolution. Spring Boot is the setup layer around Spring. When you add spring-boot-starter-web to a Boot project, Boot auto-configures Spring MVC for you. So a typical Spring Boot web application is a Spring MVC application, started and configured by Boot. The alternative to Spring MVC is not Spring Boot but Spring WebFlux, the reactive web framework, which Boot can also configure through spring-boot-starter-webflux.

Same Business Code, Different Bootstrap

The business code is the same in both worlds. A service, repository or controller written for Spring works in Spring Boot with no changes to the classes themselves. What changes is how the application is started, how dependencies are declared, and the absence of XML or infrastructure configuration classes in Boot. Two caveats make "identical" more accurate. In plain Spring, Spring Data repositories and @Transactional only work if you enabled them with @EnableJpaRepositories and @EnableTransactionManagement, as in the Java config example above; Boot enables both automatically. And @Valid only validates when a validation implementation is on the classpath: in Spring Boot, add spring-boot-starter-validation, because it is not included in the web starter. The example returns a response DTO rather than the entity. Exposing JPA entities directly from controllers couples your API to your database model and can trigger lazy-loading problems. The email existence check is a friendly early error; keep a unique constraint on the column as the real guarantee, because two concurrent requests can both pass the check.
Java
// This code is the same in Spring and Spring Boot:
public record CreateUserRequest(@NotBlank @Email String email, @NotBlank String name) { }

public record UserResponse(Long id, String email, String name) {
    static UserResponse from(User user) {
        return new UserResponse(user.getId(), user.getEmail(), user.getName());
    }
}

@Service
@Transactional
public class UserService {

    private final UserRepository repository;

    public UserService(UserRepository repository) {
        this.repository = repository;
    }

    public UserResponse createUser(CreateUserRequest request) {
        if (repository.existsByEmail(request.email())) {
            throw new DuplicateEmailException(request.email());
        }
        // A unique constraint on email is still the real guard against concurrent requests
        return UserResponse.from(repository.save(new User(request.email(), request.name())));
    }
}

@RestController
@RequestMapping("/users")
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public UserResponse createUser(@RequestBody @Valid CreateUserRequest request) {
        return userService.createUser(request);
    }
}

Common Misconceptions

Spring Boot is a different framework. It is not. It is Spring Framework with auto-configuration, starters and an embedded server. Spring Boot hides everything, so you cannot customize it. Every default can be overridden with properties, your own beans or by excluding an auto-configuration. The --debug flag shows exactly what Boot configured. Spring Boot is only for microservices. It is equally suited to monoliths, batch jobs, command-line tools and event-driven systems. Spring Boot applications are always heavy. A Boot application only includes what its starters and dependencies add. Unused auto-configurations stay inactive when their classes are not on the classpath, and you can exclude or slim down further. You can skip learning Spring because Boot does it all. Boot removes configuration work, not the concepts. Dependency injection, bean lifecycle, transactions and proxies behave exactly as in Spring, and you will need to understand them to debug real problems.

Frequently Asked Questions

What is the main difference between Spring and Spring Boot? Spring Framework provides the core programming model (dependency injection, AOP, MVC, data access) and leaves setup to you. Spring Boot adds auto-configuration, starter dependencies, an embedded server and production features so you can build and run an application with minimal setup. Is Spring Boot a replacement for Spring Framework? No. Spring Boot depends on Spring Framework and cannot work without it. Your code still uses Spring annotations and the Spring container. Can I use Spring Framework without Spring Boot? Yes. Many existing applications and libraries do. You configure the infrastructure yourself using XML or Java configuration, and typically deploy a WAR to an application server. Is Spring Boot the same as Spring MVC? No. Spring MVC is the web module of Spring Framework. Spring Boot configures and launches Spring MVC (or Spring WebFlux) for you. Should I learn Spring before Spring Boot? Learn the core concepts as you go: dependency injection, beans, configuration and transactions. You do not need to write XML configuration first, but understanding what Boot configures on your behalf makes debugging far easier. Which Java version does Spring Boot need? Spring Boot 3.x requires Java 17 or later. Check the documentation of the specific Boot version you use. Can Spring Boot deploy to an external Tomcat? Yes. Package as a WAR, extend SpringBootServletInitializer and mark the embedded Tomcat starter as provided. The external server must support Jakarta EE 9 or later for Boot 3. How do I see what Spring Boot auto-configured? Start the application with the --debug flag, or set debug=true in application.properties. Boot prints a conditions evaluation report showing which auto-configurations were applied and which were skipped. If Actuator is enabled, the conditions endpoint shows the same information. Is Spring Boot slower than plain Spring? Not in any way that matters for request handling. Both run the same Spring code at runtime. Startup can differ because of auto-configuration scanning, which you can tune, and Boot also supports ahead-of-time processing and native images for faster startup when you need them.