Skip to content

How to Build AI Apps Entirely in Java with Spring AI

Purpose

I wanted to build an AI application, but I didn’t want to deal with Python. Every AI tutorial I found assumed I’d use Python, set up virtual environments, and manage Python dependencies. But I work in Java, and I wanted to stay in Java.

This post shows how I built my first AI app entirely in Java using Spring AI, with just one Maven dependency and some YAML configuration.

Environment

  • Java 21
  • Spring Boot 3.2
  • Spring AI 1.0.0-M4
  • Maven 3.9
  • macOS (but OS doesn’t matter for Java)

The Problem

When I searched for “how to build AI applications”, everything pointed me to Python:

Terminal window
# Typical AI setup I wanted to avoid
pip install openai
python -m venv venv
source venv/bin/activate
export OPENAI_API_KEY="sk-..."

I already have Java projects, Spring Boot applications, and a team that knows Java. Learning Python just to call an API felt unnecessary. The AI providers have HTTP APIs, so why can’t I use them from Java?

Then I found Spring AI.

What is Spring AI?

Spring AI is a Spring project that provides a unified API for multiple AI providers: OpenAI, Google, Anthropic, Azure, and more. It handles the HTTP calls, authentication, and response parsing, leaving me with a clean Java interface.

The key insight: I don’t need Python to call AI APIs. I just need a good Java client.

The Setup

I created a new Spring Boot project and added one dependency to my pom.xml:

pom.xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai-sdk</artifactId>
<version>1.0.0-M4</version>
</dependency>

That’s it. One dependency. No Python, no virtual environments.

For the API key, I added this to application.yml:

src/main/resources/application.yml
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
chat-options:
model: gpt-3.5-turbo
temperature: 0.7

I set the environment variable OPENAI_API_KEY in my shell, and Spring Boot picked it up automatically.

My First AI Call

I wanted to start simple. I created a controller that accepts a message and returns an AI response:

ChatController.java
@RestController
class ChatController {
private final ChatClient chatClient;
public ChatController(ChatClient chatClient) {
this.chatClient = chatClient;
}
@GetMapping("/chat")
public String chat(@RequestParam String message) {
return chatClient.prompt()
.user(message)
.call()
.content();
}
}

When I started the application and hit http://localhost:8080/chat?message=Hello, I got back an AI response. No Python involved.

How It Works

Spring AI provides the ChatClient bean through auto-configuration. I just inject it and use it. The prompt() method starts a builder, user() adds my message, call() makes the HTTP request to OpenAI, and content() extracts the text response.

I wanted to understand what was happening under the hood, so I checked the logs. I could see HTTP calls to OpenAI’s API, but I didn’t have to manage any of that.

Adding a System Prompt

Next, I wanted to give the AI a persona. I created a configuration class:

ChatConfig.java
@Configuration
class ChatConfig {
@Bean
ChatClient chatClient(ChatClient.Builder builder) {
return builder
.defaultSystem("You are a helpful AI assistant that answers questions about Java and Spring")
.build();
}
}

Now every request includes this system prompt automatically. I didn’t have to modify my controller.

Using Multiple AI Providers

I wanted to try Google’s Gemini API alongside OpenAI. I added another dependency:

pom.xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-google-genai</artifactId>
<version>1.0.0-M4</version>
</dependency>

Then I configured both API keys:

application.yml
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
google:
ai:
api-key: ${GOOGLE_AI_API_KEY}

I created two separate beans:

MultiChatClientConfig.java
@Configuration
class MultiChatClientConfig {
@Bean
public ChatClient openAiChatClient(OpenAiChatModel openAiChatModel) {
return ChatClient.create(openAiChatModel);
}
@Bean
public ChatClient googleChatClient(GoogleAiChatModel googleAiChatModel) {
return ChatClient.create(googleAiChatModel);
}
}

Now I can inject either one and get responses from different providers. Same API, different backing service.

What I Learned

The biggest surprise was how simple this was. I expected to wrestle with authentication, HTTP clients, and JSON parsing. But Spring AI handles all of that.

The ChatClient API is fluent and intuitive. Here’s a more complex example with parameters:

AdvancedChatService.java
@Service
class ChatService {
private final ChatClient chatClient;
public ChatService(ChatClient chatClient) {
this.chatClient = chatClient;
}
public String tellMeAbout(String composer) {
return chatClient.prompt()
.user(u -> u
.text("Tell me about the movies composed by {composer}")
.param("composer", composer))
.call()
.content();
}
}

This uses parameterized prompts, similar to prepared statements in SQL. The text gets preprocessed before sending to the AI, which is useful for reusable prompt templates.

Why This Matters

Before Spring AI, Java developers had two choices:

  1. Write Python code for AI features
  2. Build their own HTTP client wrapper for AI APIs

Option 1 means maintaining two languages, two build systems, and two deployment pipelines. Option 2 means reinventing the wheel and handling edge cases.

Spring AI gives us a third option: use AI APIs like any other Spring dependency. It fits naturally into existing Spring Boot applications.

I can see using this for:

  • Customer service chatbots
  • Code review assistants
  • Document summarization
  • Content generation

All in Java, all in the same application.

Common Mistakes I Made

I made a few mistakes getting started:

First, I forgot the Spring Milestone Repository. Spring AI is in milestone release, so I needed this in my pom.xml:

pom.xml
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>

Second, I tried to manually create the ChatClient. I thought I needed to instantiate it myself, but Spring Boot auto-configures it. I just needed to inject it.

Third, I used the wrong property for Google’s API key. It’s spring.ai.google.ai.api-key, not spring.ai.google.api-key. The extra ai caught me.

Summary

In this post, I showed how to build AI applications entirely in Java using Spring AI. The key point is that you don’t need Python for AI development anymore. With a single Maven dependency and some YAML configuration, you can call multiple AI providers through a unified ChatClient API.

This approach keeps you in the Java ecosystem, leverages existing Spring Boot patterns, and eliminates the complexity of managing multiple languages. You can find the complete example code on GitHub if you want to try it yourself.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments