Skip to content

Getting Started with Spring Boot

jfarcand edited this page Feb 21, 2026 · 5 revisions

Getting Started with Spring Boot 4.0

Build a real-time chat application with Atmosphere and Spring Boot 4.0 in minutes.

Prerequisites

  • JDK 21+
  • Maven 3.9+

1. Create the Project

Start from spring-boot-starter-parent 4.0.2 and add the Atmosphere starter:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>4.0.2</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.atmosphere</groupId>
        <artifactId>atmosphere-spring-boot-starter</artifactId>
        <version>4.0.1</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>jakarta.inject</groupId>
        <artifactId>jakarta.inject-api</artifactId>
        <version>2.0.1</version>
    </dependency>

    <!-- For JSON encoding/decoding -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
</dependencies>

2. Configure

In application.yml:

atmosphere:
  packages: com.example.chat

That's it — the starter auto-configures the AtmosphereServlet mapped to /atmosphere/*.

Available Properties

Property Default Description
atmosphere.servlet-path /atmosphere/* Servlet URL mapping
atmosphere.packages (none) Packages to scan for annotations
atmosphere.session-support false Enable HttpSession support
atmosphere.websocket-support (auto) Force WebSocket on/off
atmosphere.heartbeat-interval-in-seconds (default) Heartbeat frequency
atmosphere.broadcaster-class (default) Custom Broadcaster implementation
atmosphere.broadcaster-cache-class (default) Custom BroadcasterCache
atmosphere.init-params {} Additional servlet init parameters

3. Create the Chat Service

@ManagedService(path = "/atmosphere/chat",
    atmosphereConfig = "org.atmosphere.cpr.maxInactiveActivity=120000")
public class Chat {

    @Inject
    private AtmosphereResource r;

    @Inject
    private AtmosphereResourceEvent event;

    @Ready
    public void onReady() {
        // Connection is ready to receive messages
    }

    @Disconnect
    public void onDisconnect() {
        if (event.isClosedByClient()) {
            // Client closed the connection
        }
    }

    @Message(encoders = {JacksonEncoder.class}, decoders = {JacksonDecoder.class})
    public Message onMessage(Message message) {
        return message; // Broadcast to all connected clients
    }
}

See Understanding @ManagedService for the full annotation reference.

4. Create the Message Class

public class Message {
    private String message;
    private String author;
    private long time;

    public Message() { this("", ""); }

    public Message(String author, String message) {
        this.author = author;
        this.message = message;
        this.time = System.currentTimeMillis();
    }

    // Getters and setters
}

5. Create Encoder and Decoder

public class JacksonEncoder implements Encoder<Message, String> {
    @Inject private ObjectMapper mapper;

    @Override
    public String encode(Message m) {
        try { return mapper.writeValueAsString(m); }
        catch (IOException e) { throw new RuntimeException(e); }
    }
}

public class JacksonDecoder implements Decoder<String, Message> {
    @Inject private ObjectMapper mapper;

    @Override
    public Message decode(String s) {
        try { return mapper.readValue(s, Message.class); }
        catch (IOException e) { throw new RuntimeException(e); }
    }
}

6. Create the Application

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

7. Add the Client

Place atmosphere.js in src/main/resources/static/javascript/. You can grab the latest build from atmosphere.js releases or install via npm:

npm install atmosphere.js@5
cp node_modules/atmosphere.js/dist/atmosphere.js src/main/resources/static/javascript/

Create src/main/resources/static/index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Atmosphere 4.0 Chat</title>
    <script src="javascript/atmosphere.js"></script>
    <style>
        body { font-family: sans-serif; max-width: 600px; margin: 40px auto; }
        #messages { height: 300px; overflow-y: auto; border: 1px solid #ccc; padding: 10px; margin-bottom: 10px; }
        #messages p { margin: 4px 0; }
        #input { width: 100%; padding: 8px; font-size: 14px; }
    </style>
</head>
<body>
    <h2>Atmosphere 4.0 Chat</h2>
    <div id="messages"></div>
    <input id="input" placeholder="Enter your name, then start chatting..." disabled />
    <script src="javascript/app.js"></script>
</body>
</html>

Create src/main/resources/static/javascript/app.js:

document.addEventListener('DOMContentLoaded', async () => {
    const messages = document.getElementById('messages');
    const input = document.getElementById('input');
    let author = null;

    function addMessage(text) {
        const p = document.createElement('p');
        p.textContent = text;
        messages.appendChild(p);
        messages.scrollTop = messages.scrollHeight;
    }

    const subscription = await atmosphere.atmosphere.subscribe(
        {
            url: `${location.protocol}//${location.host}/atmosphere/chat`,
            transport: 'websocket',
            fallbackTransport: 'long-polling',
            trackMessageLength: true,
            contentType: 'application/json'
        },
        {
            open: (response) => {
                addMessage(`Connected via ${response.transport}. Enter your name:`);
                input.disabled = false;
                input.focus();
            },
            message: (response) => {
                const body = response.responseBody.trim();
                if (body.length <= 1) return; // heartbeat
                try {
                    const msg = JSON.parse(body);
                    addMessage(`${msg.author}: ${msg.message}`);
                } catch (e) { /* ignore */ }
            },
            close: () => addMessage('Disconnected.'),
            error: () => addMessage('Connection error.')
        }
    );

    input.addEventListener('keydown', (e) => {
        if (e.key !== 'Enter' || !input.value.trim()) return;
        if (!author) author = input.value.trim();
        subscription.push(JSON.stringify({
            author: author,
            message: input.value.trim()
        }));
        input.value = '';
    });
});

