Your Simple Guide to Spring AI: Build Your First GenAI App with Spring Boot

    Your Simple Guide to Spring AI: Build Your First GenAI App with Spring Boot

    Learn how to integrate Generative AI into your Spring Boot applications using Spring AI. This guide walks Java developers through setup, chat responses, embeddings, and AI memory—all with simple Spring code.

    default profile

    Munaf Badarpura

    October 22, 2025

    9 min read

    Generative AI is everywhere. You've seen it write code, create images, and answer complex questions. As a Java developer, you might be wondering, "How can I get in on this? How can I build these powerful features into my Spring Boot applications?"

    The answer is Spring AI.

    If you've ever felt that integrating AI was a complex mess of Python scripts, new frameworks, and confusing APIs, you're not alone. The Spring team saw this and did what they do best: they simplified it.

    In this guide, we'll walk you through Spring AI, step by step. We'll start from the absolute basics and build up to creating an application that can "remember" information. No complex data science degree required—just you, your Java skills, and the Spring framework you already love.

    Let's get started.

    1. First, What Are the Prerequisites?#

    Before we write any code, let's get our tools ready. You don't need much:

    1. Java 21+: Spring AI requires a modern version of Java.
    2. A Spring Boot Project: A basic Spring Boot 3.2 (or newer) project is all you need. You can create one easily from start.spring.io.
    3. An AI Model API Key: Spring AI connects to AI models. You'll need an API key from a provider.
      • For a quick start: I recommend using OpenAI (requires a paid account, but it's very cheap for testing).
      • For a free, local option: You can run a model like Llama 3 or Mistral locally using Ollama. It's fantastic for development.

    For this guide, our examples will use OpenAI, but I'll show you the config for Ollama too!

    2. What is Spring AI? (The 30,000-Foot View)#

    Think about how Spring Data JPA works. You don't write specific SQL for MySQL, then different SQL for-PostgreSQL. You just use a JpaRepository, and Spring handles the rest.

    Spring AI is the exact same idea, but for AI models.

    It's an abstraction layer. It gives you a set of simple, standard interfaces (like ChatClient and EmbeddingClient) to perform AI tasks. You write your code against these Spring interfaces, and behind the scenes, Spring AI translates your request into the specific format that OpenAI, Google Gemini, Ollama, or any other model understands.

    This means you can switch your AI model provider just by changing a dependency and a line in your properties file, without any changes to your Java code. That's the power of Spring.

    3. Why Should You Use Spring AI?#

    • Portability: As we just covered, you're not locked into one AI vendor. You can start with OpenAI and switch to a cheaper or more powerful model later.
    • Simplicity: It hides all the messy HttpClient calls, JSON parsing, and error handling. You just call a method, like chatClient.call("Tell me a joke").
    • Spring Integration: It fits perfectly into your existing app. You can @Autowired an AI client just like any other bean. It works with configuration properties, dependency injection, and everything else you expect.
    • Solves Common Problems: It provides ready-to-use solutions for common AI patterns, like prompt templates, memory, and retrieving data (RAG).

    4. The Core Setup: Dependencies & Configuration#

    Okay, let's build our project. First, we need to add the correct dependencies to our pom.xml and set up our application.properties.

    pom.xml Dependencies#

    You need two things: the Spring AI "starter" and the starter for the specific model you want to use.

    <dependencies> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-openai-starter</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-bom</artifactId> <version>1.0.0-M1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>

    application.properties Configuration#

    Next, we tell Spring AI how to connect to our model. Open src/main/resources/application.properties.

    For OpenAI:

    You just need to provide your API key. (Never hard-code your key! Use an environment variable.)

    # Your OpenAI API Key (Best practice: set this as an environment variable) spring.ai.openai.api-key=${OPENAI_API_KEY}

    For Ollama (if you're running it locally):

    You need to tell Spring AI where Ollama is running and which model to use.

    # The default URL for Ollama spring.ai.ollama.base-url=http://localhost:11434 # Specify the model you want to use (e.g., llama3) spring.ai.ollama.chat.options.model=llama3

    That's it! Your application is now configured to talk to an AI.

    5. Your First Conversation: The ChatClient#

    The ChatClient is your primary tool. It's how you send a prompt and get a response. Let's create a simple REST controller to test it.

    import org.springframework.ai.chat.client.ChatClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class ChatController { private final ChatClient chatClient; // Spring Boot auto-configures the ChatClient bean for us public ChatController(ChatClient.Builder chatClientBuilder) { // We get a ChatClient.Builder to configure our client // For this simple case, we just build the default one this.chatClient = chatClientBuilder.build(); } @GetMapping("/ai/simple-joke") public String getJoke(@RequestParam(defaultValue = "programmers") String topic) { // This is the simplest way to get a response // .call() sends the prompt and returns the AI's message as a String return chatClient.prompt() .user("Tell me a simple joke about " + topic) .call() .content(); // .content() extracts the String response } }

    If you run your app and go to http://localhost:8080/ai/simple-joke, you'll get a joke back from the AI!

    Getting More: ChatResponse and Metadata#

    Sometimes, you need more than just the text. You might want to know how many "tokens" (pieces of words) your request used, or if the model had any safety warnings. For this, you can get a ChatResponse object instead of just a String.

    import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.model.Generation; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; // ... (imports and class definition) @GetMapping("/ai/full-response") public String getFullResponse() { ChatResponse response = chatClient.prompt() .user("Tell me a simple joke") .call() .response(); // .response() returns the full ChatResponse // Get the actual message content Generation generation = response.getResult(); String content = generation.getOutput().getContent(); // Get the metadata String model = response.getMetadata().getModel(); int usage = response.getMetadata().getUsage().getTotalTokens(); System.out.println("Model used: " + model); System.out.println("Tokens used: " + usage); return content; }

    6. Stop Copy-Pasting! Using PromptTemplate#

    In our first example, we built our prompt string using basic Java concatenation: "Tell me a simple joke about " + topic. This gets messy fast.

    A PromptTemplate is like a fill-in-the-blanks letter. You define the template once, and then just pass in the variables.

    import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.chat.prompt.PromptTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.Map; // ... (imports and class definition) @GetMapping("/ai/template-joke") public String getTemplateJoke(@RequestParam String adjective, @RequestParam String topic) { // 1. Define the template with placeholders {adjective} and {topic} String templateString = "Tell me a {adjective} joke about {topic}."; PromptTemplate promptTemplate = new PromptTemplate(templateString); // 2. Create a map of variables to fill in the placeholders Map<String, Object> variables = Map.of( "adjective", adjective, "topic", topic ); // 3. Create the final Prompt object Prompt prompt = promptTemplate.create(variables); // 4. Call the client with the final prompt return chatClient.call(prompt).getResult().getOutput().getContent(); }

    Now you can call http://localhost:8080/ai/template-joke?adjective=funny&topic=cats and it will work perfectly. This is much cleaner and safer.

    7. What Are Embeddings? (The AI's "Brain")#

    This is where things get really cool.

    Models like ChatGPT don't "read" text. They understand numbers. An embedding is the process of turning text (like "Hello, world") into a list of numbers (a "vector") that represents its meaning.

    Think of it as a numerical fingerprint for an idea.

    • The text "king" will have a numerical fingerprint.
    • The text "queen" will have a very similar fingerprint.
    • The text "banana" will have a very different fingerprint.

    Spring AI gives us an EmbeddingClient to do this easily.

    import org.springframework.ai.embedding.EmbeddingClient; import org.springframework.ai.embedding.EmbeddingResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; // ... (imports and class definition) @Autowired private EmbeddingClient embeddingClient; // Autowire the EmbeddingClient @GetMapping("/ai/embed") public List<Double> embedText(@RequestParam String text) { // This one call turns your text into a list of numbers (a vector) EmbeddingResponse embeddingResponse = this.embeddingClient.embedForResponse(text); List<Double> embedding = embeddingResponse.getResult().getOutput(); System.out.println("Your text was turned into " + embedding.size() + " numbers."); return embedding; }

    If you call http://localhost:8080/ai/embed?text=Hello world, you'll get back a long array of numbers like [-0.0012, 0.0345, ..., -0.0221].

    Why is this useful? It's the key to AI memory.

    8. Giving Your AI a Memory (Using a VectorStore)#

    By default, a ChatClient is stateless. It has no memory. Every call is a brand new conversation.

    If you want your AI to "remember" your documents, past conversations, or product info, you need to use embeddings with a Vector Store (also called a Vector Database).

    Here's the (simplified) idea, known as Retrieval-Augmented Generation (RAG):

    1. Store: You take all your documents (e.g., product manuals), turn them into embeddings (fingerprints), and save them in a Vector Store.
    2. Search: When a user asks a question (e.g., "How do I reset my a-Model?"), you turn their question into an embedding.
    3. Find: You ask the Vector Store: "Find me the documents with fingerprints most similar to this question's fingerprint."
    4. Augment: The store gives you back the relevant document text (e.g., "Chapter 4: Resetting the a-Model...").
    5. Answer: You stuff that text into a PromptTemplate and send it to the ChatClient:
      • "Using this information: [Chapter 4 text...]... please answer this question: [How do I reset my a-Model?]"

    The AI then answers the question using the exact information you provided.

    Spring AI makes this "search" step simple with the VectorStore interface. You'll need to add a dependency for a vector database (e.g., spring-ai-pgvector-starter for Postgres, or spring-ai-chroma-starter), but here is a conceptual example using a simple in-memory store.

    import org.springframework.ai.document.Document; import org.springframework.ai.vectorstore.SimpleVectorStore; import org.springframework.ai.vectorstore.VectorStore; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; import java.util.stream.Collectors; // ... (imports and class definition) // We'll use a simple in-memory store for this example // In a real app, you'd autowire a persistent VectorStore bean private final VectorStore vectorStore; public ChatController(ChatClient.Builder chatClientBuilder, EmbeddingClient embeddingClient) { this.chatClient = chatClientBuilder.build(); // Initialize our in-memory store this.vectorStore = new SimpleVectorStore(embeddingClient); // "Feed" our store some documents (this is the "Store" step) List<Document> documents = List.of( new Document("Spring Boot is a Java framework for microservices."), new Document("The 'spring-boot-starter-web' dependency is for building REST APIs."), new Document("A 'pom.xml' file manages a project's dependencies.") ); this.vectorStore.add(documents); } @GetMapping("/ai/rag-search") public List<String> ragSearch(@RequestParam String query) { // "Search" step: Find the most similar documents to the query List<Document> similarDocs = vectorStore.similaritySearch(query); // Just return the text content for this example return similarDocs.stream() .map(Document::getContent) .collect(Collectors.toList()); }

    If you run this and call http://localhost:8080/ai/rag-search?query=what is maven for?, it will likely return "A 'pom.xml' file manages a project's dependencies." because that document's "fingerprint" is closest to your question's.

    You would then take this result and pass it to your ChatClient to get a final, context-aware answer.

    Conclusion: Your Journey Starts Now#

    We've covered a lot of ground, but you've just seen the core building blocks of any modern AI application.

    • You learned that Spring AI is an abstraction layer, just like Spring Data.
    • You built your first AI-powered app using the ChatClient.
    • You made your code clean and reusable with PromptTemplate.
    • You understood that Embeddings are "numerical fingerprints" for text.
    • You saw how a VectorStore uses those fingerprints to give your AI a "memory."

    The world of Generative AI is moving incredibly fast, but with Spring AI, you don't have to be a data scientist to build powerful, intelligent applications. You're a Spring developer, and now, you're a Spring AI developer, too. Now it's your turn. Go build something amazing.

    Want to Master Spring Boot and Land Your Dream Job?

    Struggling with coding interviews? Learn Data Structures & Algorithms (DSA) with our expert-led course. Build strong problem-solving skills, write optimized code, and crack top tech interviews with ease

    Learn more
    Spring AI
    Java Spring Boot
    Generative AI
    OpenAI Integration
    AI in Java

    Subscribe to our newsletter

    Read articles from Coding Shuttle directly inside your inbox. Subscribe to the newsletter, and don't miss out.

    More articles