Recent Posts
Archives

Posts Tagged ‘SpringIO2025’

PostHeaderIcon [SpringIO2025] What’s (new) with Spring Boot and Containers? by Matthias Haeussler / Eva Panadero

Lecturer

Matthias Haeussler is the Vice President Consulting Expert at CGI (formerly Novatec Consulting), a university lecturer for distributed systems at multiple Stuttgart universities, and an awarded ambassador for Cloud Foundry. With a focus on cloud-native technologies, he organizes the Stuttgart Cloud Foundry Meetup, speaks frequently at conferences, and contributes to open-source projects. Eva Panadero is a Software Engineering Lead and Senior Consultant at CGI (formerly Novatec Consulting), with over 10 years of experience in Java and Spring development. She specializes in software engineering, emphasizing cloud-native solutions and container integrations.

Abstract

This article analyzes the integrations between Spring Boot and container technologies, tracing their evolution and highlighting tools for building, testing, and deploying applications in Docker and Kubernetes. It explores features like Buildpacks, Testcontainers, Docker Compose support, and emerging innovations such as Docker Model Runner with Spring AI. Through practical demonstrations, it evaluates methodologies for efficient image creation, local development, and secure deployments, offering insights for optimizing workflows in cloud-native environments.

Historical Context and Evolution of Integrations

Spring Boot and containers have co-evolved since around 2013-2014, with Docker, Kubernetes, and Spring Boot emerging concurrently. This timeline, though non-linear, illustrates key milestones: from early containerization (e.g., chroot in 1979) to modern tools like Testcontainers (2015) and Spring Native (2021). Their synergy addresses packaging standardization, portability, and runtime isolation, enabling consistent deployments across environments.

Containers encapsulate applications with dependencies, ensuring reproducibility. Spring Boot’s executable JARs integrate seamlessly, but containerization adds layers for security and orchestration. The talk’s focus: beyond basic Dockerization, leveraging Spring’s features for streamlined developer journeys.

Building Efficient Container Images

Spring Boot’s Buildpacks integration, via the Maven plugin, automates image creation without Dockerfiles. Commands like mvn spring-boot:build-image produce layered images with base OS, runtime, and application components, optimizing rebuilds. Options include JLink for modularized runtimes, reducing image sizes by stripping unused modules.

For native images, GraalVM compiles ahead-of-time, yielding smaller, faster-starting executables. Challenges like reflection handling are mitigated by metadata agents. Demos show Buildpacks generating OCI-compliant images, pushable to registries.

Code for building with Maven:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <image>
            <name>myapp:${project.version}</name>
        </image>
    </configuration>
</plugin>

This simplifies CI/CD, focusing on application logic over infrastructure.

Testing in Realistic Container Environments

Testcontainers facilitate integration testing by spinning up containers (e.g., databases, queues) programmatically. Spring Boot’s auto-configuration detects these, overriding properties for seamless testing. Annotations like @ServiceConnection simplify setup, injecting connection details.

For example, testing PostgreSQL interactions:

@SpringBootTest
@AutoConfigureServiceConnection(PostgreSQLContainer.class)
class MyTest {
    @Autowired JdbcTemplate jdbcTemplate;
    // Tests use real DB
}

This ensures realistic environments, catching issues early. Multi-container setups via Docker Compose files enhance complexity handling.

Local Development and Deployment Strategies

Spring Boot’s Docker Compose module maps services to properties, starting containers on boot. Profiles activate contextually, e.g., for development. Demos illustrate overriding ports and environment variables.

In Kubernetes, Spring Boot apps deploy via manifests, with features like readiness probes ensuring liveness. Security contexts and secrets management protect sensitive data.

Emerging: Docker Model Runner integrates Spring AI, running local models in containers for inference, reducing cloud dependency.

Developer Tools and Workflow Optimizations

Devcontainers standardize environments via JSON specs, provisioning tools like JDK and extensions. GitHub Codespaces leverage this for cloud-based setups, ensuring consistency.

Implications: Reduced “works on my machine” issues, faster onboarding.

Conclusion: Optimizing Cloud-Native Workflows

Spring Boot’s container integrations streamline from build to production, emphasizing efficiency and security. Buildpacks and Testcontainers minimize ceremony, while features like Docker Model Runner pioneer AI-container synergies. Developers benefit from portable, reproducible workflows, adapting to evolving ecosystems.