8. Run

mvn spring-boot:run

Open http://localhost:8080 and start chatting!

Adding Rooms

The starter automatically scans @RoomService annotations and exposes a RoomManager bean — no manual configuration needed:

@RoomService(path = "/chat/{roomId}", maxHistory = 50)
public class ChatRoom {

    @Message(encoders = {JacksonEncoder.class}, decoders = {JacksonDecoder.class})
    public Message onMessage(Message message) {
        return message;
    }
}

You can also inject RoomManager directly into any Spring component:

@RestController
public class RoomController {

    private final RoomManager roomManager;

    public RoomController(RoomManager roomManager) {
        this.roomManager = roomManager;
    }

    @GetMapping("/api/rooms")
    public List<Map<String, Object>> listRooms() {
        return roomManager.rooms().stream()
            .map(room -> Map.of("name", room.name(), "members", room.members().size()))
            .toList();
    }
}

See Understanding @RoomService for the full annotation reference and Understanding Rooms for the programmatic API.

Actuator Integration

The starter includes optional health and metrics support:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
management:
  endpoints:
    web:
      exposure:
        include: health,metrics
  endpoint:
    health:
      show-details: always

This exposes an Atmosphere health indicator at /actuator/health and Micrometer metrics at /actuator/metrics:

Metric Description
atmosphere.connections.active Currently connected clients
atmosphere.connections.total Total connections since startup
atmosphere.messages.broadcast Messages broadcast
atmosphere.messages.delivered Messages delivered to clients
atmosphere.broadcasters.active Active Broadcasters
atmosphere.rooms.active Active Rooms
atmosphere.rooms.members Room membership count
atmosphere.cache.size BroadcasterCache entries
atmosphere.cache.hits / misses / evictions Cache performance
atmosphere.backpressure.drops / disconnects Backpressure events

Grafana Dashboard

A ready-to-import Grafana dashboard is available at samples/shared-resources/grafana/atmosphere-dashboard.json. It includes panels for connections, message rates, broadcast latency (p50/p95/p99), room members, cache performance, and backpressure.

Spring Beans Integration

The starter auto-exposes the following Spring beans:

Bean Description
AtmosphereFramework The core framework instance
RoomManager Singleton room manager (auto-created)

You can inject either into your Spring components:

@Service
public class NotificationService {
    private final AtmosphereFramework framework;
    private final RoomManager roomManager;

    public NotificationService(AtmosphereFramework framework, RoomManager roomManager) {
        this.framework = framework;
        this.roomManager = roomManager;
    }
}

AI / LLM Streaming

The Atmosphere AI module adds real-time LLM token streaming over WebSocket. Add the dependency:

<dependency>
    <groupId>org.atmosphere</groupId>
    <artifactId>atmosphere-ai</artifactId>
    <version>4.0.1</version>
</dependency>

Configure via environment variables or application.yml:

llm:
  mode: ${LLM_MODE:remote}
  model: ${LLM_MODEL:gemini-2.5-flash}
  api-key: ${LLM_API_KEY:${GEMINI_API_KEY:}}

See AI / LLM Streaming for the full guide, including Spring AI and LangChain4j adapter integration.

Clone this wiki locally