Setup & Starters

This content was generated with the assistance of AI and should be verified against the official documentation of each tool and framework before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

Written against Spring AI 2.0.1, Spring Boot 4.1.x and Java 21 (verified 2026-09-29). Spring AI 2.0.x supports Spring Boot 4.0.x and 4.1.x, needs Java 17 or later, and is built on Spring Framework 7, Jackson 3 and JSpecify; the examples here use Java 21 and Maven. This page adds the first capability of the running DocsAssistant service: a project skeleton that starts, finds its models and can be tested. DocsAssistant is a Spring Boot service that answers questions about the Irurueta documentation; it uses the same Ollama llama3.1:8b chat model, nomic-embed-text embeddings and pgvector store as Integrating with Spring AI, so the two sections describe one application. The next page, ChatModel & ChatClient, adds its chat endpoint. For the map of the whole sub-section see the Spring AI overview.

The three moving parts

Spring AI is wired in three layers, and knowing which layer a problem lives in saves a lot of time. A starter is a dependency that brings a provider module and its auto-configuration. The auto-configuration reads spring.ai.* properties and registers beans such as ChatModel, EmbeddingModel and a prototype-scoped ChatClient.Builder. Your code then depends only on the portable interfaces, so changing provider is mostly a matter of changing a dependency and a few properties.