Links:

PostHeaderIcon [SpringIO2025] Taming Testing of AI apps by Alex Soto

Lecturer

Alex Soto is the Director of Developer Experience at Red Hat, a Java Champion, and an advocate for open-source software. With over 17 years in the tech industry, he specializes in Java development, software automation, and AI integration. Soto is a prolific author, having co-authored books like “Applied AI for Enterprise Java Developers” and “Quarkus Cookbook,” and he frequently speaks on testing, cloud-native applications, and AI challenges.

Abstract

This article examines the complexities of testing AI-integrated applications, addressing challenges like non-deterministic outputs, hallucinations, and bias. It discusses strategies for ensuring reliability, including synthetic data generation, evaluation metrics, and model-assisted testing. Drawing on practical examples, it highlights methodologies for validating both deterministic and probabilistic components, emphasizing the role of data scientists and robust testing frameworks in building trustworthy AI systems.

Challenges in Testing AI-Integrated Applications

Integrating large language models (LLMs) into applications introduces unique testing hurdles, primarily due to their non-deterministic nature. Responses from models like GPT or Grok vary even for identical inputs, complicating assertions. For instance, querying an image might yield “cat” one time and “kitten” another, rendering strict equality checks ineffective. This unpredictability stems from the probabilistic architecture of LLMs, which prioritize generating plausible answers over consistency.

Hallucinations exacerbate this: models may produce inconsistent outputs (e.g., “Alex is tall and short”), input-output mismatches (e.g., rude responses despite politeness prompts), or factually incorrect information (e.g., “the Earth is flat”). Such behaviors, akin to journalists offering opinions on unfamiliar topics, necessitate specialized testing to detect and mitigate risks.

Traditional testing paradigms falter here, as AI components act as “black boxes.” Developers must treat models as external services, focusing on integration points while acknowledging limited control over internal mechanics.

Strategies for Handling Non-Determinism and Hallucinations

To address non-determinism, employ evaluation metrics over binary pass/fail. Tools like Ragas compute faithfulness (alignment with context), answer relevance, and contextual precision. For example, in retrieval-augmented generation (RAG), Ragas assesses if responses accurately reflect retrieved documents, using scores from 0 to 1.

Synthetic data generation enhances testing realism. LLMs can create diverse datasets, simulating user inputs without privacy concerns. In a pet clinic demo, a model populates forms with realistic personas, verifying outputs against expectations.

For hallucinations, chain-of-thought prompting guides models toward reasoned responses, reducing errors. Assertions check for inconsistencies, such as ensuring polite outputs or factual accuracy via external verifiers.

Code for Ragas evaluation in Java:

import dev.langchain4j.rag.query.Query;
import io.ragas.RagasEvaluator;

RagasEvaluator evaluator = new RagasEvaluator();
Query query = new Query("What is Spring Boot?");
String response = model.generate(query);
double faithfulness = evaluator.evaluateFaithfulness(response, context);
assert faithfulness > 0.8;

This quantifies response quality, enabling threshold-based assertions.

Model-Assisted Testing and Integration Approaches

Leverage AI for test creation and execution. Tools like MCPlaywright use models to script browser interactions, generating tests dynamically. In the pet clinic example, prompts instruct models to navigate, fill forms with synthetic data, and verify tables, outputting pass/fail.

Involve data scientists early for model-specific insights, ensuring tests cover bias and drift. Test deterministic parts (e.g., API routing) separately from AI components, using mocks for isolation.

Be resource-conscious: unnecessary politeness in prompts wastes compute (e.g., “thank you” equates to energy for three water bottles). Focus on rude, direct interactions for efficiency.

Implications for Reliable AI Development

Testing AI apps demands a paradigm shift toward probabilistic validation, blending traditional unit tests with advanced evaluators. Synthetic data and model-assisted tools democratize realistic testing, but require strong testing fundamentals. As AI permeates critical systems, these strategies ensure fairness, safety, and robustness, mitigating risks like hallucinations in production.

Future directions include AI-driven test optimization, reducing human effort while enhancing coverage. Developers must balance innovation with rigor, treating AI as an enhancement rather than a core dependency.

Links:

PostHeaderIcon [SpringIO2025] Panta rhei: runtime configuration updates with Spring Boot by Joris Kuipers

Lecturer

