Spring Boot Integration
LangChain4j provides Spring Boot starters for:
- popular integrations
- declarative AI Services
Spring Boot Starters
Spring Boot starters help with creating and configuring language models, embedding models, embedding stores, and other core LangChain4j components through properties.
To use one of the Spring Boot starters, import the corresponding dependency.
The naming convention for the Spring Boot starter dependency is:
langchain4j-{integration-name}-spring-boot4-starterfor Spring Boot 4langchain4j-{integration-name}-spring-boot-starterfor Spring Boot 3
For example, for OpenAI (langchain4j-open-ai):
Spring Boot 4:
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai-spring-boot4-starter</artifactId>
<version>1.23.0-beta33</version>
</dependency>
Spring Boot 3:
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai-spring-boot-starter</artifactId>
<version>1.23.0-beta33</version>
</dependency>
Then, you can configure model parameters in the application.properties file as follows:
langchain4j.open-ai.chat-model.api-key=${OPENAI_API_KEY}
langchain4j.open-ai.chat-model.model-name=gpt-4o
langchain4j.open-ai.chat-model.log-requests=true
langchain4j.open-ai.chat-model.log-responses=true
...
In this case, an instance of OpenAiChatModel (an implementation of a ChatModel) will be automatically created,
and you can autowire it where needed:
@RestController
public class ChatController {
ChatModel chatModel;
public ChatController(ChatModel chatModel) {
this.chatModel = chatModel;
}
@GetMapping("/chat")
public String model(@RequestParam(value = "message", defaultValue = "Hello") String message) {
return chatModel.chat(message);
}
}
If you need an instance of a StreamingChatModel,
use the streaming-chat-model instead of the chat-model properties:
langchain4j.open-ai.streaming-chat-model.api-key=${OPENAI_API_KEY}
...
Spring Boot starter for declarative AI Services
LangChain4j provides a Spring Boot starter for auto-configuring AI Services, RAG, Tools etc.
Assuming you have already imported one of the integrations starters (see above),
import langchain4j-spring-boot4-starter (Spring Boot 4) or langchain4j-spring-boot-starter (Spring Boot 3):
Spring Boot 4:
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-spring-boot4-starter</artifactId>
<version>1.23.0-beta33</version>
</dependency>
Spring Boot 3:
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-spring-boot-starter</artifactId>
<version>1.23.0-beta33</version>
</dependency>
You can now define AI Service interface and annotate it with @AiService:
@AiService
interface Assistant {
@SystemMessage("You are a polite assistant")
String chat(String userMessage);
}
Think of it as a standard Spring Boot @Service, but with AI capabilities.
When the application starts, LangChain4j starter will scan the classpath
and find all interfaces annotated with @AiService.
For each AI Service found, it will create an implementation of this interface
using all LangChain4j components available in the application context and will register it as a bean,
so you can auto-wire it where needed:
@RestController
class AssistantController {
@Autowired
Assistant assistant;
@GetMapping("/chat")
public String chat(String message) {
return assistant.chat(message);
}
}
Automatic Component Wiring
The following components will be automatically wired into the AI Service if available in the application context:
ChatModelStreamingChatModelChatMemoryChatMemoryProviderContentRetrieverRetrievalAugmentorToolProviderToolExecutionErrorHandlerandToolArgumentsErrorHandler(see Error Handling)- All methods of any
@Componentor@Serviceclass that are annotated with@ToolAn example:
@Component
public class BookingTools {
private final BookingService bookingService;
public BookingTools(BookingService bookingService) {
this.bookingService = bookingService;
}
@Tool
public Booking getBookingDetails(String bookingNumber, String customerName, String customerSurname) {
return bookingService.getBookingDetails(bookingNumber, customerName, customerSurname);
}
@Tool
public void cancelBooking(String bookingNumber, String customerName, String customerSurname) {
bookingService.cancelBooking(bookingNumber, customerName, customerSurname);
}
}
If multiple components of the same type are present in the application context, the application will fail to start. In this case, use the explicit wiring mode (explained below).
Explicit Component Wiring
If you have multiple AI Services and want to wire different LangChain4j components into each of them,
you can specify which components to use with explicit wiring mode (@AiService(wiringMode = EXPLICIT)).
Let's say we have two ChatModels configured:
# OpenAI
langchain4j.open-ai.chat-model.api-key=${OPENAI_API_KEY}
langchain4j.open-ai.chat-model.model-name=gpt-4o-mini
# Ollama
langchain4j.ollama.chat-model.base-url=http://localhost:11434
langchain4j.ollama.chat-model.model-name=llama3.1
@AiService(wiringMode = EXPLICIT, chatModel = "openAiChatModel")
interface OpenAiAssistant {
@SystemMessage("You are a polite assistant")
String chat(String userMessage);
}
@AiService(wiringMode = EXPLICIT, chatModel = "ollamaChatModel")
interface OllamaAssistant {
@SystemMessage("You are a polite assistant")
String chat(String userMessage);
}
In this case, you must explicitly specify all components.
Choosing Components via Configuration
Instead of hard-coding bean names, you can use property placeholders, so that the component wired into an AI Service can be changed via configuration (e.g., per environment or profile):
my-app.assistant.chat-model-bean=ollamaChatModel
my-app.assistant.tool-beans=weatherTools,calendarTools
@AiService(
wiringMode = EXPLICIT,
chatModel = "${my-app.assistant.chat-model-bean:openAiChatModel}",
tools = "${my-app.assistant.tool-beans}"
)
interface Assistant {
String chat(String userMessage);
}
A few things to keep in mind:
- A placeholder must resolve to a bean name (e.g.,
ollamaChatModel), not to the name of an LLM model (e.g.,llama3.1). The model name is configured on theChatModelbean itself (e.g.,langchain4j.ollama.chat-model.model-name). - A default value can be specified after a colon:
${my-app.assistant.chat-model-bean:openAiChatModel}. - If a placeholder cannot be resolved and has no default value, the application fails to start.
- If a placeholder resolves to an empty value, the attribute is treated as if it was not set.
- For
tools, a placeholder can resolve to several comma-separated bean names.
More details can be found here (same API for the Spring Boot 4 variant).
Listening for AI Service Registration Events
After you have completed the development of the AI Service in a declarative manner, you can listen for the
AiServiceRegisteredEvent by implementing the ApplicationListener<AiServiceRegisteredEvent> interface.
This event is triggered when AI Service is registered in the Spring context,
allowing you to obtain information about all registered AI services and their tools at runtime.
Here is an example:
@Component
class AiServiceRegisteredEventListener implements ApplicationListener<AiServiceRegisteredEvent> {
@Override
public void onApplicationEvent(AiServiceRegisteredEvent event) {
Class<?> aiServiceClass = event.aiServiceClass();
List<ToolSpecification> toolSpecifications = event.toolSpecifications();
for (int i = 0; i < toolSpecifications.size(); i++) {
System.out.printf("[%s]: [Tool-%s]: %s%n", aiServiceClass.getSimpleName(), i + 1, toolSpecifications.get(i));
}
}
}
Flux
When streaming, you can use Flux<String> as a return type of AI Service:
@AiService
interface Assistant {
@SystemMessage("You are a polite assistant")
Flux<String> chat(String userMessage);
}
For this, please import langchain4j-reactor module.
See more details here.
That module also provides Mono<T> and Flux<AiServiceStreamingEvent> for the
non-blocking modes, and the starter can route LangChain4j's offloaded work through
Spring's own task executor with langchain4j.executor.use-spring-task-executor=true so that tracing, MDC and
security context follow an asynchronous invocation.
The OpenAI, Mistral AI and Ollama starters send requests with Spring's HTTP clients: RestClient for blocking
calls and WebClient for the non-blocking modes. So when you use the non-blocking modes with one of these starters
and a provider that supports them (see Provider support), WebClient
has to be on the classpath. Flux<String> and TokenStream do not need it.
- Spring Boot 4: add
spring-boot-starter-webclient. - Spring Boot 3: add
org.springframework:spring-webflux. If your application is not a web application (nospring-boot-starter-web), also setspring.main.web-application-type=none, because Spring Boot treats an application withspring-webfluxon the classpath as a reactive web application.
Avoid spring-boot-starter-webflux for this purpose unless your application is a WebFlux application: it also
brings a web server. Without WebClient, a non-blocking call fails with an AsyncNotSupportedException that says
what to add.
See HTTP client for how these requests are configured.
HTTP client
The OpenAI, Mistral AI and Ollama starters send requests with Spring's HTTP clients, configured the way Spring Boot
configures its own. The other starters use their own HTTP client, and this section does not apply to them. A model
that you build yourself, with OpenAiChatModel.builder() for example, is not configured this way: pass it an HTTP
client builder as shown in Customizable HTTP Client.
- Builders: blocking requests are built from the application's
RestClient.Builder, non-blocking requests from itsWebClient.Builder. So the filters, default headers and observability configured on each builder apply, and a filter that adds credentials for your own services also runs for requests to the model provider. The two builders are configured separately: a customization of one does not apply to the other. If the application's builder is@LoadBalanced(Spring Cloud), declare a plain@Primarybuilder as well, or the model's HTTP client builder bean as shown below: a load-balanced builder resolves the model provider's host name as a service name. - HTTP client and settings: requests are sent with the HTTP client that Spring Boot is configured to use,
including its customizers (for example a proxy), and with Spring Boot's HTTP client settings, such as redirects
and the SSL bundle. LangChain4j builds the request factory and the connector itself, to apply its timeouts, so a
ClientHttpRequestFactoryorClientHttpConnectorbean of the application is not used: customize them withClientHttpRequestFactoryBuilderCustomizerandClientHttpConnectorBuilderCustomizerbeans instead. The settings are:- Spring Boot 4:
spring.http.clients.*, for both kinds of requests; - Spring Boot 3:
spring.http.client.*for blocking requests andspring.http.reactiveclient.*for non-blocking requests, as for Spring Boot's own clients. In a reactive web application, Spring Boot 3 does not applyspring.http.client.*, so blocking requests use the defaults.
- Spring Boot 4:
- Several beans: if the application has several beans of one of these types (the builders, Spring Boot's HTTP
client builders and its settings) and none of them is
@Primary, LangChain4j uses none of them: it uses a new builder or the defaults instead, and logs a warning. - Timeouts always come from LangChain4j, for example
langchain4j.open-ai.chat-model.timeout. When it is not set, the connect timeout is 15 seconds and the read timeout 60 seconds: raise it for slow models, such as reasoning models or local models that load on first use. Spring Boot'sconnect-timeoutandread-timeouthave no effect on these requests.
So an SSL bundle configured in Spring Boot has to trust the model provider's certificate as well, and a client
certificate in it is also presented to the model provider. To send the requests of a model with other settings,
declare its HTTP client builder bean yourself. Its name is that of the model bean followed by HttpClientBuilder,
for example openAiChatModelHttpClientBuilder or ollamaEmbeddingModelHttpClientBuilder. With Spring Boot 4:
import dev.langchain4j.http.client.HttpClientBuilder;
import dev.langchain4j.http.client.spring.restclient.SpringRestClient;
import org.springframework.boot.http.client.HttpClientSettings;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestClient;
@Bean
HttpClientBuilder openAiChatModelHttpClientBuilder() {
return SpringRestClient.builder()
.restClientBuilder(RestClient.builder())
// no SSL bundle, so the JVM's default trust store is used; the same as not setting it.
// To use another SSL bundle: HttpClientSettings.ofSslBundle(sslBundles.getBundle("..."))
.httpClientSettings(HttpClientSettings.defaults())
.createDefaultStreamingRequestExecutor(false); // a chat model does not stream
}
With Spring Boot 3, the settings of blocking and non-blocking requests are set separately:
import dev.langchain4j.http.client.HttpClientBuilder;
import dev.langchain4j.http.client.spring.restclient.SpringRestClient;
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
import org.springframework.boot.http.client.reactive.ClientHttpConnectorSettings;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestClient;
@Bean
HttpClientBuilder openAiChatModelHttpClientBuilder() {
return SpringRestClient.builder()
.restClientBuilder(RestClient.builder())
.clientHttpRequestFactorySettings(ClientHttpRequestFactorySettings.defaults())
.clientHttpConnectorSettings(ClientHttpConnectorSettings.defaults())
.createDefaultStreamingRequestExecutor(false);
}
Such a bean replaces everything the starter configures for the HTTP client of that model, not only the settings. To keep the rest, pass it yourself:
- the application's
RestClient.Builder, withrestClientBuilder(...). The examples pass a new one, so that the filters and interceptors of the application are not applied to requests to the model provider; - the application's
WebClient.Builder, for non-blocking requests, withwebClientBuilder(WebClientBuilderHolder.of(webClientBuilder)). Without it, a newWebClient.builder()is used; - the HTTP client chosen by Spring Boot, with its customizers: pass the
ClientHttpRequestFactoryBuilderandClientHttpConnectorBuilderbeans toclientHttpRequestFactoryBuilder(...)andclientHttpConnectorBuilder(...); - for a streaming model, the task executor that carries the context (for example tracing, MDC and security) to the
thread reading the stream, with
streamingRequestExecutor(...). The starter declares it as a bean named after the model bean followed byTaskExecutor, for exampleopenAiStreamingChatModelTaskExecutor.
To give LangChain4j a dedicated WebClient.Builder, declare a WebClientBuilderHolder bean:
import dev.langchain4j.http.client.spring.restclient.WebClientBuilderHolder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.reactive.function.client.WebClient;
@Bean
WebClientBuilderHolder langchain4jWebClientBuilder() {
return WebClientBuilderHolder.of(WebClient.builder());
}
Observability
To enable observability for a ChatModel or StreamingChatModel
bean, you need to declare one or more ChatModelListener beans:
@Configuration
class MyConfiguration {
@Bean
ChatModelListener chatModelListener() {
return new ChatModelListener() {
private static final Logger log = LoggerFactory.getLogger(ChatModelListener.class);
@Override
public void onRequest(ChatModelRequestContext requestContext) {
log.info("onRequest(): {}", requestContext.chatRequest());
}
@Override
public void onResponse(ChatModelResponseContext responseContext) {
log.info("onResponse(): {}", responseContext.chatResponse());
}
@Override
public void onError(ChatModelErrorContext errorContext) {
log.info("onError(): {}", errorContext.error().getMessage());
}
};
}
}
Every ChatModelListener bean in the application context will be automatically
injected into all ChatModel and StreamingChatModel beans
created by one of our Spring Boot starters.
Micrometer Metrics
Add the langchain4j-micrometer-metrics dependency to your project:
For Maven:
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-micrometer-metrics</artifactId>
<version>1.23.0-beta33</version>
</dependency>
For Gradle:
implementation 'dev.langchain4j:langchain4j-micrometer-metrics:1.23.0-beta33'
Micrometer (Actuator) Configuration
You should also have the necessary Actuator dependency in your project.
For example, if you are using Spring Boot, you can add the following dependencies to your pom.xml:
For Maven:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
For Gradle:
implementation 'org.springframework.boot:spring-boot-starter-actuator'
Enable the /metrics Actuator endpoint in your properties.
application.properties:
management.endpoints.web.exposure.include=metrics
application.yaml:
management:
endpoints:
web:
exposure:
include: metrics
Configure MicrometerMetricsChatModelListener bean
In a Spring Boot application, you can define the listener as a bean and inject the MeterRegistry:
import dev.langchain4j.micrometer.metrics.listeners.MicrometerMetricsChatModelListener;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MetricsConfig {
@Bean
public MicrometerMetricsChatModelListener listener(MeterRegistry meterRegistry) {
return new MicrometerMetricsChatModelListener(meterRegistry);
}
}
View the Metrics
You can view the metrics by visiting the /actuator/metrics endpoint of your application.
For example, if you are running your application on localhost:8080,
you can visit http://localhost:8080/actuator/metrics to view the metrics.
Token Usage Metric
View the token usage metric at:
http://localhost:8080/actuator/metrics/gen_ai.client.token.usage
Filtering by Token Type
The gen_ai.token.type tag indicates whether the tokens were used for input or output:
| Token Type | Endpoint |
|---|---|
| Input tokens | /actuator/metrics/gen_ai.client.token.usage?tag=gen_ai.token.type:input |
| Output tokens | /actuator/metrics/gen_ai.client.token.usage?tag=gen_ai.token.type:output |
Note: The
gen_ai.client.token.usagemetric is a histogram (DistributionSummary). The endpoint without any tags shows aggregated statistics (count, total, max) across all token types, models, and providers.
Micrometer Observation API
This implements the ChatModelListener using the Micrometer Observation API allowing transparent generation of Metrics and Traces by adding the following dependency:
For Maven:
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-observation</artifactId>
</dependency>
For Gradle:
implementation 'dev.langchain4j:langchain4j-observation'
You need to instantiate the Observation listener as follows...
Configure the ObservationChatModelListener bean
@Configuration
public class ObservationConfig {
@Bean
public ObservationChatModelListener listener(ObservationRegistry observationRegistry, MeterRegistry meterRegistry) {
return new ObservationChatModelListener(observationRegistry, meterRegistry);
}
}
This dependency requires the configuration of the SpringBoot Actuator, as described above.
For additional observability requirements on a SpringBoot application please follow: Building Your First Observed Application
For more details about the langchain4j-observation library, please check the Observability documentation.
Testing
Supported versions
LangChain4j Spring Boot integration requires Java 17 and supports both:
- Spring Boot 4 (4.0+) — use starters with the
-spring-boot4-startersuffix - Spring Boot 3 (3.5+) — use starters with the
-spring-boot-startersuffix, in line with the Spring Boot OSS support policy
Both families are released together and share the same version number. Choose the set of starters that matches the Spring Boot version in your project.