
If you’re building Java applications in 2025, you’re choosing between Spring Boot and the Spring Framework. While they’re closely related, they serve different purposes—and understanding these differences can save you significant time and complexity. In this comprehensive guide, we’ll explore both frameworks through practical examples, configuration comparisons, and real-world scenarios to help you make the right choice for your project.
What Is Spring Framework?
The Spring Framework is a comprehensive, modular platform for developing Java applications. Introduced in 2003 by Rod Johnson as an alternative to heavyweight Java EE, it revolutionized enterprise Java development by providing:
- Dependency Injection (DI) – Inversion of Control container for loose coupling
- Aspect-Oriented Programming (AOP) – Cross-cutting concerns like logging and security
- Transaction management – Declarative transaction handling
- MVC architecture – Web application development
- Data access – Integration with JDBC, JPA, Hibernate
Spring Framework Configuration Example
Here’s what a typical Spring Framework application looks like with XML configuration:
<!-- applicationContext.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="com.example"/>
<!-- DataSource configuration -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName" value="org.postgresql.Driver"/>
<property name="url" value="jdbc:postgresql://localhost:5432/mydb"/>
<property name="username" value="user"/>
<property name="password" value="password"/>
</bean>
<!-- EntityManagerFactory -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="com.example.entity"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true"/>
<property name="generateDdl" value="true"/>
</bean>
</property>
</bean>
<!-- Transaction Manager -->
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven/>
</beans>
And the Java configuration equivalent:
@Configuration
@EnableTransactionManagement
@ComponentScan("com.example")
public class AppConfig {
@Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("org.postgresql.Driver");
dataSource.setUrl("jdbc:postgresql://localhost:5432/mydb");
dataSource.setUsername("user");
dataSource.setPassword("password");
return dataSource;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan("com.example.entity");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setShowSql(true);
vendorAdapter.setGenerateDdl(true);
em.setJpaVendorAdapter(vendorAdapter);
return em;
}
@Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
}
What Is Spring Boot?
Spring Boot is an opinionated layer on top of Spring Framework that simplifies configuration and deployment. It provides:
- Auto-configuration – Automatic setup based on classpath dependencies
- Embedded servers – Tomcat, Jetty, or Netty included
- Starter dependencies – Curated dependency sets for common use cases
- Production-ready features – Health checks, metrics, externalized configuration
- Native image support – GraalVM compilation for fast startup
Spring Boot Equivalent
The same application in Spring Boot:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
# application.yml
spring:
datasource:
url: jdbc:postgresql://localhost:5432/mydb
username: user
password: password
jpa:
show-sql: true
hibernate:
ddl-auto: update
That’s it. Spring Boot auto-configures the DataSource, EntityManagerFactory, and TransactionManager based on the classpath dependencies and properties.
Detailed Comparison
Project Setup
Spring Framework:
<!-- pom.xml - Manual dependency management -->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.1.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.1.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>6.1.4</version>
</dependency>
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId>
<version>6.4.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.1</version>
</dependency>
<!-- Many more dependencies with version management -->
</dependencies>
Spring Boot:
<!-- pom.xml - Starter dependencies -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.3</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-data-jpa</artifactId>
</dependency>
</dependencies>
Web Application Configuration
Spring Framework (web.xml required):
<!-- web.xml -->
<web-app>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/dispatcher-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
</web-app>
Spring Boot (no web.xml needed):
// Just add spring-boot-starter-web dependency
// DispatcherServlet is auto-configured
REST API Controller
The controller code is identical for both:
@RestController
@RequestMapping("/api/products")
public class ProductController {
private final ProductService productService;
public ProductController(ProductService productService) {
this.productService = productService;
}
@GetMapping
public List<Product> getAllProducts() {
return productService.findAll();
}
@GetMapping("/{id}")
public ResponseEntity<Product> getProduct(@PathVariable Long id) {
return productService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@PostMapping
public ResponseEntity<Product> createProduct(@Valid @RequestBody ProductDTO dto) {
Product product = productService.create(dto);
URI location = URI.create("/api/products/" + product.getId());
return ResponseEntity.created(location).body(product);
}
@PutMapping("/{id}")
public ResponseEntity<Product> updateProduct(
@PathVariable Long id,
@Valid @RequestBody ProductDTO dto) {
return productService.update(id, dto)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteProduct(@PathVariable Long id) {
productService.delete(id);
return ResponseEntity.noContent().build();
}
}
Security Configuration
Spring Framework:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults());
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
// Manual configuration required
UserDetails user = User.withUsername("user")
.password(passwordEncoder().encode("password"))
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Spring Boot:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults());
return http.build();
}
// UserDetailsService and PasswordEncoder auto-configured
// if spring.security.user.name and password are set
}
# application.yml - Security auto-configured
spring:
security:
user:
name: admin
password: secret
roles: ADMIN
Production Readiness
Spring Boot includes production-ready features out of the box:
# application.yml
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: when-authorized
health:
db:
enabled: true
redis:
enabled: true
Access these endpoints:
# Health check
curl http://localhost:8080/actuator/health
# Response
{
"status": "UP",
"components": {
"db": { "status": "UP" },
"diskSpace": { "status": "UP" },
"redis": { "status": "UP" }
}
}
# Metrics
curl http://localhost:8080/actuator/metrics/jvm.memory.used
# Prometheus format
curl http://localhost:8080/actuator/prometheus
Native Image Compilation
Spring Boot 3 supports GraalVM native images for faster startup:
# Build native image
./mvnw -Pnative native:compile
# Or with Gradle
./gradlew nativeCompile
# Run native executable
./target/myapp
# Startup time: ~50ms vs ~2s for JVM
<!-- pom.xml native profile -->
<profiles>
<profile>
<id>native</id>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</profile>
</profiles>
Comprehensive Comparison Table
| Feature | Spring Framework | Spring Boot |
|---|---|---|
| Setup Time | Hours to days | Minutes |
| Configuration | XML or Java config | Auto-configuration + properties |
| Web Server | External (Tomcat, JBoss) | Embedded (Tomcat, Jetty, Netty) |
| Deployment | WAR file to server | Standalone JAR |
| Dependency Management | Manual version coordination | Starter BOMs |
| Health Checks | Custom implementation | Actuator built-in |
| Metrics | Manual setup | Micrometer auto-configured |
| Native Images | Complex setup | First-class support |
| Docker Support | Manual Dockerfile | Buildpacks, layered JARs |
| Testing | Manual test context | @SpringBootTest slices |
| Learning Curve | Steep | Gentle |
When to Use Each
Choose Spring Framework When:
- Fine-grained control – You need complete control over every configuration detail
- Legacy integration – Working with existing Java EE systems or application servers
- Complex modularity – Building highly modular applications with custom bootstrapping
- Specialized deployment – Deploying to traditional application servers (WebSphere, WebLogic)
- Learning purposes – Understanding how Spring works under the hood
Choose Spring Boot When:
- New projects – Starting any greenfield application
- Microservices – Building distributed systems
- REST APIs – Creating web services
- Cloud deployment – Targeting Kubernetes, AWS, GCP, Azure
- Rapid development – MVPs, prototypes, or time-sensitive projects
- Container deployment – Docker-first deployment strategy
Common Mistakes to Avoid
1. Using Spring Framework When Boot Would Suffice
// Wrong - manual configuration for simple API
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MappingJackson2HttpMessageConverter());
}
}
// Right - Spring Boot auto-configures Jackson
// Just add spring-boot-starter-web
2. Disabling Auto-Configuration Unnecessarily
// Wrong - disabling useful auto-configuration
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
JpaRepositoriesAutoConfiguration.class
})
public class Application { }
// Right - leverage auto-configuration, customize via properties
spring:
datasource:
url: jdbc:h2:mem:testdb
3. Not Using Profiles Effectively
# application-dev.yml
spring:
datasource:
url: jdbc:h2:mem:devdb
jpa:
show-sql: true
# application-prod.yml
spring:
datasource:
url: jdbc:postgresql://prod-db:5432/app
jpa:
show-sql: false
4. Ignoring Actuator in Production
# Always configure actuator for production
management:
endpoints:
web:
exposure:
include: health,info
endpoint:
health:
probes:
enabled: true # Kubernetes probes
Final Thoughts
Spring Boot and Spring Framework aren’t competitors—they’re complementary. Spring Boot builds on Spring Framework’s foundation while eliminating boilerplate configuration. For most modern applications in 2025—REST APIs, microservices, cloud-native apps—Spring Boot is the clear choice. It provides faster development, easier deployment, and production-ready features out of the box. Reserve Spring Framework for cases requiring complete configuration control or integration with legacy systems.
To get started with Spring Boot, read Getting Started with Spring Boot 3 and Building Reactive APIs with Spring WebFlux. For official documentation, visit the Spring Boot Reference and the Spring Framework Documentation.
1 Comment