Joris Kuipers is the CTO and hands-on architect at Trifork Amsterdam, with 25 years of experience in software engineering, enterprise Java consulting, and architecture. Specializing in Spring Boot, he focuses on observability, JSON processing, and dynamic configuration. Kuipers is an active speaker at conferences like Spring I/O, contributing insights on production-ready applications and performance optimizations.

Abstract

This article explores Spring’s mechanisms for dynamic configuration reloads in Boot applications, enabling runtime updates without restarts. It delineates reloadable elements like logging configurations, @ConfigurationProperties beans, and @RefreshScope-annotated components. The analysis covers trigger mechanisms, supported property sources, and considerations for production deployment, including Kubernetes integrations and potential pitfalls.

Foundations of Dynamic Configuration in Spring

Configuration in Spring Boot applications is environment-specific, allowing a single build to adapt via external property sources like files, classpath resources, or remote servers. Traditionally read at startup, changes necessitate restarts, leading to downtime, loss of in-memory state (e.g., caches), and JVM warm-up delays, which can extend from seconds to hours for complex integrations.

Spring Cloud Context introduces reload capabilities, exposing writable actuator endpoints for ephemeral updates and refresh triggers for persistent sources. Posting to the /actuator/env endpoint rebinds @ConfigurationProperties beans and updates logging levels, though changes revert on restart. The /actuator/refresh endpoint, when triggered, reloads external configurations, rebinding properties without full context restarts.

Demo applications illustrate this: a simple MVC controller injects mutable and immutable @ConfigurationProperties classes, demonstrating value updates via getters to ensure visibility.

Trigger Mechanisms and Reloadable Components

Reloads can be manual (POST to /actuator/refresh) or automated via change detection in property sources. @ConfigurationProperties beans rebind automatically, but direct field access in mutable classes may cache stale values—always use getters.

@RefreshScope proxies beans, destroying and recreating them on refresh, useful for stateful components like data sources. However, it incurs overhead and requires careful management to avoid disrupting dependencies.

Logging configurations reload dynamically, altering levels without restarts. @Value annotations, while injectable, do not rebind automatically unless scoped.

Code for enabling refresh:

@SpringBootApplication
@RefreshScope  // Optional for specific beans
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

Supported Property Sources and Kubernetes Integrations

Property sources vary in reload support: file-based (e.g., application.properties) require manual triggers, while remote sources like Consul enable automatic detection via polling (e.g., every 30 seconds).

In Kubernetes, ConfigMaps and Secrets mount as files or environment variables. Spring Cloud Kubernetes Config Reload detects changes, triggering refreshes. Configuration involves enabling reload mode (e.g., polling) and setting intervals.

Example properties:

spring.cloud.kubernetes.config.enabled=true
spring.cloud.kubernetes.reload.enabled=true
spring.cloud.kubernetes.reload.mode=polling
spring.cloud.kubernetes.reload.period=30s

Delays in propagation (e.g., 30+ seconds) necessitate tuning to avoid partial updates.

Practical Considerations and Best Practices

Dynamic reloads suit credential rotations or feature flags but require securing actuators to prevent denial-of-service. Avoid Hikari for refresh-scoped data sources due to connection issues; alternatives like Tomcat JDBC work better.

CRaC (Checkpoint/Restore) combines with reloads for fast startups with dynamic configs, but GraalVM is unsupported. Validate via /actuator/env and /actuator/configprops; test for binding errors.

In conclusion, runtime updates enhance availability and efficiency, demanding rigorous testing to mitigate risks like incomplete propagations.

Links:

PostHeaderIcon [SpringIO2025] Modern Authentication Demystified: A Deep Dive into Spring Security’s Latest Innovations @ Spring IO

Lecturer

Andreas Falk is an Executive Consultant at CGI, specializing in software architecture, application and cloud security, and identity and access management (IAM). As an iSAQB Certified Architect, security expert, trainer, and public speaker, he has over 25 years of experience in enterprise application development. Falk is renowned for his contributions to Spring Security, including workshops on reactive security and presentations on modern authentication mechanisms.

Abstract

This article provides an in-depth analysis of recent advancements in Spring Security, focusing on features introduced from version 6.3 onward. It examines compromised password checking, OAuth token exchange, one-time token login, and passkey authentication, alongside improvements in filter chain management and emerging specifications like Demonstrating Proof of Possession (DPoP). The discussion elucidates implementation strategies, security implications, and best practices for integrating these innovations into Spring-based applications.

