Why architecture matters here

Spring integration matters because the hard parts of production agents are the parts Spring already solves. An agent in production needs dependency injection (its tools depend on repositories, clients, config), externalized and profiled configuration (dev vs prod model endpoints, keys from a secret manager), security (who can invoke this agent, as whom), persistence (sessions in a real database), observability (metrics, health, tracing), and testing infrastructure — and building all of that bespoke around an agent framework recreates, badly, what Spring provides mature and integrated. By making agents beans, ADK Java lets the agent inherit all of it: the tool bean gets its dependencies injected, the config comes from the same property system, security applies through the same filter chain, sessions persist through Spring Data, and metrics flow through Micrometer. The agent is production-ready because it lives in a production-ready framework.

The tools-as-Spring-services point is the deepest integration win. In a non-integrated agent, the tool that looks up an order is a function that reimplements or wraps the application's order logic — a duplication and a drift risk. In Spring-integrated ADK Java, the order-lookup tool is a method on the OrderService bean the rest of the application already uses, with the same transaction semantics, the same caching, the same security context, the same tested logic. The agent's capabilities are the application's capabilities, exposed to the model — no adapter layer, no duplication, no drift. This is what 'the application has an agent' means concretely: the agent acts through the same services that serve every other interface.

And the reactive-versus-blocking choice, which Spring surfaces explicitly, matters particularly for agents because agent workloads are IO-bound in a specific way: each turn waits on model calls (hundreds of milliseconds to seconds) and tool calls (database, APIs), so an agent serving many concurrent conversations needs to not block a thread per waiting conversation. Spring WebFlux (reactive, non-blocking) or Spring MVC with virtual threads (Java 21+, blocking-style code that doesn't pin OS threads) both solve this — and choosing between them (reactive's composability and backpressure vs virtual threads' simplicity) is an architectural decision Spring makes explicit and ADK Java accommodates, letting the agent scale to many concurrent conversations without a thread-per-conversation wall.

Advertisement

The architecture: every piece explained

Top row: the application and its agents. A Spring Boot app hosts everything — the agent is a capability within it, not a separate service (though it can be a dedicated microservice if the architecture prefers). Agent beans: agents are defined as Spring beans (via configuration classes or component scanning), so an LlmAgent is constructed by the DI container with its tools, model config, and callbacks injected — and consumed by other beans (a REST controller autowires the agent to serve it). Config properties: model selection, endpoints, keys, temperature, and agent behavior flow through Spring's @ConfigurationProperties — typed, validated, profile-aware (dev/staging/prod), externalized (env, config server, secret manager), so the agent's configuration is managed exactly like the application's. REST/WebFlux endpoints expose the agent: a controller (MVC or reactive) receives requests, invokes the agent's runner, and streams or returns responses — with SSE for streaming agents.

Middle row: the integration surfaces. Tool beans are the key: tools are methods on Spring service beans, so the agent's tools are the application's services with dependencies injected, transactions applied, and logic shared — no wrapping. Session store: ADK Java's session service backs onto Spring Data repositories (JPA, R2DBC for reactive, MongoDB, Redis), so sessions persist in the application's database with the ORM/repository patterns the team uses. Security: Spring Security integration means the agent endpoint is protected by the same authentication (OAuth, JWT, session) and authorization (method security, role checks) as the rest of the app, and the authenticated principal flows into the agent's context so tools act with the right identity — the security integration that makes agent tool-calls respect the user's permissions. Actuator + Micrometer: agent health checks (model reachability, session store) surface through Actuator, and agent metrics (turn latency, token counts, tool durations, costs) flow through Micrometer to Prometheus/whatever the team monitors — the agent's observability in the same pane as the application's.

Bottom rows: concurrency and quality. Reactive vs blocking is the scaling decision: WebFlux (Project Reactor, non-blocking, backpressure — composes with reactive tool and model clients) or Spring MVC with virtual threads (Java 21+, straightforward blocking-style code on virtual threads that don't pin OS threads) — either lets the agent serve many concurrent conversations without thread-per-conversation exhaustion, matching the IO-bound nature of agent turns. Testing: Spring Boot Test spins the context for integration tests (agent + real tools + test session store), combined with ADK's eval framework for behavioral testing — the agent tested with the same infrastructure as the app. The ops strip: bean lifecycle (agents and tools as managed beans with proper startup/shutdown, resource cleanup via Spring's lifecycle), config management (profiles, externalization, secret integration), and enterprise integration (the agent participating in the org's Spring-based platform — service discovery, config server, distributed tracing).

ADK Java + Spring — agents as first-class Spring citizensDI, config, observability, the enterprise stackSpring Boot appthe host applicationAgent beansDI-managed agents + toolsConfig propertiestyped, profiled, externalizedREST / WebFluxagent endpointsTool beansSpring services as toolsSession storeSpring Data backendsSecuritySpring Security integrationActuator + Micrometermetrics + healthReactive vs blockingWebFlux + virtual threadsTestingSpring Boot Test + agent evalsOps — bean lifecycle + config management + enterprise integrationwirepersistsecureobservethreadtestmonitoroperateoperate
ADK Java in Spring: agents and tools are DI-managed beans, sessions use Spring Data, endpoints use WebFlux/MVC, and Actuator/Micrometer provide observability.
Advertisement

End-to-end flow

Build an agent into an existing Spring Boot order-management application and watch the integration pay off. The app already has an OrderService, a CustomerService, Spring Security with OAuth, JPA repositories, and Actuator metrics. Adding a support agent: define an LlmAgent bean whose tools are methods on the existing services — orderService.lookupOrder, orderService.initiateRefund, customerService.getProfile — exposed as ADK tools. No wrapping: the tools are the services, so a refund initiated by the agent goes through the same transaction, the same business rules, the same audit logging as a refund from any other interface. The agent's config (model, temperature, endpoints) lives in application.yml with profiles (a cheaper model in dev, the production model in prod, keys from the secret manager) — managed identically to the app's config.

The security and observability integration shows the enterprise fit. The agent endpoint (/api/support/chat) is protected by the same Spring Security config as everything else; the authenticated customer's principal flows into the agent's context, and a before_tool-style check ensures lookupOrder only returns orders belonging to that customer — the agent's tool-calls respect the same authorization as the app's endpoints, because they share the security context. Metrics flow through Micrometer: turn latency, token counts, tool durations, and costs appear on the same Grafana dashboards as the app's HTTP metrics; Actuator health includes model reachability. When the agent is slow, it's debugged with the same distributed tracing (the agent's model and tool calls are spans in the same trace as the HTTP request) the team uses for everything else.

The concurrency and testing vignettes complete the enterprise story. The app runs on Spring MVC with virtual threads (Java 21): the agent's IO-bound turns (waiting on model and tool calls) don't pin OS threads, so a single instance serves hundreds of concurrent support conversations without a thread-per-conversation wall — the virtual-thread model letting blocking-style agent code scale like reactive without the reactive complexity. Testing uses Spring Boot Test: an integration test spins the context with real services and a test session store, drives the agent through a support scenario, and asserts both the outcome and (via ADK's eval framework) the tool trajectory — the agent tested with the app's testing infrastructure. And when the app deploys — same CI/CD, same container, same config server, same rolling deploy — the agent deploys with it, because it's part of the application, not a separate system to release. The consolidated lesson: the agent inherited DI, config, security, persistence, observability, concurrency, testing, and deployment from Spring, so the team built an agent feature, not an agent platform — which is exactly what an enterprise adopting agents into an existing stack needs.