# Lombok Builder Pattern vs Constructor in Spring Boot: When to Use Each

> Understand Lombok @Builder vs constructors in Spring Boot. Learn when to use each for cleaner, more readable object creation and avoid constructor overload issues.

- Repository: [Spring/spring-boot](https://github.com/spring-projects/spring-boot)
- Tags: deep-dive
- Published: 2026-02-12

---

**Lombok @Builder generates a fluent API that makes object construction readable with optional parameters and immutability support, while plain constructors require all arguments upfront and become unwieldy with more than three or four parameters.**

When working with the spring-projects/spring-boot repository, you'll encounter both Lombok-generated builders and traditional constructors for object instantiation. Understanding the difference between the Lombok builder pattern vs constructor approaches helps you write cleaner configuration classes, DTOs, and entity objects that align with Spring Boot's conventions.

## How Lombok @Builder Generates Code vs Plain Constructors

### Generated Code Structure

When you annotate a class with `@Builder`, Lombok creates a static inner **Builder** class, a private all-arguments constructor, and a `builder()` factory method. The builder supplies fluent setter methods that return the builder instance, enabling method chaining, and a final `build()` method that invokes the private constructor.

In contrast, a plain constructor is written directly in the class:

```java
// Plain constructor approach
public class ServerConfig {
    private final String host;
    private final int port;
    
    public ServerConfig(String host, int port) {
        this.host = host;
        this.port = port;
    }
}

```

### Required Arguments and Default Values

**Lombok @Builder** treats every field as optional by default. You can construct an object with only the fields you need, and use `@Builder.Default` to specify default values for fields that aren't explicitly set.

**Plain constructors** require you to pass all arguments that don't have default values at the call site. To support optional parameters, you must create overloaded constructors or use the telescoping constructor pattern, which quickly becomes verbose and error-prone.

## Readability and Developer Experience

### Fluent API vs Long Parameter Lists

The builder pattern shines when a class has many properties. Consider the difference between these two approaches:

```java
// Constructor with many parameters - hard to read
ServerConfig config = new ServerConfig("localhost", 8080, true, 30, 100, "admin", "secret");

// Lombok @Builder - self-documenting
ServerConfig config = ServerConfig.builder()
    .host("localhost")
    .port(8080)
    .secure(true)
    .timeout(30)
    .maxConnections(100)
    .username("admin")
    .password("secret")
    .build();

```

### IDE Support and Compile-Time Safety

Lombok's annotation processor generates the builder at compile time, so IDEs with Lombok support can autocomplete the fluent API and detect missing mandatory fields through type checking. The compiler catches missing arguments in plain constructors immediately, but you lose the step-by-step, named-parameter experience that prevents bugs in long parameter lists.

## Immutability and Binary Size Considerations

**Immutability** is straightforward with Lombok @Builder. The generated fields are `private final`, and the builder is the only mechanism that sets values before object creation. With plain constructors, you must manually mark fields as `final` and ensure no setters exist, which requires discipline and boilerplate.

**Binary size** increases slightly with Lombok @Builder because it generates an additional static inner class. However, this is typically negligible compared to the maintenance burden of hand-written builder classes or multiple constructor overloads.

## When to Use Lombok @Builder vs Constructor in Spring Boot

### Prefer @Builder When

- You have **more than three or four fields** in configuration properties, DTOs, or entity objects
- You need **optional parameters** with sensible defaults without creating telescoping constructors
- You want **immutable objects** with minimal boilerplate code
- You're working with **test fixtures** or **sample data** where readable construction improves test clarity

### Prefer Plain Constructor When

- The class has **only one or two required fields** – a simple constructor keeps the API minimal
- You need **explicit control** over validation logic, side effects, or complex initialization that belongs in the constructor
- You're optimizing for **minimal runtime footprint** in constrained environments like GraalVM native images where you want to avoid Lombok's annotation processing
- You're creating **Spring components** with mandatory dependencies where constructor injection is the standard pattern (though this is different from the builder pattern for POJOs)

## Code Examples from Spring Boot

### Hand-Written Builder Implementation

The Spring Boot codebase includes `BuilderPojo` in the configuration processor tests, demonstrating a traditional hand-written builder class that implements the classic pattern without external libraries:

```java
// configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/specific/BuilderPojo.java
public class BuilderPojo {

    private final String host;
    private final int    port;
    private final boolean secure;

    private BuilderPojo(Builder builder) {
        this.host = builder.host;
        this.port = builder.port;
        this.secure = builder.secure;
    }

    public static Builder builder() {
        return new Builder();
    }

    public static final class Builder {
        private String host;
        private int    port;
        private boolean secure = false; // default

        public Builder host(String host) {
            this.host = host;
            return this;
        }

        public Builder port(int port) {
            this.port = port;
            return this;
        }

        public Builder secure(boolean secure) {
            this.secure = secure;
            return this;
        }

        public BuilderPojo build() {
            return new BuilderPojo(this);
        }
    }
}

```

### Lombok @Builder Implementation

While the repository uses Lombok heavily for data objects (such as `SimpleLombokPojo`), adding `@Builder` to such a class instantly generates the same fluent API without hand-written code:

```java
// configuration-metadata/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/SimpleLombokPojo.java
import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class SimpleLombokPojo {
    private final String host;
    private final int    port;
    @Builder.Default private final boolean secure = false;
}

```

Both approaches produce identical immutable objects; Lombok eliminates the boilerplate builder class while maintaining the same compile-time safety and fluent API.

## Summary

- **Lombok @Builder** generates a static inner builder class that provides fluent setters, optional parameters with defaults, and immutable object construction with minimal boilerplate
- **Plain constructors** require all arguments upfront, become unwieldy with many parameters, and need manual telescoping or overloading to support optional fields
- Use **@Builder** when you have many fields, need readability, or want immutability without boilerplate; use **plain constructors** for simple objects with few fields or when you need explicit control over initialization logic
- The Spring Boot codebase demonstrates both approaches in [`BuilderPojo.java`](https://github.com/spring-projects/spring-boot/blob/main/BuilderPojo.java) (hand-written) and [`SimpleLombokPojo.java`](https://github.com/spring-projects/spring-boot/blob/main/SimpleLombokPojo.java) (Lombok-ready), showing that both patterns coexist depending on the use case

## Frequently Asked Questions

### Does Spring Boot require Lombok to use the builder pattern?

No, Spring Boot does not require Lombok. The framework supports both hand-written builders and Lombok-generated builders equally. As shown in [`BuilderPojo.java`](https://github.com/spring-projects/spring-boot/blob/main/BuilderPojo.java) within the Spring Boot test suite, you can implement the builder pattern manually without any external dependencies. Lombok simply reduces the boilerplate code required to achieve the same result.

### Can I make a Lombok @Builder class immutable?

Yes, Lombok @Builder naturally supports immutability. When you mark fields as `private final` and use `@Builder`, Lombok generates a private all-arguments constructor and sets the fields only during the `build()` method invocation. The resulting object is immutable because there are no setters generated and the fields are final. You can also use `@Builder.Default` to provide default values for optional fields while maintaining immutability.

### When should I use a hand-written builder instead of Lombok @Builder?

Use a hand-written builder when you need explicit control over the construction process that Lombok cannot provide, such as complex validation logic between fields, side effects during object creation, or custom naming conventions for the builder methods. The [`BuilderPojo.java`](https://github.com/spring-projects/spring-boot/blob/main/BuilderPojo.java) example in Spring Boot demonstrates this traditional approach. Hand-written builders are also preferable in environments where you want to avoid annotation processing overhead, such as when building GraalVM native images with strict reflection configuration requirements.

### Does using Lombok @Builder affect Spring Boot's dependency injection?

Lombok @Builder does not interfere with Spring Boot's dependency injection for Spring-managed beans. However, the builder pattern is typically used for data transfer objects (DTOs), configuration properties, or value objects rather than for Spring components that require dependency injection. For Spring beans that use constructor injection, you should use the standard `@Autowired` constructor or the `final` field with `@RequiredArgsConstructor` pattern rather than @Builder, as the builder pattern is designed for object creation rather than dependency management.