Enhancements in Password Management and Token Handling

Spring Security 6.3 introduces compromised password checking, leveraging Troy Hunt’s Have I Been Pwned API to validate passwords against known breaches. Passwords are hashed before transmission, ensuring privacy. This feature integrates into password policies, enforcing minimum lengths (e.g., 12 characters) and maximums (up to 100), while permitting diverse characters, including emojis, to enhance security without undue restrictions.

Implementation involves registering a CompromisedPasswordChecker bean:

@Bean
public CompromisedPasswordChecker compromisedPasswordChecker() {
    return new HaveIBeenPwnedRestApiPasswordChecker();
}

This checker can be invoked in registration endpoints or policy validators, rejecting compromised inputs like “password” while accepting stronger alternatives.

OAuth token exchange addresses scenarios with multiple microservices, reducing attack surfaces by exchanging short-lived JSON Web Tokens (JWTs) for domain-specific ones. A client obtains a JWT, exchanges it at the authorization server for a scoped token, minimizing risks in high-stakes services like payments versus low-risk ones like recommendations.

Advanced Authentication Mechanisms: One-Time Tokens and Passkeys

One-time tokens (OTT) secure sensitive actions, such as password resets or transaction approvals, without persistent sessions. Spring Security supports OTT generation and validation, configurable via beans like OneTimeTokenService. Users receive tokens via email or SMS, granting temporary access.

Passkeys, based on WebAuthn and FIDO2, offer passwordless, phishing-resistant authentication using biometric or hardware keys. Spring Security’s PasskeyAuthenticationProvider integrates seamlessly, supporting registration and authentication flows. Devices like YubiKeys or smartphones generate public-private key pairs, with public keys stored server-side.

Example configuration:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests(authz -> authz
            .anyRequest().authenticated()
        )
        .passkey(passkey -> passkey
            .passkeyAuthenticationProvider(passkeyAuthenticationProvider())
        );
    return http.build();
}

This enables biometric logins, eliminating passwords and enhancing usability.

Filter Chain Improvements and Emerging Specifications

Spring Security’s filter chain, often a source of configuration errors, now includes error detection for misordered chains in version 6.3+. Annotations like @Order ensure proper sequencing, preventing silent failures.

Version 6.4 adds OAuth2 support for the RestClient, automatic bearer token inclusion, and OpenSAML 5 for SAML assertions. Kotlin enhancements improve pre/post-filter annotations.

Demonstrating Proof of Possession (DPoP) in 6.5 binds tokens cryptographically to clients, thwarting theft in single-page applications (SPAs). Mutual TLS complements this for server-side frameworks. JWT profiles specify token types (e.g., access tokens), ensuring intended usage.

Pushed Authorization Requests (PAR) secure initial OAuth flows by posting parameters first, receiving a unique link devoid of sensitive data.

Implications for Secure Application Development

These innovations mitigate root causes of breaches—weak passwords and token vulnerabilities—promoting passwordless paradigms. Token exchange and DPoP reduce attack surfaces in microservices architectures. However, developers must address persistence for passkeys (e.g., database storage) and secure actuator endpoints.

Future versions (7.0+) mandate lambda DSL for configurations, removing deprecated code and defaulting to Proof Key for Code Exchange (PKCE) for all clients. Authorization server advancements, like multi-tenancy and backend-for-frontend patterns, facilitate scalable, secure ecosystems.

In conclusion, Spring Security’s evolution empowers developers to build resilient, user-friendly authentication systems, aligning with modern threats and standards.

Links:

PostHeaderIcon [SpringIO2025] A cloud cost saving journey: Strategies to balance CPU for containerized JAVA workloads in K8s

Lecturer

Laurentiu Marinescu is a Lead Software Engineer at ASML, specializing in building resilient, cloud-native platforms with a focus on full-stack development. With expertise in problem-solving and software craftsmanship, he serves as a tech lead responsible for next-generation cloud platforms at ASML. He holds a degree from the Faculty of Economic Cybernetics and is an advocate for pair programming and emerging technologies. Ajith Ganesan is a System Engineer at ASML with over 15 years of experience in software solutions, particularly in lithography process control applications. His work emphasizes data platform requirements and strategy, with a strong interest in AI opportunities. He holds degrees from Eindhoven University of Technology and is passionate about system design and optimization.

Abstract

