DEV Community

Ankit Verma
Ankit Verma

Posted on

What Boot adds over plain Spring

What Boot actually is

If you have written any Spring at all, you have met the container — the engine that reads your classes, builds one object per bean, and wires them together. That engine is powerful. On its own, it is also demanding: it does nothing until you tell it exactly what to build.

Spring Boot does not replace that engine. It sits on top of it. Boot's whole job is to remove the setup work around the container — the long lists of beans you declare, the library versions you juggle, the web server you install — and swap it for sensible defaults you can override whenever you disagree.

That last clause is the entire philosophy, and it has a name: convention over configuration. Boot makes a reasonable guess for almost everything, so an app runs with near-zero setup. When a guess is wrong, you override just that one piece — and nothing else changes.

Let me show what that setup used to cost, because Boot only makes sense once you feel the problem it removes.

The ceremony Boot removes

In plain Spring, standing up even a small web app meant writing a lot of wiring by hand. In your own configuration class you declared, one by one, every foundational bean the framework needed:

@Configuration
public class AppConfig {
    @Bean DataSource dataSource() { /* url, user, pool… */ }
    @Bean LocalContainerEntityManagerFactoryBean emf(DataSource ds) { /* … */ }
    @Bean PlatformTransactionManager txManager(EntityManagerFactory emf) { /* … */ }
    // …and the front controller that routes web requests, and more —
    // all before a single line of your own logic
}
Enter fullscreen mode Exit fullscreen mode

None of that is your application. It is plumbing that every Spring app needs, retyped in every project.

On top of the beans, you picked library versions yourself. Spring MVC, a JSON library, a validation library, a database driver — each chosen and version-matched by hand. A wrong combination broke at runtime in confusing ways.

And the app could not run on its own. It was a WAR — a package you deployed into a separate Tomcat server that you installed and managed. Nothing started from a plain main().

Boot attacks all three of those at once: the bean plumbing, the versions, and the server. Take them one at a time.

Auto-configuration: beans you never declared

The headline feature is auto-configuration: Boot inspects what is on your classpath — the set of libraries your app was built with — and, from that alone, configures the beans you most likely want.

The reasoning is simple: "if you shipped this library, you probably want the standard setup for it." Put a JDBC library and an in-memory H2 database on the classpath with no database URL configured, and Boot quietly creates a DataSource bean pointing at an H2 database. Put the web libraries on it, and Boot configures the whole web layer — the front controller, the JSON conversion, the lot. You declared none of it.

Here is the shape of the trade. Plain Spring made you write this:

@Bean
DispatcherServlet dispatcherServlet() { /* register the front controller by hand */ }
Enter fullscreen mode Exit fullscreen mode

In Boot, you write nothing — the same bean appears because the web starter is on the classpath.

Crucially, every auto-configured bean is a default, not a mandate. If you define your own bean of that type, Boot notices and steps aside:

@Bean
DataSource dataSource() {
    // your custom DataSource — Boot now backs off and uses this one
}
Enter fullscreen mode Exit fullscreen mode

So auto-configuration never fights you. It fills in what you left blank and yields the moment you speak up. (How it detects your bean and backs off is its own topic later in this module; for now, the behaviour is the point.)

Starters: dependencies that already agree

Auto-configuration keys off the classpath, which raises a question: how do the right libraries get onto the classpath, in versions that work together? That is what a starter solves.

A starter is a single dependency whose only job is to pull in a curated bundle of others. Add spring-boot-starter-web and you get Spring MVC, a JSON library, validation, and an embedded web server — all in versions already tested against each other.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Notice there is no <version> on it. Boot manages versions centrally, so you name what you want, never which version. One line replaces the dozen hand-matched dependencies plain Spring demanded — and the classpath it produces is exactly what auto-configuration then reads to wire your app.

The embedded server: your app runs itself

Remember the WAR you deployed into a separate Tomcat. Boot turns that inside out. The web server becomes an ordinary library inside your application — an embedded server — and your app starts it.

That means no external server to install, and a normal main() as your entry point:

@SpringBootApplication
public class StoreApplication {
    public static void main(String[] args) {
        SpringApplication.run(StoreApplication.class, args);
    }
}
Enter fullscreen mode Exit fullscreen mode

That one call builds the container, triggers auto-configuration, and starts the embedded server listening for requests. Run java -jar store.jar and you have a live web app, with no deployment step at all. (The exact sequence inside run is a later topic; here, the shift from "deploy into a server" to "the app is the server" is what matters.)

One annotation doing three jobs

That @SpringBootApplication at the top is not one feature but three, bundled for convenience. It is shorthand for:

  • @Configuration — this class may itself declare beans.
  • @EnableAutoConfiguration — turn on the auto-configuration described above.
  • @ComponentScan — discover your own @Component, @Service, and @Controller classes in this package and below.

So a single annotation switches on Boot's machinery and points the container at your code. You can split it back into the three, but almost nobody does.

Configuration you override, not rewire

Because Boot chose the defaults, you need a way to disagree with them — and it should not mean editing beans. That way is a plain properties file, application.properties (or its YAML twin), that Boot reads on startup.

Want a different port than the default 8080?

```plain text
server.port=9090





One line. No Java, no bean redefined. Boot ships a default; you override it by **property**. This same file later carries your database URL, your logging levels, and your own custom settings — but the principle is set right here: defaults live in the framework, overrides live in your properties.


## Production features without the plumbing


Plain Spring gave you no built-in way to answer "is this app healthy?" in production. Boot adds one through **Actuator** — a starter that, once added, exposes ready-made endpoints for health, metrics, and app info over HTTP.




```plain text
GET /actuator/health   →   {"status":"UP"}
Enter fullscreen mode Exit fullscreen mode

You wrote no controller for that. Add the starter and, true to the pattern, the endpoints auto-configure themselves — with knobs in application.properties to choose which ones are exposed. (The full set of endpoints is its own topic later.)

One runnable jar at the end

Finally, packaging. A Boot build produces a single executable jar — often called a fat jar — that contains your compiled code, every dependency, and the embedded server, all in one file.

mvn package   →   target/store.jar
java -jar store.jar
Enter fullscreen mode Exit fullscreen mode

There is nothing to install on the target machine but a Java runtime. The unit you build, test, and ship is one self-contained file — a direct payoff of the embedded server from earlier.

The model to carry into the rest of the module

Step back, and every feature above is the same idea wearing different clothes: Boot is an opinionated layer on top of the same Spring container, and its opinions are defaults you can always override.

It does not change how beans work — everything from M1 Core still holds underneath. What it removes is the labour around those beans. Auto-configuration declares the standard beans for you, reading them off the classpath that starters populate with version-matched libraries. An embedded server lets a plain main() run the app, @SpringBootApplication switches the whole machine on, application.properties lets you override any default without touching Java, Actuator adds production endpoints for free, and an executable jar ships all of it as one file.

Hold that one sentence — opinionated defaults over the same container — and the rest of this module falls into place. Every remaining topic is just a close-up of one of these conveniences: how auto-configuration decides, how conditions gate it, how starters are built, how properties are resolved, how run boots the server. You have the map now; next we go under each piece.

Top comments (0)