Exploring the Java LLM Ecosystem: Build Your First AI Chat Application

This tutorial walks Java backend developers through the mature Java LLM ecosystem, comparing frameworks like Spring AI and LangChain4j, and demonstrates step‑by‑step how to create a Spring Boot application with a chat endpoint, streaming responses, and dynamic model switching among OpenAI, Tongyi Qwen, and Ollama.

Coder Trainee
Coder Trainee
Coder Trainee
Exploring the Java LLM Ecosystem: Build Your First AI Chat Application

1. Java LLM Ecosystem Overview

The article compares four Java‑centric LLM frameworks:

Spring AI – official Spring integration, high maturity, suited for Spring Boot users.

LangChain4j – active community, rich features, also high maturity.

Ollama Java – lightweight, runs local models, medium maturity.

DeepLearning4J – deep‑learning framework, lower maturity, mainly for model training.

2. Project Goal

Build a Java application that provides a chat API, supports streaming output, and can switch between OpenAI, Alibaba Tongyi Qwen, and local Ollama models through a unified Spring AI abstraction.

2.1 Spring AI Quick Start

Project creation

<!-- pom.xml -->
<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 http://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>3.2.4</version>
    <relativePath/>
  </parent>
  <groupId>com.teaching</groupId>
  <artifactId>java-ai-agent</artifactId>
  <version>1.0.0‑SNAPSHOT</version>
  <properties>
    <java.version>17</java.version>
    <spring‑ai.version>1.0.0‑M4</spring‑ai.version>
  </properties>
  <repositories>
    <repository>
      <id>spring‑milestones</id>
      <name>Spring Milestones</name>
      <url>https://repo.spring.io/milestone</url>
    </repository>
  </repositories>
  <dependencies>
    <!-- Spring AI OpenAI starter -->
    <dependency>
      <groupId>org.springframework.ai</groupId>
      <artifactId>spring‑ai‑openai‑spring‑boot‑starter</artifactId>
      <version>${spring‑ai.version}</version>
    </dependency>
    <!-- Spring Boot Web starter -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring‑boot‑starter‑web</artifactId>
    </dependency>
    <!-- Lombok (optional) -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>
  </dependencies>
</project>

Configuration file

# application.yml
spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
      base-url: ${OPENAI_BASE_URL:https://api.openai.com/v1}
      chat:
        options:
          model: ${MODEL_NAME:gpt-4}
          temperature: 0.7
          max-tokens: 2000
server:
  port: 8080
logging:
  level:
    org.springframework.ai: DEBUG

First AI chat endpoint

// ChatController.java
package com.teaching.ai.controller;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/api/chat")
public class ChatController {
    private final ChatClient chatClient;
    public ChatController(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder.build();
    }
    @PostMapping("/simple")
    public Map<String, String> simpleChat(@RequestBody Map<String, String> request) {
        String message = request.get("message");
        String response = chatClient.prompt(message).call().content();
        Map<String, String> result = new HashMap<>();
        result.put("response", response);
        return result;
    }
    @GetMapping("/hello")
    public Map<String, String> hello() {
        return Map.of("message", "AI Agent service started");
    }
}

Application entry point

// AiApplication.java
package com.teaching.ai;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AiApplication {
    public static void main(String[] args) {
        SpringApplication.run(AiApplication.class, args);
        System.out.println("✅ Java AI Agent started!");
        System.out.println("   http://localhost:8080/api/chat/hello");
    }
}

3. Streaming Output

// StreamController.java
package com.teaching.ai.controller;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import java.util.Map;

@RestController
@RequestMapping("/api/stream")
public class StreamController {
    private final ChatClient chatClient;
    public StreamController(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder.build();
    }
    @PostMapping(value = "/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> streamChat(@RequestBody Map<String, String> request) {
        String message = request.get("message");
        return chatClient.prompt(message)
                .stream()
                .content()
                .map(chunk -> "data: " + chunk + "

");
    }
}

4. Multi‑model Switching

Configuration for multiple models

# application.yml (additional sections)
spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
      chat:
        options:
          model: gpt-4
    dashscope:
      api-key: ${DASHSCOPE_API_KEY}
      chat:
        options:
          model: qwen-max

Model router service

// ModelRouterService.java
package com.teaching.ai.service;

import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.dashscope.DashScopeChatModel;
import org.springframework.stereotype.Service;

@Service
public class ModelRouterService {
    private final OpenAiChatModel openAiModel;
    private final DashScopeChatModel dashScopeModel;
    public ModelRouterService(OpenAiChatModel openAiModel, DashScopeChatModel dashScopeModel) {
        this.openAiModel = openAiModel;
        this.dashScopeModel = dashScopeModel;
    }
    public ChatModel selectModel(String modelType) {
        return switch (modelType) {
            case "openai" -> openAiModel;
            case "qwen" -> dashScopeModel;
            default -> openAiModel;
        };
    }
    public String chat(String message, String modelType) {
        ChatModel model = selectModel(modelType);
        return model.call(message);
    }
}

5. Testing

# Test simple chat
curl -X POST http://localhost:8080/api/chat/simple \
  -H "Content-Type: application/json" \
  -d '{"message":"What is an AI Agent? Explain in one sentence"}'

# Test streaming output
curl -X POST http://localhost:8080/api/stream/chat \
  -H "Content-Type: application/json" \
  -d '{"message":"Tell me a joke"}'

6. Common Issues

Version compatibility

Ensure you use Spring Boot 3.2.x together with Spring AI 1.0.0‑M4.

API‑key configuration

Store keys in environment variables instead of hard‑coding them, e.g. export OPENAI_API_KEY=your-key.

Frontend handling of SSE

// JavaScript example for Server‑Sent Events
const eventSource = new EventSource('/api/stream/chat');
eventSource.onmessage = (event) => {
  console.log(event.data);
};

7. Next Episode Preview

The upcoming article will dive deeper into Spring AI, covering advanced ChatClient usage, prompt‑template systems, output parsers, and function calling.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaLLMStreamingSpring AIChatbotModel Routing
Coder Trainee
Written by

Coder Trainee

Experienced in Java and Python, we share and learn together. For submissions or collaborations, DM us.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.