This article investigates strategies for optimizing CPU resource utilization in Kubernetes environments for containerized Java workloads, emphasizing cost reduction and performance enhancement. It analyzes the trade-offs in resource allocation, including requests and limits, and presents data-driven approaches to minimize idle CPU cycles. Through examination of workload characteristics, scaling mechanisms, and JVM configurations, the discussion highlights practical implementations that balance efficiency, stability, and operational expenses in on-premises deployments.

Contextualizing Cloud Costs and CPU Utilization Challenges

The escalating costs of cloud infrastructure represent a significant challenge for organizations deploying containerized applications. Annual expenditures on cloud services have surpassed $600 billion, with many entities exceeding budgets by over 17%. In Kubernetes clusters, average CPU utilization hovers around 10%, even in large-scale environments exceeding 1,000 CPUs, where it reaches only 17%. This underutilization implies that up to 90% of provisioned resources remain idle, akin to maintaining expensive infrastructure on perpetual standby.

The inefficiency stems not from collective oversight but from inherent design trade-offs. Organizations deploy expansive clusters to ensure capacity for peak demands, yet this leads to substantial idle resources. The opportunity lies in reclaiming these for cost savings; even doubling utilization to 20% could yield significant reductions. This requires understanding application behaviors, load profiles, and the interplay between Kubernetes scheduling and Java Virtual Machine (JVM) dynamics.

In simulated scenarios with balanced nodes and containers, tight packing minimizes rollout costs but introduces risks. For instance, upgrading containers sequentially due to limited spare capacity (e.g., 25% headroom) can prevent zero-downtime deployments. Scaling demands may fail due to resource constraints, necessitating cluster expansions that inflate expenses. These examples underscore the need for strategies that optimize utilization without compromising reliability.

Resource Allocation Strategies: Requests, Limits, and Workload Profiling

Effective CPU management in Kubernetes hinges on judicious setting of resource requests and limits. Requests guarantee minimum allocation for scheduling, while limits cap maximum usage to prevent monopolization. For Java workloads, these must align with JVM ergonomics, which adapt heap and thread pools based on detected CPU cores.

Workload profiling is essential, categorizing applications into mission-critical (requiring deterministic latency) and non-critical (tolerant of variability). In practice, reducing requests by up to 75% for critical workloads, counterintuitively, enhanced performance by allowing burstable access to idle resources. Experiments demonstrated halved hardware, energy, and real estate costs, with improved stability.

A binary search query identified optimal requests, but assumptions—such as non-simultaneous peaks—were validated through rigorous testing. For non-critical applications, minimal requests (sharing 99% of resources) maximized utilization. Scaling based on application-specific metrics, rather than default CPU thresholds, proved superior. For example, autoscaling on heap usage or queue sizes avoided premature scaling triggered by garbage collection spikes.

Code example for configuring Kubernetes resources in a Deployment YAML:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: java-app
spec:
  replicas: 3
  template:
    spec:
      containers:
      - name: app
        image: java-app:latest
        resources:
          requests:
            cpu: "500m"  # Reduced request for sharing
          limits:
            cpu: "2"     # Expanded limit for bursts

This configuration enables overcommitment, assuming workload diversity prevents concurrent peaks.

JVM and Application-Level Optimizations for Efficiency

Java workloads introduce unique considerations due to JVM behaviors like garbage collection (GC) and thread management. Default JVM settings often lead to inefficiencies; for instance, GC pauses can spike CPU usage, triggering unnecessary scaling. Tuning collectors (e.g., ZGC for low-latency) and limiting threads reduced contention.

Servlet containers like Tomcat exhibited high overhead; profiling revealed excessive thread creation. Switching to Undertow, with its non-blocking I/O, halved resource usage while maintaining throughput. Reactive applications benefited from Netty, leveraging asynchronous processing for better utilization.

Thread management is critical: unbounded queues in executors caused out-of-memory errors under load. Implementing bounded queues with rejection policies ensured stability. For example:

@Bean
public ThreadPoolTaskExecutor executor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(10);  // Limit threads
    executor.setMaxPoolSize(20);
    executor.setQueueCapacity(50); // Bounded queue
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    return executor;
}

Monitoring tools like Prometheus and Grafana facilitated iterative tuning, adapting to evolving workloads.

Cluster-Level Interventions and Success Metrics

