How to Build Your First AI-Powered Java Application? — Spring AI Full Beginner Guide

Spring AI makes it insanely easy to connect Java with OpenAI, Hugging Face, and more — no messy APIs, just clean Spring magic. In this guide, you’ll build your first AI-driven Java application in minutes. Let’s code the future!

AI-Powered Java Application — Spring AI Full Beginner Guide

Welcome to the world of Spring AI! In 2025, generative AI (LLMs like GPT-4/5) is everywhere — writing code, answering questions, and more. Java developers naturally ask: “How do I bring this power into my Spring Boot apps?” The answer is Spring AI, a new Spring-native framework that makes integrating AI models as easy as plugging in Spring Data or Spring Security.

Instead of hand-coding HTTP calls and JSON parsing, Spring AI provides a common abstraction layer for chat, embeddings, and other AI tasks, letting you swap providers (OpenAI, Hugging Face, Azure, etc.) with just a config change. In this guide, we’ll build a simple Spring Boot service powered by OpenAI (chat model), covering setup, configuration, controllers, prompt templates, response handling, logging, testing, and even switching to Hugging Face as a bonus.

Spring AI applies Spring patterns to AI. Think of it like Spring Data JPA — you write to a standard interface and Spring talks to the right backend. You’ll inject a ChatClient and call it, and Spring AI handles the rest. This means no more hardcoding API URLs or reinventing wheels for each provider. By the end, you’ll have a Spring Boot “AI app” that can chat with GPT or other models with minimal code.

Tip: Use modern Java (e.g. Java 21+) and Spring Boot 3.2+ to be compatible with Spring AI. Set up your project with Spring Initializr: include Web and AI model starters (e.g. OpenAI). You can also add Spring Boot Actuator later for observability.

What is Spring AI and Why It Matters in 2025

Spring AI is a Spring team project for AI integration. It was inspired by Python libraries like LangChain, but built from the ground up for Java/Spring. Its goal is to make adding AI features to your Spring apps streamlined and familiar, without unnecessary complexity. Instead of writing raw HTTP calls to OpenAI or Hugging Face endpoints, you use Spring beans (like a ChatClient). Spring AI then handles authentication, requests, retries, etc., under the hood.

Why does this matter now? By 2025, AI is part of every tech stack. Enterprises want to add chatbots, summaries, code generation, etc., to their Java backend. Spring AI lets you do that “the Spring way”. You can switch from OpenAI to a local model or another cloud service just by changing dependencies and config — no code rewrite. In short, Spring AI brings the power of LLMs into the familiar Spring ecosystem, so Java developers aren’t left behind in the AI revolution.

Setting Up the Spring Boot Project

First, create a new Spring Boot project (e.g. via Spring Initializr). Choose Java 21+ and Spring Boot 3.2+, and add the Web dependency. Then add the Spring AI starters for your AI model. For OpenAI, add the Spring AI Boot and OpenAI model starters. In Maven pom.xml it looks like this

<dependencies>
<! - Spring AI core starter →
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-boot-starter</artifactId>
</dependency>
<! - Spring AI OpenAI model starter →
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-starter</artifactId>
</dependency>
</dependencies>

(Newer Spring AI versions may use artifact names like spring-ai-openai-spring-boot-starter — check Spring AI docs for the latest.) If you use Gradle, similarly include spring-ai-boot-starter and spring-ai-openai-starter. These starters bring in everything needed to call OpenAI’s Chat API without additional SDKs.

Tip: Consider importing the Spring AI Bill of Materials (BOM) for version consistency. For example, use <artifactId>spring-ai-bom</artifactId> with <type>pom</type> to pin matching versions. This ensures all Spring AI modules stay in sync.

Next, add any other dependencies your app needs (e.g. Spring Web MVC). Your project is now ready for AI integration.

Configuring the OpenAI API Key

Spring AI reads model credentials and settings from Spring properties. Create an application.properties (or .yml) in src/main/resources. At minimum, set your OpenAI API key. Do not hardcode the key – use an environment variable or secure config. For example, in application.properties

# OpenAI API credentials and model configuration
spring.ai.openai.api-key=${OPENAI_API_KEY}
spring.ai.openai.chat.model=gpt-4
spring.ai.openai.chat.temperature=0.7

Here we use ${OPENAI_API_KEY} to load the key from an environment variable (set export OPENAI_API_KEY=your_key in your OS). Spring AI will use the specified model (e.g. gpt-4) and temperature. You can omit the model if you want the default.

Tip: Store secrets securely. E.g. in Linux/Mac: export OPENAI_API_KEY=…. Spring’s placeholder ${…} reads it from the environment. This way your source code stays free of private keys.

If you prefer YAML, it looks like this:

spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
chat:
options:
model: gpt-4
temperature: 0.7

Beyond api-key, you can set other options like temperature, max tokens, etc., under spring.ai.openai.chat.options. For basic use, just the API key is enough.

Now the application knows how to reach OpenAI. Spring Boot auto-configuration will create a ChatClient bean (and other model beans) behind the scenes based on these properties.

Building a Simple AI-Driven Controller

With configuration in place, let’s write a controller that uses Spring AI’s ChatClient. Spring auto-configures a ChatClient.Builder (or directly a ChatClient bean) using the above properties. You can inject a ChatClient.Builder into your class and build the client.

For example, here’s a REST controller that returns a quick joke about any topic:

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;
public ChatController(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder.build();
}
@GetMapping("/ai/simple-joke")
public String getJoke(@RequestParam(defaultValue = "programmers") String topic) {
// Send a prompt to the AI model and return the response
return chatClient
.prompt()
.user("Tell me a simple joke about " + topic)
.call()
.content();
}
}

In this code:

  • We inject ChatClient.Builder and call build() to get a ChatClient instance.
  • In getJoke(), we create a chat prompt with user("..."), then .call() the AI.
  • .content() extracts the generated text as a String.

No OpenAI-specific code appears here — Spring AI does it. This aligns with the principle of provider-agnostic code.

You can return the raw string, or wrap it in JSON as needed (for example, returning a Map.of("joke", response)).

Tip: Use ChatClient methods fluently. The call above is equivalent to invoking OpenAI’s Chat Completions API, but you don’t deal with HTTP or JSON. If you need to customize headers or timeouts, Spring AI also provides configuration properties and hooks (see the docs).

Using Prompt Templates to Improve Prompts

Hardcoding prompt strings can get messy. Spring AI offers PromptTemplate for cleaner, parameterized prompts. This lets you define a template with placeholders and fill in variables at runtime. For example:

@GetMapping("/ai/template-joke")
public String getTemplateJoke(@RequestParam String adjective,
@RequestParam String topic)
{
// Define a template with placeholders {adjective} and {topic}
PromptTemplate template = new PromptTemplate("Tell me a {adjective} joke about {topic}.");
// Fill in the placeholders
Prompt prompt = template.create(Map.of("adjective", adjective, "topic", topic));
// Call the AI model with the templated prompt
return chatClient
.call(prompt)
.getResult()
.getOutput()
.getContent();
}

This PromptTemplate example comes from the Spring AI docs. It produces prompts like “Tell me a funny joke about cats.” You just change the template and variables – no string concatenation needed.

Tip: Prompt templates are great for maintenance. You can store templates in files or constants and reuse them. It also avoids injection issues with raw user input. Spring AI automatically adds any special instructions needed for output formatting (if you ask for structured data) when using entity() or converters.

Handling the AI Response

Spring AI lets you extract just the text or get the full response metadata. In the above examples we used .content() to get the plain string. Under the hood, Spring AI’s .call() returned a ChatResponse object with details. If you need more control (e.g. token usage, model name, etc.), you can get the full response. For example:

ChatResponse response = chatClient
.prompt()
.user("Tell me a simple joke")
.call()
.response(); // get full ChatResponse
String joke = response.getResult().getOutput().getContent();
System.out.println("Model used: " + response.getMetadata().getModel());

Here, response.getResult().getOutput().getContent() is the joke text, and response.getMetadata() has info like the model name and token counts.

Error Handling

AI calls can fail (network issues, rate limits, invalid input, etc.). Wrap your calls in a try-catch to handle errors gracefully. Spring AI provides a common exception for API errors: OpenAiApiClientErrorException (and equivalents for other providers). For example:

try {
String answer = chatClient.prompt()
.user("Hello world")
.call()
.content();
return answer;
}
catch (OpenAiApiClientErrorException ex) {
// Handle API error (e.g., log and return a default message)
System.err.println("AI call failed: " + ex.getMessage());
return "Sorry, I couldn't get a response right now.";
}

This catches all OpenAI-related errors in one placebaeldung.com. You could also catch the generic ChatClientException or use Spring’s @ControllerAdvice to map exceptions to HTTP error responses.

Tip: Always validate user input. Do basic checks (e.g., not empty) before sending to the model, to avoid unexpected failures.

Adding Logging for Debugging

Logging is crucial for debugging AI prompts and responses. For simple cases, you can print or log in your code. For example, using SLF4J:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@RestController
public class ChatController {
private static final Logger log = LoggerFactory.getLogger(ChatController.class);
// ...
public String getJoke(...) {
String prompt = "Tell me a joke about " + topic;
log.info("Sending prompt to AI: {}", prompt);
String answer = chatClient.prompt().user(prompt).call().content();
log.info("Received response: {}", answer);
return answer;
}
}

Spring AI also has built-in observability. You can enable logging of prompts and completions via properties :

spring.ai.chat.client.observations.log-prompt=true
spring.ai.chat.client.observations.log-completion=true

By default these are false (to avoid accidentally logging sensitive info). Turn them on in development or debugging to see exactly what gets sent and received.

Tip: Consider using Spring Boot Actuator and Micrometer to collect metrics (Spring AI provides metrics for calls). But for beginners, simple logs (or the built-in observers) are a good start.

Testing the API with Postman or curl

With the app running, test your endpoints just like any REST API. For example, if you used the above controller:

Using curl:

curl "http://localhost:8080/ai/simple-joke?topic=java"

  • This might return something like "A SQL query walks into a bar, walks up to two tables and asks: 'Can I join you?'".

Using Postman:

  • Send a GET to http://localhost:8080/ai/simple-joke with query param topic=java.
  • You should get a JSON/string response with the joke.

If you built the ChatService example (from [18]):

curl "http://localhost:8080/ai/chat?prompt=Hello"

It will return the AI’s answer in JSON. (The exact URL depends on your mappings.)

Check your console logs to see the prompt and response printed (if you added logging).

Deploying Locally

For local testing (no Docker needed), you can run the Spring Boot app directly:

  1. Run via Maven:
mvn spring-boot:run

This will compile and start the app on port 8080 by default.

2. Run the JAR:

mvn clean package
java -jar target/your-app-name-0.0.1-SNAPSHOT.jar

(Replace the JAR name as built.) The embedded Tomcat will start and serve your APIs.

Before running, ensure the OPENAI_API_KEY environment variable is set in that shell/session (or set the property in application.properties if you prefer, though env var is safer). Once running, open http://localhost:8080/ in a browser or use curl/Postman as above.

No special deployment steps are needed beyond a normal Spring Boot app — it’s just another web application with an extra AI client.

Bonus: Switching from OpenAI to Hugging Face

One of Spring AI’s advantages is easy switching between providers. To use a Hugging Face Inference API instead of OpenAI:

  1. Add the Hugging Face starter to your pom.xml (instead of or in addition to the OpenAI one)
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-huggingface-spring-boot-starter</artifactId>
<!-- or spring-ai-starter-model-huggingface in some versions -->
</dependency>

2. Configure the Hugging Face endpoint and key in application.properties:

spring.ai.huggingface.chat.api-key=${HUGGINGFACE_API_KEY}
spring.ai.huggingface.chat.url=https://api-inference.huggingface.co/models/your-model

You need a Hugging Face Inference API token (HUGGINGFACE_API_KEY) and the inference URL for your model endpoint.

3. Use the Hugging Face client in code: The starter auto-configures a HuggingfaceChatModel bean. You can inject it directly, as in this example from the docs :

@RestController
public class HuggingChatController {

private final HuggingfaceChatModel chatModel;

public HuggingChatController(HuggingfaceChatModel chatModel) {
this.chatModel = chatModel;
}

@GetMapping("/ai/generate")
public Map<String, String> generate(@RequestParam(defaultValue = "Hello") String message) {
String output = this.chatModel.call(message);
return Map.of("output", output);
}
}

This works the same way, but under the covers it calls the Hugging Face endpoint. No need to change the rest of your app code.

Tip: To swap providers, you really only change the dependency and config. Your service logic stays identical. This “write once, switch easily” capability is one of Spring AI’s core benefits

Too Long; Didn’t Read

Introduction: Spring AI is a Spring-friendly way to call LLMs (OpenAI, HuggingFace, etc.) in your Java apps. It saves you from manual HTTP calls and JSON handling.

Setup: Create a Spring Boot project (Java 21+, Spring Boot 3.2+). Add spring-ai-boot-starter and spring-ai-openai-starter (or the appropriate provider starter) to your pom.xml.

API Key: In application.properties, set spring.ai.openai.api-key=${OPENAI_API_KEY} (and model, temperature) to point to your OpenAI key.

Controller: Inject ChatClient (via ChatClient.Builder), then in your endpoint do:

chatClient.prompt().user("Your prompt here").call().content();

This sends the prompt to the AI and gets a String reply.

  • Prompt Templates: Use PromptTemplate for dynamic prompts. E.g.
PromptTemplate template = new PromptTemplate("Tell me a {adj} joke about {topic}.");
Prompt prompt = template.create(Map.of("adj", "funny", "topic", "cats"));
String answer = chatClient.call(prompt).getResult().getOutput().getContent();

Handling Responses: .content() gives the text. Use .response() to get ChatResponse with metadata. Wrap calls in try-catch to handle errors (Spring AI throws OpenAiApiClientErrorException on API failures).

Logging: Use a logger (or enable Spring AI observability) to log prompts and responses for debugging.

Testing: Run the app and test with Postman or curl. E.g.:

curl "http://localhost:8080/ai/simple-joke?topic=java"

You should see the AI’s generated text in the response.

Running Locally: Just run with mvn spring-boot:run or build a jar and java -jar. Ensure the OPENAI_API_KEY env var is set. The app will serve on port 8080 by default.

Bonus — Switch to Hugging Face: Replace the OpenAI starter with spring-ai-huggingface-spring-boot-starter, and in application.properties set spring.ai.huggingface.chat.api-key and spring.ai.huggingface.chat.url as documented. Inject HuggingfaceChatModel in your code and call it just like ChatClient.

Spring AI brings AI into Spring Boot in a familiar way, so you can focus on your application logic instead of plumbing. With these steps, you’ve built a basic AI-powered Spring app. From here, you can explore more Spring AI features — structured outputs, embeddings, RAG, and more — as your AI needs grow.

Post a Comment

0 Comments