flowchart LR S["Starter
spring-ai-starter-model-ollama"] --> M["Provider module
spring-ai-ollama"] S --> A["Auto-configuration
reads spring.ai.ollama.*"] A --> CM["ChatModel"] A --> EM["EmbeddingModel"] CM --> CB["ChatClient.Builder
prototype bean"] P["spring.ai.model.chat=ollama"] -. "selects the provider" .-> A CB --> YOU["Your services and controllers"] CM --> YOU EM --> YOU
Figure 1. From starter to the beans your code injects

The dotted arrow is the switch that decides which provider’s auto-configuration is active; the flattened properties section below explains it. Source: Spring AI: Getting Started (BOM, repositories, dependency management).

The BOM

The Spring AI Bill of Materials declares one consistent version for every Spring AI module. It is a BOM only: it has no plugin declarations and no reference to Spring Boot, so you keep the Spring Boot parent (or the Spring Boot BOM) for the Boot versions and import the Spring AI BOM beside it. Once it is imported, starters need no <version>.

pom.xml: Boot parent, Spring AI BOM and the DocsAssistant starters
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

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

  <groupId>com.example</groupId>
  <artifactId>docsassistant</artifactId>
  <version>0.0.1-SNAPSHOT</version>

  <properties>
    <java.version>21</java.version>
    <spring-ai.version>2.0.1</spring-ai.version>
  </properties>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-bom</artifactId>
        <version>${spring-ai.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-webmvc</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.ai</groupId>
      <artifactId>spring-ai-starter-model-ollama</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.ai</groupId>
      <artifactId>spring-ai-starter-vector-store-pgvector</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.ai</groupId>
      <artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId>
    </dependency>
    <dependency>
      <groupId>org.postgresql</groupId>
      <artifactId>postgresql</artifactId>
      <scope>runtime</scope>
    </dependency>
  </dependencies>
</project>

This pom.xml was resolved with Maven against Maven Central on 2026-09-29. Gradle builds import the same BOM with a platform dependency, implementation platform("org.springframework.ai:spring-ai-bom:2.0.1"), and then declare the starters without versions. Release versions come from Maven Central and need no extra repository; only snapshots and milestones need the Spring snapshot repositories, and the reference warns that a Maven mirror declared as * hides them. The baseline used throughout this sub-section is the stable 2.0 line (2.0.1); a 2.1 milestone exists but is not used here. Source: Spring AI: Getting Started (BOM, repositories, dependency management).

Two details differ from what older tutorials and the books show. The web starter is now spring-boot-starter-webmvc on Boot 4 (Spring Initializr generates it for the "Spring Web" choice), and the version property is normally named spring-ai.version, which is also what Initializr writes.

Starter naming

Since Spring AI 1.0.0.M7 every starter follows one of a few patterns. The books that target 0.8.x or 1.0.x still use the earlier spring-ai-<provider>-spring-boot-starter form, which no longer exists, so a copied dependency block from an older source is the first thing to check when a resolve fails.

Table 1. The starter families in the 2.0.1 BOM
Pattern Purpose Examples

spring-ai-starter-model-<provider>

A model provider: chat and, where the provider offers them, embeddings, images, audio and moderation

spring-ai-starter-model-ollama, -openai, -anthropic, -google-genai, -mistral-ai, -deepseek, -bedrock-converse

spring-ai-starter-vector-store-<store>

A vector store behind the portable VectorStore interface

spring-ai-starter-vector-store-pgvector, -redis, -qdrant, -milvus, -chroma, -elasticsearch

spring-ai-starter-mcp-<role>

A Model Context Protocol client or server

spring-ai-starter-mcp-client, -client-webflux, -server, -server-webmvc, -server-webflux

spring-ai-starter-model-chat-memory-repository-<store>

Persistent storage for conversational memory

…​-jdbc, -cassandra, -mongodb, -neo4j, -redis

spring-ai-starter-tool-search-advisor

The tool-search advisor for large tool catalogues

spring-ai-starter-tool-search-advisor

The list of names above is taken from the 2.0.1 BOM. Some modules are not starters: the advisor that answers questions from a vector store is spring-ai-vector-store-advisor (the old name spring-ai-advisors-vector-store was renamed in 2.0), and the development-time integrations are spring-ai-spring-boot-docker-compose and spring-ai-spring-boot-testcontainers. The chat-memory starters and the vector-store advisor are covered where they are used: Chat Memory Repositories and RAG advisors.

Several starters were removed in 2.0. There is no spring-ai-starter-model-azure-openai any more: Azure OpenAI is reached through the OpenAI module, which has microsoft-foundry, deployment-name and related properties. The dedicated MiniMax module was removed in favour of the Anthropic module, and the spring-ai-openai-sdk and spring-ai-oci-genai modules were removed too. The full list is on Migrating from 1.x to 2.x.

Provider families

Spring AI groups providers into families by how they are reached. The table lists what the 2.0.1 BOM ships as a starter, and what is reached through another starter. Which capabilities a provider offers (tools, streaming, multimodal input, native JSON) is maintained in the reference’s Chat Models Comparison; check it before choosing a model, because it changes faster than this page.

Table 2. Chat provider families in Spring AI 2.0.1
Family How you get it Notes

Local runtime

spring-ai-starter-model-ollama

The DocsAssistant default. The Ollama module also has embeddings and pulls models on start-up when asked to. See Local Models for other local runtimes

OpenAI and OpenAI-compatible

spring-ai-starter-model-openai, with spring.ai.openai.base-url changed for a compatible endpoint

Chat, embeddings, images, audio transcription, text-to-speech and moderation. Groq, NVIDIA, Perplexity and the Docker Model Runner are reached through this starter by pointing base-url at their OpenAI-compatible API

Anthropic Claude

spring-ai-starter-model-anthropic

Text, PDF and image input. Models named in this sub-section: claude-sonnet-5, claude-opus-5-5, claude-haiku-4-5, claude-fable-5-1

Google

spring-ai-starter-model-google-genai, plus -google-genai-embedding and -google-genai-image

Multimodal input including audio and video

Mistral AI, DeepSeek

spring-ai-starter-model-mistral-ai, spring-ai-starter-model-deepseek

Mistral supports image and audio input

Cloud platforms

spring-ai-starter-model-bedrock-converse (Amazon Bedrock Converse) and spring-ai-starter-model-bedrock

Converse is the API to prefer for chat on Bedrock

Specialised

spring-ai-starter-model-stability-ai (images), spring-ai-starter-model-elevenlabs (speech), spring-ai-starter-model-transformers (local ONNX embeddings), spring-ai-starter-model-postgresml-embedding

Not general chat providers

The hosted model names used elsewhere in the AI section (claude-sonnet-5, claude-opus-5-5, claude-haiku-4-5, claude-fable-5-1, gpt-6-sol) appear in this sub-section only in provider matrices and option examples; the running DocsAssistant example stays on local Ollama so that it runs without an account.

Flattened properties and provider selection

Spring AI 2.0 flattened the property paths: the .options segment that older releases and books put between the provider and the setting is gone. A chat model is configured with spring.ai.openai.chat.model, not spring.ai.openai.chat.options.model. The old spelling still exists in the metadata as a deprecated alias, which is why copied configuration from an older source often keeps working and then shows deprecation warnings.

The second rule is that a provider is chosen per capability with spring.ai.model.<capability>. The keys are spring.ai.model.chat, spring.ai.model.embedding, spring.ai.model.image, spring.ai.model.moderation, spring.ai.model.audio.transcription and spring.ai.model.audio.speech. Their values are the provider names, for example ollama, openai, anthropic, mistral, google-genai, deepseek and bedrock-converse. When the property is absent, every chat auto-configuration on the classpath is active (the condition is matchIfMissing = true), so two starters give two ChatModel beans and an ambiguity error on ChatClient.Builder. Setting the property is how you say which one wins.

src/main/resources/application.yml: DocsAssistant on local Ollama
spring:
  application:
    name: docsassistant
  ai:
    model:
      chat: ollama
      embedding: ollama
    ollama:
      base-url: http://localhost:11434
      init:
        pull-model-strategy: when_missing
      chat:
        model: llama3.1:8b
        temperature: 0.2
      embedding:
        model: nomic-embed-text
    retry:
      max-attempts: 3
      backoff:
        initial-interval: 2s
        multiplier: 2
        max-interval: 30s
    vectorstore:
      pgvector:
        index-type: HNSW
        distance-type: COSINE_DISTANCE
        dimensions: 768
        initialize-schema: true
        table-name: docs_vector_store
    chat:
      memory:
        repository:
          jdbc:
            initialize-schema: always

Every spring.ai.* key above was checked against the configuration metadata inside the 2.0.1 starter jars. pull-model-strategy accepts always, when_missing and never. The retry block configures the shared retry policy (RetryTemplate), which only the RestClient-based provider modules such as Ollama use, and is developed on Production & Deployment. The vectorstore.pgvector values match the vector-rag page, and the chat.memory.repository.jdbc key is used from Chat Memory. Source: Spring AI: Ollama Chat.

Switching DocsAssistant to a hosted model is a profile change, not a code change. The API key comes from an environment variable and is never written into the file.

application-hosted.yml: the same service on OpenAI, embeddings still local
spring:
  ai:
    model:
      chat: openai
      embedding: ollama
    openai:
      api-key: ${OPENAI_API_KEY}
      chat:
        model: gpt-6-sol
        reasoning-effort: medium
application-anthropic.yml: the same service on Anthropic
spring:
  ai:
    model:
      chat: anthropic
    anthropic:
      api-key: ${ANTHROPIC_API_KEY}
      chat:
        model: claude-sonnet-5
        max-tokens: 4096

Both profiles were validated against the metadata in the same way. Keeping keys in environment variables, or in a secret store that Spring Boot can read, matters more than it looks: a key that is committed once stays in the history. See Security & Guardrails for the wider picture.

Development-time services

DocsAssistant needs a PostgreSQL with pgvector and an Ollama. Spring Boot can start both for you during development and tests, and Spring AI contributes the connection details that Boot cannot know about.

Docker Compose

With spring-boot-docker-compose on the classpath, Spring Boot starts the services in a compose.yaml when the application starts and stops them when it stops. Spring AI’s spring-ai-spring-boot-docker-compose adds service connections that match the container image name, among them ollama/ollama, so spring.ai.ollama.base-url is filled in from the mapped port. PostgreSQL’s connection comes from Boot itself; a pgvector/pgvector image is not called postgres, so a label tells Boot to treat it as one.

pom.xml additions: Compose support
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-docker-compose</artifactId>
  <scope>runtime</scope>
  <optional>true</optional>
</dependency>
<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-spring-boot-docker-compose</artifactId>
  <scope>runtime</scope>
  <optional>true</optional>
</dependency>
compose.yaml
services:
  ollama:
    image: 'ollama/ollama:0.34.4'
    ports:
      - '11434'
  pgvector:
    image: 'pgvector/pgvector:pg17'
    environment:
      - 'POSTGRES_DB=docsassistant'
      - 'POSTGRES_USER=docs'
      - 'POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?set it in .env}'
    labels:
      - 'org.springframework.boot.service-connection=postgres'
    ports:
      - '5432'

The password comes from the environment or from a .env file next to compose.yaml (kept out of version control), and Compose refuses to start without it; Spring Boot reads the resolved value from the running container, so nothing is duplicated in application.yml. Real environments inject credentials from a secret store. Spring Initializr generates this same shape of file (with an ollama/ollama image and a pgvector/pgvector image carrying the service-connection=postgres label) when the Docker Compose dependency is selected. See Compose for Local Development for Compose itself. Source: Spring AI: Docker Compose.

Testcontainers

Testcontainers is the alternative when the containers should live and die with the JVM, which is what tests and a "run without Compose" development entry point want. A @ServiceConnection bean turns a running container into the matching connection details; Spring AI’s spring-ai-spring-boot-testcontainers module recognises OllamaContainer, and Spring Boot’s own spring-boot-testcontainers recognises PostgreSQL.

pom.xml additions: Testcontainers (test scope). The Testcontainers 2.x artifact names are the ones the Boot 4 BOM manages
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-testcontainers</artifactId>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-spring-boot-testcontainers</artifactId>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.testcontainers</groupId>
  <artifactId>testcontainers-postgresql</artifactId>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.testcontainers</groupId>
  <artifactId>testcontainers-ollama</artifactId>
  <scope>test</scope>
</dependency>
src/test/java/com/example/docsassistant/DevServicesConfiguration.java
package com.example.docsassistant;

import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Bean;
import org.testcontainers.postgresql.PostgreSQLContainer;
import org.testcontainers.ollama.OllamaContainer;
import org.testcontainers.utility.DockerImageName;

@TestConfiguration(proxyBeanMethods = false)
class DevServicesConfiguration {

    @Bean
    @ServiceConnection
    PostgreSQLContainer pgvector() {
        return new PostgreSQLContainer(
                DockerImageName.parse("pgvector/pgvector:pg17").asCompatibleSubstituteFor("postgres"));
    }

    @Bean
    @ServiceConnection
    OllamaContainer ollama() {
        return new OllamaContainer("ollama/ollama:0.34.4");
    }
}

The test-side launcher wraps DocsAssistantApplication, the @SpringBootApplication main class that Spring Initializr generates (see Spring Initializr):

src/test/java/com/example/docsassistant/TestDocsAssistantApplication.java: run the app with its containers
package com.example.docsassistant;

import org.springframework.boot.SpringApplication;

public class TestDocsAssistantApplication {

    public static void main(String[] args) {
        SpringApplication.from(DocsAssistantApplication::main)
                .with(DevServicesConfiguration.class)
                .run(args);
    }
}

Running TestDocsAssistantApplication starts the application with both containers and the connection details wired, without a compose.yaml. The same DevServicesConfiguration can be imported into @SpringBootTest classes, which is how Testing builds its integration tests. The class-level @Testcontainers style with static @Container fields also works and is shown on the vector-rag testing section. The Ollama container starts empty: the models still have to be pulled, either with spring.ai.ollama.init.pull-model-strategy or with execInContainer("ollama", "pull", …​). For Testcontainers itself see Spring Boot Integration Tests with Testcontainers. Source: Spring AI: Testcontainers.

Spring Initializr

Spring Initializr has an AI group of dependencies that maps one to one to the starters above: for example spring-ai-ollama, spring-ai-openai, spring-ai-anthropic, spring-ai-vectordb-pgvector, spring-ai-chat-memory-repository-jdbc, spring-ai-mcp-client, spring-ai-mcp-server and spring-ai-tool-search-advisor. Its metadata on 2026-09-29 defaulted to Spring Boot 4.1.1 and Java 17, and the generated pom.xml set spring-ai.version to 2.0.1. The command line below produced the DocsAssistant skeleton, with a compose.yaml and both Docker Compose dependencies added because docker-compose was requested.

Generating the DocsAssistant project from the command line
curl https://start.spring.io/starter.zip \
  -d type=maven-project -d language=java \
  -d bootVersion=4.1.1 -d javaVersion=21 \
  -d groupId=com.example -d artifactId=docsassistant \
  -d packageName=com.example.docsassistant \
  -d dependencies=web,spring-ai-ollama,spring-ai-vectordb-pgvector,spring-ai-chat-memory-repository-jdbc,docker-compose \
  -o docsassistant.zip

Note that the generated project also pulled in spring-ai-vector-store-advisor for the pgvector choice. Initializr’s generated application.properties is nearly empty; the YAML above is what you add. Source: Spring AI: Getting Started.

Checking that it works

A start-up runner that asks the model one question is the smallest check that the starter, the property and the connection all agree. It is a profile so that it never runs in production.

SmokeRunner.java: a one-question health check
package com.example.docsassistant;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration
@Profile("smoke")
class SmokeRunner {

    private static final Logger log = LoggerFactory.getLogger(SmokeRunner.class);

    @Bean
    ApplicationRunner smoke(ChatClient.Builder builder) {
        ChatClient chatClient = builder.build();
        return args -> log.info("Model says: {}",
                chatClient.prompt().user("Reply with the single word: ready").call().content());
    }
}

Start it with SPRING_PROFILES_ACTIVE=smoke. If it fails, the usual causes are, in order: two chat starters and no spring.ai.model.chat; a model name that was never pulled; a base-url that does not match the container’s mapped port; and a missing API key variable. This page’s Java classes and both dev-service configurations compile against Spring AI 2.0.1; the container and model behaviour needs Docker and was not run when the page was written. Source: Spring AI: Chat Client API.