Cluster-wide optimizations complement application-level efforts. Overcommitment, by reducing requests while expanding limits, smoothed resource contention. Pre-optimization graphs showed erratic throttling; post-optimization, latency decreased 10-20%, with 7x more requests handled.

Success hinged on validating assumptions through experiments. Despite risks of simultaneous scaling, diverse workloads ensured viability. Continuous monitoring—via vulnerability scans and metrics—enabled proactive adjustments.

Key metrics included reduced throttling, stabilized performance, and halved costs. Policies at namespace and node levels aligned with overcommitment strategies, incorporating backups for node failures.

Implications for Sustainable Infrastructure Management

Optimizing CPU for Java in Kubernetes demands balancing trade-offs: determinism versus sharing, cost versus performance. Strategies emphasize application understanding, JVM tuning, and adaptive scaling. While mission-critical apps benefit from resource sharing under validated assumptions, non-critical ones maximize efficiency with minimal requests.

Future implications involve AI-driven predictions for peak avoidance, enhancing sustainability by reducing energy consumption. Organizations must iterate: monitor, fine-tune, adapt—treating efficiency as a dynamic goal.

Links:

PostHeaderIcon [SpringIO2025] Real-World AI Patterns with Spring AI and Vaadin by Marcus Hellberg / Thomas Vitale

Lecturer

Marcus Hellberg is the Vice President of AI Research at Vaadin, a company specializing in tools for Java developers to build web applications. As a Java Champion with nearly 20 years of experience in Java and web development, he focuses on integrating AI capabilities into Java ecosystems. Thomas Vitale is a software engineer at Systematic, a Danish software company, with expertise in cloud-native solutions, Java, and AI. He is the author of “Cloud Native Spring in Action” and an upcoming book on developer experience on Kubernetes, and serves as a CNCF Ambassador.

Abstract

This article examines practical patterns for incorporating artificial intelligence into Java applications using Spring AI and Vaadin, transitioning from experimental to production-ready implementations. It analyzes techniques for memory management, guardrails, multimodality, retrieval-augmented generation, tool calling, and agents, with implications for security, user experience, and system integration. Insights emphasize robust, observable AI workflows in on-premises or cloud environments.

Memory Management and Streaming in AI Interactions

Integrating large language models (LLMs) into applications requires addressing their stateless nature, where each interaction lacks inherent context from prior exchanges. Spring AI provides advisors—interceptor-like mechanisms—to augment prompts with conversation history, enabling short-term memory. For instance, a MessageChatMemoryAdvisor retains the last N messages, ensuring continuity without manual tracking.

This pattern enhances user interactions in chat-based interfaces, built here with Vaadin’s component model for server-side Java UIs. A vertical layout hosts message lists and inputs, injecting a ChatClientBuilder to construct clients with advisors. Basic interactions involve prompting the model and appending responses, but for realism, streaming via reactive fluxes improves responsiveness, subscribing to token streams and updating UI progressively.

Code illustration:

ChatClient chatClient = builder.build();
messageInput.addSubmitListener(submitEvent -> {
    String message = submitEvent.getMessage();
    MessageItem userItem = messageList.addMessage("You", message);
    chatClient.stream(new Prompt(message))
        .subscribe(response -> {
            userItem.append(response.getResult().getOutput().getContent());
        });
});

Streaming suits verbose responses, reducing perceived latency, while observability integrations (e.g., OpenTelemetry) trace interactions for debugging nondeterministic behaviors.

Guardrails for Security and Validation

AI workflows must mitigate risks like sensitive data leaks or invalid outputs. Input guardrails intercept prompts, using on-premises models to check for compliance with policies, blocking unauthorized queries (e.g., personal information). Output guardrails validate responses, reprompting for corrections if deserialization fails.

Advisors enable this: a default advisor with a local chat model filters inputs/outputs. For example, querying an address might be blocked if flagged, preventing cloud exposure. This ensures determinism in structured outputs, converting unstructured text to Java objects via JSON instructions.

Implications include privacy preservation in regulated sectors and integration with Spring Security for role-based tool access.

Multimodality and Retrieval-Augmented Generation

LLMs extend beyond text through multimodality, processing images, audio, or videos. Spring AI’s entity methods augment prompts for structured extraction, e.g., parsing attendee details from images into tables for programmatic use.

Retrieval-augmented generation (RAG) combats hallucinations by embedding external data as vectors in stores like PostgreSQL. A RetrievalAugmentationAdvisor retrieves relevant documents via similarity search, augmenting prompts. Customizations allow empty contexts for fallback to model knowledge.

Example:

VectorStore vectorStore = // PostgreSQL vector store
DocumentRetriever retriever = new VectorStoreDocumentRetriever(vectorStore);
RetrievalAugmentationAdvisor advisor = RetrievalAugmentationAdvisor.builder()
    .documentRetriever(retriever)
    .queryAugmentor(QueryAugmentor.contextual().allowEmptyContext(true))
    .build();

This pattern grounds responses in proprietary data, with thresholds controlling retrieval scope.

Tool Calling, Agents, and Dynamic Integrations

Tool calling empowers LLMs as agents, invoking external functions for tasks like database queries. Annotations describe tools, passed to clients for dynamic selection. For products, a service might expose query/update methods:

@Tool(description = "Fetch products from database")
public List<Product> getProducts(@P(description = "Category filter") String category) {
    // Database query
}

Agents orchestrate tools, potentially via Model Context Protocol for external services. Demonstrations include theme generation from screenshots, editing CSS via file system tools, highlighting nondeterminism and the need for safeguards.

In conclusion, these patterns enable production AI, emphasizing modularity, security, and observability for robust Java applications.

Links:

PostHeaderIcon [SpringIO2025] Spring I/O 2025 Keynote

Lecturer

The keynote features Spring leadership: Juergen Hoeller (Framework Lead), Rossen Stoyanchev (Web), Ana Maria Mihalceanu (AI), Moritz Halbritter (Boot), Mark Paluch (Data), Josh Long (Advocate), Mark Pollack (Messaging). Collectively, they steer the Spring portfolio’s technical direction and community engagement.

Abstract

The keynote unveils Spring Framework 7.0 and Boot 4.0, establishing JDK 21 and Jakarta EE 11 as baselines while advancing AOT compilation, virtual threads, structured concurrency, and AI integration. Live demonstrations and roadmap disclosures illustrate how these enhancements—combined with refined observability, web capabilities, and data access—position Spring as the preeminent platform for cloud-native Java development.

Baseline Evolution: JDK 21 and Jakarta EE 11

Spring Framework 7.0 mandates JDK 21, embracing virtual threads for lightweight concurrency and records for immutable data carriers. Jakarta EE 11 introduces the Core Profile and CDI Lite, trimming enterprise bloat. The demonstration showcases a virtual thread-per-request web handler processing 100,000 concurrent connections with minimal heap, contrasting traditional thread pools. This baseline shift enables native image compilation via Spring AOT, reducing startup to milliseconds and memory footprint by 90%.

AOT and Native Image Optimization

Spring Boot 4.0 refines AOT processing through Project Leyden integration, pre-computing bean definitions and proxy classes at build time. Native executables startup in under 50ms, suitable for serverless platforms. The live demo compiles a Kafka Streams application to GraalVM native image, achieving sub-second cold starts and 15MB RSS—transforming deployment economics for event-driven microservices.

AI Integration and Modern Web Capabilities

Spring AI matures with function calling, tool integration, and vector database support. A live-coded agent retrieves beans from a running context to answer natural language queries about application metrics. WebFlux enhances structured concurrency with Schedulers.boundedElastic() replacement via virtual threads, simplifying reactive code. The demonstration contrasts traditional Mono/Flux composition with straightforward sequential logic executing on virtual threads, preserving backpressure while improving readability.

Data, Messaging, and Observability Advancements

Spring Data advances R2DBC connection pooling and Redis Cluster native support. Spring for Apache Kafka 4.0 introduces configurable retry templates and Micrometer metrics out-of-the-box. Unified observability aggregates metrics, traces, and logs: Prometheus exposes 200+ Kafka client metrics, OpenTelemetry correlates spans across HTTP and Kafka, and structured logging propagates MDC context. A Grafana dashboard visualizes end-to-end latency from REST ingress to database commit, enabling proactive incident response.

Community and Future Trajectory

The keynote celebrates Spring’s global community, highlighting contributions to null-safety (JSpecify), virtual thread testing, and AOT hint generation. Planned enhancements include JDK 23 support, Project Panama integration for native memory access, and AI-driven configuration validation. The vision positions Spring as the substrate for the next decade of Java innovation, balancing cutting-edge capabilities with backward compatibility.

Links: