<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="http://feeds.feedblitz.com/feedblitz_rss.xslt"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:webfeeds="http://webfeeds.org/rss/1.0"  xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">
<channel>
	<title>Baeldung</title>
	<atom:link href="https://www.baeldung.com/feed" rel="self" type="application/rss+xml" />
	<link>https://www.baeldung.com</link>
	<description>Java, Spring and Web Development tutorials</description>
	<lastBuildDate>Sun, 06 Sep 2026 23:19:21 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
<meta xmlns="http://www.w3.org/1999/xhtml" name="robots" content="noindex" />
<item>
<feedburner:origLink>https://www.baeldung.com/spring-ai-mcp-logging</feedburner:origLink>
		<title>MCP Logging in Spring AI</title>
		<link>https://feeds.feedblitz.com/~/968567129/0/baeldung~MCP-Logging-in-Spring-AI</link>
					<comments>https://feeds.feedblitz.com/~/968567129/0/baeldung~MCP-Logging-in-Spring-AI#respond</comments>
		
		<dc:creator><![CDATA[Burak Gökmen]]></dc:creator>
		<pubDate>Sun, 06 Sep 2026 23:19:21 +0000</pubDate>
				<category><![CDATA[Spring AI]]></category>
		<category><![CDATA[MCP]]></category>
		<guid isPermaLink="false">https://www.baeldung.com/?p=204813</guid>
					<description><![CDATA[<img src="https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-11-1024x536.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="max-width:100% !important;height:auto !important;float: left; margin-right: 5px;" fetchpriority="high" /><p>Learn about MCP logging in Spring AI,  both in an MCP server and a client.</p>
<p>The post <a rel="NOFOLLOW" href="https://feeds.feedblitz.com/~/968567129/0/baeldung~MCP-Logging-in-Spring-AI">MCP Logging in Spring AI</a> first appeared on <a rel="NOFOLLOW" href="https://www.baeldung.com">Baeldung</a>.</p><div style="clear:both;padding-top:0.2em;"><a title="Like on Facebook" href="https://feeds.feedblitz.com/_/28/968567129/baeldung"><img height="20" src="https://assets.feedblitz.com/i/fblike20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Pin it!" href="https://feeds.feedblitz.com/_/29/968567129/baeldung,https%3a%2f%2fwww.baeldung.com%2fwp-content%2fuploads%2f2024%2f07%2fJava-Featured-11-1024x536.jpg"><img height="20" src="https://assets.feedblitz.com/i/pinterest20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Post to X.com" href="https://feeds.feedblitz.com/_/24/968567129/baeldung"><img height="20" src="https://assets.feedblitz.com/i/x.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by email" href="https://feeds.feedblitz.com/_/19/968567129/baeldung"><img height="20" src="https://assets.feedblitz.com/i/email20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by RSS" href="https://feeds.feedblitz.com/_/20/968567129/baeldung"><img height="20" src="https://assets.feedblitz.com/i/rss20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a rel="NOFOLLOW" title="View Comments" href="https://www.baeldung.com/spring-ai-mcp-logging#respond"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/comments20.png"></a>&#160;<a title="Follow Comments via RSS" href="https://www.baeldung.com/spring-ai-mcp-logging/feed"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/commentsrss20.png"></a>&#160;</div>]]>
</description>
										<content:encoded><![CDATA[<img src="https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-11-1024x536.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="float: left; margin-right: 5px;" decoding="async" srcset="https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-11-1024x536.jpg 1024w, https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-11-300x157.jpg 300w, https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-11-768x402.jpg 768w, https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-11-100x52.jpg 100w, https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-11.jpg 1200w" sizes="(max-width: 580px) 100vw, 580px" /><h2 id="bd-overview" data-id="overview">1. Overview</h2>
<div class="bd-anchor" id="overview"></div>
<p><a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/java-sdk-model-context-protocol">MCP</a> (Model Context Protocol) is an open-source, <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/cs/rest-vs-rpc#2-rpc">JSON-RPC</a>-based (JavaScript Object Notation-Remote Procedure Call) protocol. It provides a standardized way for AI (Artificial Intelligence) applications to connect to external tools, services, and data sources. Therefore, it eliminates the need for custom integrations.</p>
<p>Logging in MCP gives servers a standardized way to send structured, severity-tagged log messages to clients. This might be especially useful while debugging an MCP server.</p>
<p>In this tutorial, we’ll discuss MCP logging in Spring AI. Firstly, we’ll give brief information about MCP logging. Then, we’ll discuss logging both in an MCP server and client.</p>
<h2 id="bd-basic-information-about-mcp-logging" data-id="basic-information-about-mcp-logging">2. Basic Information About MCP Logging</h2>
<div class="bd-anchor" id="basic-information-about-mcp-logging"></div>
<p>An MCP server pushes log messages to an MCP client as <em>notifications/message</em> JSON-RPC notifications<strong>. This one-way notification message from the server to the client contains a severity level, an optional logger name, and the log message</strong>.</p>
<p>The MCP client can send the <em>logging/setLevel</em> JSON-RPC request to the server to configure log-message verbosity. Consequently, <strong>the server only sends log messages that meet or exceed the requested severity level</strong>. The client can dynamically adjust log verbosity by issuing another <em>logging/setLevel</em>.</p>
<p>An important caveat is that <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://modelcontextprotocol.io/specification/2026-07-28/server/utilities/logging">the July 2026 spec revision (2026-07-28)</a> formally deprecated the logging feature along with other features, in favor of newer mechanisms. However, existing implementations will continue to work for at least a year. It’s recommended that newer implementations migrate to using stderr (standard error) for the stdio (standard input/output) transport and <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/spring-boot-opentelemetry-setup">OpenTelemetry</a> for structured observability.</p>
<h2 id="bd-maven-dependencies" data-id="maven-dependencies">3. Maven Dependencies</h2>
<div class="bd-anchor" id="maven-dependencies"></div>
<p>Let’s start by adding the necessary Maven dependencies to our <em>pom.xml</em>:</p>
<pre><code class="language-java">&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.ai&lt;/groupId&gt;
    &lt;artifactId&gt;spring-ai-starter-mcp-client&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.ai&lt;/groupId&gt;
    &lt;artifactId&gt;spring-ai-starter-mcp-server&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
<p>The <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://mvnrepository.com/artifact/org.springframework.ai/spring-ai-starter-mcp-client">MCP client starter</a> dependency enables a Spring AI application to connect to MCP servers and use their tools. On the other hand, we use the <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://mvnrepository.com/artifact/org.springframework.ai/spring-ai-starter-mcp-server">MCP server starter</a> dependency to turn a Spring Boot application into an MCP server. The dependency lets the application expose tools and resources to other LLM (Large Language Model) applications.</p>
<p>We use the <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://mvnrepository.com/artifact/org.springframework.ai/spring-ai-bom">Spring AI BOM</a> (Bill of Materials) to avoid the risk of version conflicts between Spring AI dependencies:</p>
<pre><code class="language-java">&lt;dependencyManagement&gt;
    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.ai&lt;/groupId&gt;
            &lt;artifactId&gt;spring-ai-bom&lt;/artifactId&gt;
            &lt;version&gt;${spring-ai.version}&lt;/version&gt;
            &lt;type&gt;pom&lt;/type&gt;
            &lt;scope&gt;import&lt;/scope&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;
&lt;/dependencyManagement&gt;
</code></pre>
<p><strong>Therefore, we don’t have to specify the versions of MCP starters explicitly</strong>.</p>
<h2 id="bd-logging-on-the-server-side" data-id="logging-on-the-server-side">4. Logging on the Server Side</h2>
<div class="bd-anchor" id="logging-on-the-server-side"></div>
<p>Let’s start by discussing logging in the MCP server. <strong>The server we’ll discuss exposes a tool that an MCP client can run whenever a user requests a password strength evaluation</strong>.</p>
<h3 id="bd-1-tool-implementation" data-id="1-tool-implementation">4.1. Tool Implementation</h3>
<div class="bd-anchor" id="1-tool-implementation"></div>
<p>The <em>PasswordStrengthService</em> class holds the actual tool implementation, the <em>checkStrength()</em> method exposed by the <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/spring-ai-mcp-annotations#exposing-functionality-with-mcptool"><em>@McpTool</em></a> server annotation. It’s a Spring bean since it’s annotated with <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/spring-component-repository-service#service"><em>@Service</em></a>:</p>
<pre><code class="language-java">@Service
public class PasswordStrengthService {
    ...
    @McpTool(name = "check_password_strength", 
      description = "Evaluates password strength and returns a score with recommendations.")
    public PasswordStrengthResult checkStrength(
        @McpToolParam(description = "The password to evaluate", required = true) String password,
	  McpSyncRequestContext ctx) {
        ...
    }
}</code></pre>
<p>The <em>checkStrength()</em> method, i.e., the tool, takes two parameters. The first one is the password to be evaluated. The tool checks whether the password is at least 12 characters long, contains at least one uppercase letter, and contains at least one digit. It also checks whether the password matches common passwords such as <em>&#8220;123456&#8221;</em> or <em>&#8220;qwerty&#8221;</em>.</p>
<p>The second parameter is of type <em>McpSyncRequestContext</em>, which we’ll use for logging. The Spring AI MCP annotations framework injects it automatically. <strong>An MCP server uses an object of this special type to log messages</strong>. It doesn’t write the log messages directly to the stdout (standard output) stream. Instead, it packages them into standard MCP protocol notification payloads and sends them over the transport layer, like stdio or SSE (Server-Sent Events), to the client. We use the stdio transport in our example.</p>
<p><strong>The <em>McpSyncRequestContext</em> interface provides several logging methods such as <em>debug()</em>, <em>info()</em>, <em>warn()</em>, and <em>error()</em></strong>. For example, if the password matches a common password, we log it using the <em>ctx.error(&#8220;Password found in common-password list&#8221;)</em> call in our example:</p>
<pre><code class="language-java">if (COMMON_PASSWORDS.contains(password.toLowerCase())) {
    ctx.error("Password found in common-password list");
    issues.add("commonly used");
}
</code></pre>
<p>Therefore, <strong>on the server side, we can choose the logging level per call</strong>.</p>
<h3 id="bd-2-mcp-server" data-id="2-mcp-server">4.2. MCP Server</h3>
<div class="bd-anchor" id="2-mcp-server"></div>
<p><strong>The <em>McpLoggingServerApplication</em> class provides the Spring Boot entry point for the MCP server process</strong>:</p>
<pre><code class="language-java">@SpringBootApplication
public class McpLoggingServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(McpLoggingServerApplication.class, args);
    }
}
</code></pre>
<p>When the server process starts, Spring AI’s MCP server auto-configuration scans for methods annotated with<em> @McpTool</em>. Then, it registers and exposes the detected tools over the stdio transport. A connected client can therefore discover and call them.</p>
<p><strong>We need to enable the stdio transport in the server’s configuration file</strong>, <em>application-server.properties</em>:</p>
<pre><code class="language-java">spring.ai.mcp.server.stdio=true</code></pre>
<p>Besides, we must prevent the server application from printing logs to stdout. <strong>The stdout stream is dedicated to exchanging the MCP protocol’s JSON-RPC messages</strong>:</p>
<pre><code class="language-java">spring.main.web-application-type=none
spring.main.banner-mode=off
logging.pattern.console=
</code></pre>
<p>Setting <em>spring.main.web-application-type</em> to <em>none</em> prevents Spring Boot from starting a web server. This means that no web-related startup logs are emitted. Setting <em>spring.main.banner-mode</em> to <em>off</em> suppresses the Spring Boot banner that is normally printed to stdout on startup. Finally, <em>logging.pattern.console=</em> strips the pattern formatting in logs.</p>
<h2 id="bd-handling-logs-on-the-client-side" data-id="handling-logs-on-the-client-side">5. Handling Logs on the Client Side</h2>
<div class="bd-anchor" id="handling-logs-on-the-client-side"></div>
<p>Let’s now discuss logging in the MCP client.</p>
<h3 id="bd-1-mcp-client" data-id="1-mcp-client">5.1. MCP Client</h3>
<div class="bd-anchor" id="1-mcp-client"></div>
<p><strong>Similar to <em>McpLoggingServerApplication</em>, the <em>McpLoggingClientApplication</em> class provides the Spring Boot entry point for the MCP client process</strong>:</p>
<pre><code class="language-java">@SpringBootApplication
public class McpLoggingClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(McpLoggingClientApplication.class, args);
    }
}
</code></pre>
<p>On startup, the client boot starter reads the <em>spring.ai.mcp.client.stdio.connections.*</em> properties in the <em>application-client.properties</em> file and launches the server as a child process. For example, the configuration property <em>spring.ai.mcp.client.stdio.connections.password-strength-logging-server.args</em> specifies which arguments the client passes to the server when spawning it. <strong>The <em>stdio</em> segment in these configuration properties selects the stdio transport for the named connection, <em>password-strength-logging-server</em></strong>.</p>
<p>The <em>spring.ai.mcp.client.type=SYNC</em> configuration property selects the synchronous client implementation, i.e., <em>McpSyncClient</em>. Therefore, client calls block until the server responds.</p>
<h3 id="bd-2-logging-handler" data-id="2-logging-handler">5.2. Logging Handler</h3>
<div class="bd-anchor" id="2-logging-handler"></div>
<p><strong>To capture the notification logs sent by the server on the client side, we have to use the <em>@McpLogging</em> client annotation</strong>:</p>
<pre><code class="language-java">@Component public class PasswordStrengthMcpClientHandlers {
    ...
    @McpLogging(clients = "password-strength-logging-server")
    public void handleLoggingMessage(McpSchema.LoggingMessageNotification notification) {
        LOGGER.info("Received server logging notification [{}]: {}"
          , notification.level(), notification.data());
        receivedLogs.add(notification);
    }
    ...
} </code></pre>
<p><em>@McpLogging</em> is a Spring AI MCP annotation that registers a method as a notification handler for a specific MCP client connection. The handler’s name is <em>handleLoggingMessage()</em> in our example. The connection’s name is <em>password-strength-logging-server</em>, which matches the ID configured for the stdio connection in the server’s configuration file, <em>application-server.properties</em>:</p>
<pre><code class="language-java">spring.ai.mcp.server.name= password-strength-logging-server</code></pre>
<p><strong>The handler’s parameter, <em>notification</em>, is the deserialized payload of an MCP logging notification</strong>. Its <em>level()</em> method prints the log severity (e.g., <em>DEBUG</em>, <em>ERROR</em>). Its <em>data()</em> method, on the other hand, contains the actual log sent by the server.</p>
<p>We’ve already seen that the server can choose the logging level per call since the <em>McpSyncRequestContext</em> interface provides several logging methods such as <em>warn()</em> and <em>error()</em>. However, the client can set the server’s logging level. Therefore, <strong>the server can filter out notification messages at a specific level and below</strong>:</p>
<pre><code class="language-java">mcpSyncClient.setLoggingLevel(McpSchema.LoggingLevel.WARNING);</code></pre>
<p>Here, this client-side call instructs the server to filter out anything below <em>WARNING</em>. For example, the server doesn’t send notifications with <em>DEBUG</em> or <em>INFO</em> levels.</p>
<h2 id="bd-an-example" data-id="an-example">6. An Example</h2>
<div class="bd-anchor" id="an-example"></div>
<p>When we send <em>&#8220;</em><em>weak</em><em>&#8220;</em> as the password to be evaluated by the MCP server, the logging handler prints the following list of log messages:</p>
<pre><code class="language-bash">Received server logging notification [WARNING]: Password shorter than recommended 12 characters
Received server logging notification [WARNING]: Password missing uppercase letters
Received server logging notification [WARNING]: Password missing digits
Received server logging notification [INFO]: Final score: 25
</code></pre>
<p>This is expected since the password <em>&#8220;</em><em>weak</em><em>&#8220;</em> is shorter than 12 characters and doesn’t contain uppercase letters and digits. It only satisfies the common password criterion. Therefore, it gets a final score of <em>25</em> out of <em>100</em>.</p>
<p>Our example consists of only an MCP server and client. But if the client were an LLM-orchestrated agent, a list of transactions similar to the following list would have occurred:</p>
<ul>
<li>User -&gt; Client: The user asks the following question: <em>&#8220;How strong is the password weak?&#8221;</em></li>
<li>Client -&gt; LLM: The client sends the user’s message to the LLM together with the tool information received from the MCP server at session start</li>
<li>LLM -&gt; Client: The LLM doesn’t call the server itself. Instead, it returns a response that indicates that it wants to call the tool <em>checkStrength</em> with <em>password=weak</em></li>
<li>Client -&gt; Server: The client issues the actual JSON-RPC request over the stdio transport</li>
<li>Server -&gt; Client: The server returns the response we saw earlier as a JSON-RPC response</li>
<li>Client -&gt; LLM: The client forwards the tool result back to the conversation and asks the LLM to continue</li>
<li>LLM –&gt; User: The LLM generates a natural-language answer synthesizing the structured result from the tool, something like: <em>&#8220;That password is very weak. It&#8217;s only 4 characters long — you&#8217;ll need at least 12 — and it doesn&#8217;t include any uppercase letters or digits. I can make suggestions if you like.</em><em>&#8220;</em></li>
</ul>
<h2 id="bd-conclusion" data-id="conclusion">7. Conclusion</h2>
<div class="bd-anchor" id="conclusion"></div>
<p>In this article, we discussed MCP logging in Spring AI. Firstly, we learned about how logging works within the MCP ecosystem. Then, we examined the details of MCP logging on an MCP server evaluating a password’s strength. We saw that we can use the logging methods in the <em>McpSyncRequestContext</em> interface to print log messages at different severity levels.</p>
<p>Then, we discussed logging on the MCP client. We learned that we can use a handler annotated with <em>@McpLogging</em> for handling logs. Finally, we saw an example using the server and client and discussed the interaction between an LLM, the client, and the server when a user asks for an evaluation of a password’s strength.</p>
<p>As usual, the complete source code for the examples is available <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/eugenp/tutorials/tree/master/spring-ai-modules/spring-ai-mcp-logging">over on GitHub</a>.</p><p>The post <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/spring-ai-mcp-logging">MCP Logging in Spring AI</a> first appeared on <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com">Baeldung</a>.</p><Img align="left" border="0" height="1" width="1" alt="" style="border:0;float:left;margin:0;padding:0;width:1px!important;height:1px!important;" hspace="0" src="https://feeds.feedblitz.com/~/i/968567129/0/baeldung">
<div style="clear:both;padding-top:0.2em;"><a title="Like on Facebook" href="https://feeds.feedblitz.com/_/28/968567129/baeldung"><img height="20" src="https://assets.feedblitz.com/i/fblike20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Pin it!" href="https://feeds.feedblitz.com/_/29/968567129/baeldung,https%3a%2f%2fwww.baeldung.com%2fwp-content%2fuploads%2f2024%2f07%2fJava-Featured-11-1024x536.jpg"><img height="20" src="https://assets.feedblitz.com/i/pinterest20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Post to X.com" href="https://feeds.feedblitz.com/_/24/968567129/baeldung"><img height="20" src="https://assets.feedblitz.com/i/x.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by email" href="https://feeds.feedblitz.com/_/19/968567129/baeldung"><img height="20" src="https://assets.feedblitz.com/i/email20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by RSS" href="https://feeds.feedblitz.com/_/20/968567129/baeldung"><img height="20" src="https://assets.feedblitz.com/i/rss20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a rel="NOFOLLOW" title="View Comments" href="https://www.baeldung.com/spring-ai-mcp-logging#respond"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/comments20.png"></a>&#160;<a title="Follow Comments via RSS" href="https://www.baeldung.com/spring-ai-mcp-logging/feed"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/commentsrss20.png"></a>&#160;</div>]]>
</content:encoded>
					
					<wfw:commentRss>https://feeds.feedblitz.com/~/968567129/0/baeldung~MCP-Logging-in-Spring-AI/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<webfeeds:featuredImage>https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-11-150x150.jpg</webfeeds:featuredImage></item>
<item>
<feedburner:origLink>https://www.baeldung.com/java-weekly-662</feedburner:origLink>
		<title>Java Weekly, Issue 662</title>
		<link>https://feeds.feedblitz.com/~/968560595/0/baeldung~Java-Weekly-Issue</link>
					<comments>https://feeds.feedblitz.com/~/968560595/0/baeldung~Java-Weekly-Issue#respond</comments>
		
		<dc:creator><![CDATA[baeldung]]></dc:creator>
		<pubDate>Sun, 06 Sep 2026 09:25:19 +0000</pubDate>
				<category><![CDATA[Weekly Review]]></category>
		<category><![CDATA[no-ads]]></category>
		<category><![CDATA[no-after-post]]></category>
		<category><![CDATA[no-before-post]]></category>
		<category><![CDATA[no-optins]]></category>
		<guid isPermaLink="false">https://www.baeldung.com/?p=204825</guid>
					<description><![CDATA[<img src="https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="max-width:100% !important;height:auto !important;float: left; margin-right: 5px;" /><p>The three Loom pillars and, more importantly, the tooling. Plus IntelliJ IDEA Conf 2026 getting close.</p>
<p>The post <a rel="NOFOLLOW" href="https://feeds.feedblitz.com/~/968560595/0/baeldung~Java-Weekly-Issue">Java Weekly, Issue 662</a> first appeared on <a rel="NOFOLLOW" href="https://www.baeldung.com">Baeldung</a>.</p><div style="clear:both;padding-top:0.2em;"><a title="Like on Facebook" href="https://feeds.feedblitz.com/_/28/968560595/baeldung"><img height="20" src="https://assets.feedblitz.com/i/fblike20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Pin it!" href="https://feeds.feedblitz.com/_/29/968560595/baeldung,https%3a%2f%2fwww.baeldung.com%2fwp-content%2fuploads%2f2016%2f10%2fsocial-Weekly-Reviews-4.jpg"><img height="20" src="https://assets.feedblitz.com/i/pinterest20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Post to X.com" href="https://feeds.feedblitz.com/_/24/968560595/baeldung"><img height="20" src="https://assets.feedblitz.com/i/x.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by email" href="https://feeds.feedblitz.com/_/19/968560595/baeldung"><img height="20" src="https://assets.feedblitz.com/i/email20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by RSS" href="https://feeds.feedblitz.com/_/20/968560595/baeldung"><img height="20" src="https://assets.feedblitz.com/i/rss20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a rel="NOFOLLOW" title="View Comments" href="https://www.baeldung.com/java-weekly-662#respond"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/comments20.png"></a>&#160;<a title="Follow Comments via RSS" href="https://www.baeldung.com/java-weekly-662/feed"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/commentsrss20.png"></a>&#160;</div>]]>
</description>
										<content:encoded><![CDATA[<img src="https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="float: left; margin-right: 5px;" decoding="async" loading="lazy" srcset="https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4.jpg 952w, https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4-300x157.jpg 300w, https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4-768x402.jpg 768w" sizes="auto, (max-width: 580px) 100vw, 580px" /><h2 style="text-align: left;" id="bd-spring-and-java" data-id="spring-and-java">1.<strong> Spring and Java</strong></h2>
<div class="bd-anchor" id="spring-and-java"></div>
<p><strong><a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.jetbrains.com/idea/2026/08/project-loom-in-intellij-idea-virtual-threads-scoped-values-and-structured-concurrency/">&gt;&gt; Project Loom in IntelliJ IDEA: Virtual Threads, Scoped Values, and Structured Concurrency</a></strong> [<span style="color: #993300;">jetbrains.com</span>]</p>
<p>A hands-on tour of the three Loom pillars: virtual threads, scoped values, and structured concurrency. This show you exactly what each feature buys and the tooling coming along with it. Solid stuff.</p>
<h4><strong>Also worth reading:</strong></h4>
<ul>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.infoq.com/articles/pqc-in-spring-boot/" target="_blank" rel="noopener"><strong>Post-Quantum Cryptography in Spring Boot: Four Patterns You Can Ship This Sprint</strong></a> [<span style="color: #800000;">infoq.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.jetbrains.com/idea/2026/09/the-grails-plugin-has-a-new-home-apache-grails/" target="_blank" rel="noopener"><strong>The Grails Plugin Has a New Home: Apache Grails</strong></a> [<span style="color: #800000;">jetbrains.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.javaspecialists.eu/archive/Issue337-Module-Imports.html" target="_blank" rel="noopener"><strong>Module Imports</strong></a> [<span style="color: #800000;">javaspecialists.eu</span>]</li>
</ul>
<h4><strong>Webinars and presentations:</strong></h4>
<ul>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://inside.java/2026/08/27/podcast-068/" target="_blank" rel="noopener"><strong>Episode 68 “Operator Overloading with Type Classes” [AtA]</strong></a> [<span style="color: #800000;">inside.java</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://spring.io/blog/2026/08/27/a-bootiful-podcast-joe-grandja" target="_blank" rel="noopener"><strong>A Bootiful Podcast: Joe Grandja on Spring Authorization Server, OAuth, and so much more</strong></a> [<span style="color: #800000;">spring.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://spring.io/blog/2026/08/31/spring-office-hours-podcast-S5E21" target="_blank" rel="noopener"><strong>Spring Office Hours Podcast: S5E21 &#8211; Pro Spring Boot 4 with Felipe Gutierrez</strong></a> [<span style="color: #800000;">spring.io</span>]</li>
</ul>
<h4><strong>Time to upgrade:</strong></h4>
<ul>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.jetbrains.com/idea/2026/09/intellij-idea-2026-2-2/" target="_blank" rel="noopener"><strong>IntelliJ IDEA 2026.2.2 Is Out!</strong></a> [<span style="color: #800000;">jetbrains.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.jetbrains.com/kotlin/2026/08/compose-multiplatform-1-12-0/" target="_blank" rel="noopener"><strong>Compose Multiplatform 1.12.0 Released</strong></a> [<span style="color: #800000;">jetbrains.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://quarkus.io/blog/quarkus-flow-1-0-0-released/" target="_blank" rel="noopener"><strong>Quarkus Flow 1.0.0 released</strong></a> [<span style="color: #800000;">quarkus.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://quarkus.io/blog/quarkus-3-39-released/" target="_blank" rel="noopener"><strong>Quarkus 3.39 &#8211; Post-quantum cryptography support in TLS registry</strong></a> [<span style="color: #800000;">quarkus.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.wildfly.org/news/2026/08/27/WildFly-41-0-1-is-released/" target="_blank" rel="noopener"><strong>WildFly 41.0.1 is released!</strong></a> [<span style="color: #800000;">wildfly.org</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/jhipster/generator-jhipster/releases/tag/v9.3.0" target="_blank" rel="noopener"><strong>JHipster v9.3.0</strong></a> [<span style="color: #800000;">github.com/jhipster</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/eclipse-vertx/vert.x/releases/tag/4.5.33" target="_blank" rel="noopener"><strong>Vert.x 4.5.33</strong></a> and <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/eclipse-vertx/vert.x/releases/tag/5.1.7" target="_blank" rel="noopener"><strong>5.1.7</strong></a> [<span style="color: #800000;">github.com/eclipse-vertx</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/elastic/elasticsearch/releases/tag/v9.4.6" target="_blank" rel="noopener"><strong>Elasticsearch 9.4.6</strong></a> and <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/elastic/elasticsearch/releases/tag/v8.19.21" target="_blank" rel="noopener"><strong>8.19.21</strong></a> [<span style="color: #800000;">github.com/elastic</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/Netflix/zuul/releases/tag/v4.1.5" target="_blank" rel="noopener"><strong>Zuul v4.1.5</strong></a> [<span style="color: #800000;">github.com/Netflix</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/micronaut-projects/micronaut-core/releases/tag/v5.1.13" target="_blank" rel="noopener"><strong>Micronaut Core 5.1.13</strong></a> [<span style="color: #800000;">github.com/micronaut-projects</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/helidon-io/helidon/releases/tag/4.5.4" target="_blank" rel="noopener"><strong>Helidon 4.5.4</strong></a> [<span style="color: #800000;">github.com/helidon-io</span>]</li>
</ul>
<h2 style="text-align: left;" id="bd-technical-amp-musings" data-id="technical-amp-musings">2.<strong> Technical &amp; Musings</strong></h2>
<div class="bd-anchor" id="technical-amp-musings"></div>
<p><strong><a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://martinfowler.com/rachels-ramblings/code-review.html">&gt;&gt; Maybe We Shouldn’t Be Reviewing All This Code</a></strong> [<span style="color: #993300;">martinfowler.com</span>]</p>
<p>PR sizes really are ballooning, but the fix isn’t automating review away, it’s noticing that we’d been leaning on review to carry things that belongs much earlier in the flow. An interesting weekend read.</p>
<h4><strong>Also worth reading:</strong></h4>
<ul>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.frankel.ch/ai-assisted-genealogy/" target="_blank" rel="noopener"><strong>AI-assisted genealogy</strong></a> [<span style="color: #800000;">frankel.ch</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.allthingsdistributed.com/2026/08/duckdb-and-the-changing-physics-of-analytics.html" target="_blank" rel="noopener"><strong>DuckDB and the changing physics of analytics</strong></a> [<span style="color: #800000;">allthingsdistributed.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://martinfowler.com/articles/making-data-ready-for-agentic-ai.html" target="_blank" rel="noopener"><strong>Making Your Data Ready for Agentic AI</strong></a> [<span style="color: #800000;">martinfowler.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://martinfowler.com/articles/exploring-gen-ai/an-accidental-blackboard.html" target="_blank" rel="noopener"><strong>An Accidental Blackboard</strong></a> [<span style="color: #800000;">martinfowler.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://netflixtechblog.com/maps-netflixs-multimodal-asset-personalization-at-scale-32f96320785e" target="_blank" rel="noopener"><strong>MAPS: Netflix’s Multimodal Asset Personalization at Scale</strong></a> [<span style="color: #800000;">netflixtechblog.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://netflixtechblog.medium.com/running-apache-spark-experiments-in-my-sleep-and-on-a-plane-588816b6b5f8" target="_blank" rel="noopener"><strong>Running Apache Spark experiments in my sleep (and on a plane)</strong></a> [<span style="color: #800000;">netflixtechblog.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.codinghorror.com/you-cant-vibe-code-love/" target="_blank" rel="noopener"><strong>You Can’t “Vibe Code” Love</strong></a> [<span style="color: #800000;">codinghorror.com</span>]</li>
</ul>
<h2 style="text-align: left;" id="bd-pick-of-the-week" data-id="pick-of-the-week">3.<strong> Pick of the Week</strong></h2>
<div class="bd-anchor" id="pick-of-the-week"></div>
<p>And from last week, the <strong>free, fully virtual conference</strong> for our ecosystem:</p>
<p><strong><a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/jetbrains-conf-jw-agof" rel="sponsored ">&gt;&gt; IntelliJ IDEA Conf 2026</a></strong> [<span style="color: #993300;">jetbrains.com</span>]</p><p>The post <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/java-weekly-662">Java Weekly, Issue 662</a> first appeared on <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com">Baeldung</a>.</p><Img align="left" border="0" height="1" width="1" alt="" style="border:0;float:left;margin:0;padding:0;width:1px!important;height:1px!important;" hspace="0" src="https://feeds.feedblitz.com/~/i/968560595/0/baeldung">
<div style="clear:both;padding-top:0.2em;"><a title="Like on Facebook" href="https://feeds.feedblitz.com/_/28/968560595/baeldung"><img height="20" src="https://assets.feedblitz.com/i/fblike20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Pin it!" href="https://feeds.feedblitz.com/_/29/968560595/baeldung,https%3a%2f%2fwww.baeldung.com%2fwp-content%2fuploads%2f2016%2f10%2fsocial-Weekly-Reviews-4.jpg"><img height="20" src="https://assets.feedblitz.com/i/pinterest20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Post to X.com" href="https://feeds.feedblitz.com/_/24/968560595/baeldung"><img height="20" src="https://assets.feedblitz.com/i/x.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by email" href="https://feeds.feedblitz.com/_/19/968560595/baeldung"><img height="20" src="https://assets.feedblitz.com/i/email20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by RSS" href="https://feeds.feedblitz.com/_/20/968560595/baeldung"><img height="20" src="https://assets.feedblitz.com/i/rss20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a rel="NOFOLLOW" title="View Comments" href="https://www.baeldung.com/java-weekly-662#respond"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/comments20.png"></a>&#160;<a title="Follow Comments via RSS" href="https://www.baeldung.com/java-weekly-662/feed"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/commentsrss20.png"></a>&#160;</div>]]>
</content:encoded>
					
					<wfw:commentRss>https://feeds.feedblitz.com/~/968560595/0/baeldung~Java-Weekly-Issue/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<webfeeds:featuredImage>https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4-150x150.jpg</webfeeds:featuredImage></item>
<item>
<feedburner:origLink>https://www.baeldung.com/java-embabel-streaming-objects</feedburner:origLink>
		<title>LLM Streaming in the Embabel Agentic AI Framework</title>
		<link>https://feeds.feedblitz.com/~/968282549/0/baeldung~LLM-Streaming-in-the-Embabel-Agentic-AI-Framework</link>
					<comments>https://feeds.feedblitz.com/~/968282549/0/baeldung~LLM-Streaming-in-the-Embabel-Agentic-AI-Framework#respond</comments>
		
		<dc:creator><![CDATA[Igor Dayen]]></dc:creator>
		<pubDate>Wed, 26 Aug 2026 22:02:05 +0000</pubDate>
				<category><![CDATA[Spring AI]]></category>
		<category><![CDATA[Embabel]]></category>
		<guid isPermaLink="false">https://www.baeldung.com/java-embabel-streaming-objects</guid>
					<description><![CDATA[<img src="https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-14-1024x536.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="Two co-workers; the one on the left is holding up a magnifying glass to an artistic representation of files and folders. The one on the right is looking at a projection of some Java code. In between them there is an office plant. To the left of the image are the words &quot;Java on Baeldung&quot;" style="max-width:100% !important;height:auto !important;float: left; margin-right: 5px;" loading="lazy" /><p>Learn to how to use Embabel AI to automatically parse objects as the data arrive in a stream from the large language model you use in your application.</p>
<p>The post <a rel="NOFOLLOW" href="https://feeds.feedblitz.com/~/968282549/0/baeldung~LLM-Streaming-in-the-Embabel-Agentic-AI-Framework">LLM Streaming in the Embabel Agentic AI Framework</a> first appeared on <a rel="NOFOLLOW" href="https://www.baeldung.com">Baeldung</a>.</p><div style="clear:both;padding-top:0.2em;"><a title="Like on Facebook" href="https://feeds.feedblitz.com/_/28/968282549/baeldung"><img height="20" src="https://assets.feedblitz.com/i/fblike20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Pin it!" href="https://feeds.feedblitz.com/_/29/968282549/baeldung,https%3a%2f%2fwww.baeldung.com%2fwp-content%2fuploads%2f2024%2f07%2fJava-Featured-14-1024x536.jpg"><img height="20" src="https://assets.feedblitz.com/i/pinterest20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Post to X.com" href="https://feeds.feedblitz.com/_/24/968282549/baeldung"><img height="20" src="https://assets.feedblitz.com/i/x.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by email" href="https://feeds.feedblitz.com/_/19/968282549/baeldung"><img height="20" src="https://assets.feedblitz.com/i/email20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by RSS" href="https://feeds.feedblitz.com/_/20/968282549/baeldung"><img height="20" src="https://assets.feedblitz.com/i/rss20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a rel="NOFOLLOW" title="View Comments" href="https://www.baeldung.com/java-embabel-streaming-objects#respond"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/comments20.png"></a>&#160;<a title="Follow Comments via RSS" href="https://www.baeldung.com/java-embabel-streaming-objects/feed"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/commentsrss20.png"></a>&#160;</div>]]>
</description>
										<content:encoded><![CDATA[<img src="https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-14-1024x536.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="Two co-workers; the one on the left is holding up a magnifying glass to an artistic representation of files and folders. The one on the right is looking at a projection of some Java code. In between them there is an office plant. To the left of the image are the words &quot;Java on Baeldung&quot;" style="float: left; margin-right: 5px;" decoding="async" loading="lazy" srcset="https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-14-1024x536.jpg 1024w, https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-14-300x157.jpg 300w, https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-14-768x402.jpg 768w, https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-14-100x52.jpg 100w, https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-14.jpg 1200w" sizes="auto, (max-width: 580px) 100vw, 580px" /><h2 id="bd-overview" data-id="overview">1. Overview</h2>
<div class="bd-anchor" id="overview"></div>
<p>LLM streaming delivers output token by token as it&#8217;s generated. This reduces latency, as users see the first results in the UI within milliseconds instead of waiting seconds for a complete answer.</p>
<p>For agents with structured output and reasoning-aware agents, <strong>streaming should enable incremental processing:</strong></p>
<ul>
<li><strong>parsing responses into Java objects as they arrive, </strong>and</li>
<li><strong>processing thinking blocks as they arrive as well</strong></li>
</ul>
<p>However, current major frameworks don&#8217;t support these functionalities.</p>
<p>In this tutorial, we&#8217;ll:</p>
<ul>
<li>briefly review the limitations of these frameworks</li>
<li>and show how to use the <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/embabel/embabel-agent">Embabel Agentic AI Framework</a> to process objects and thinking blocks incrementally, as they arrive in a stream</li>
</ul>
<p>We&#8217;ll cover four cases:</p>
<ul>
<li>one object without reasoning</li>
<li>multiple objects without reasoning</li>
<li>multiple objects with reasoning but without tool calls</li>
<li>multiple objects with both reasoning and tool calls</li>
</ul>
<h2 id="bd-structured-streaming-framework-limitations" data-id="structured-streaming-framework-limitations">2. Structured Streaming: Framework Limitations</h2>
<div class="bd-anchor" id="structured-streaming-framework-limitations"></div>
<p>The major Java frameworks for LLM integration, Spring AI and LangChain4j, support streaming at a limited level. We can get a live stream of text as the model generates it, but neither framework turns that stream into a collection of typed objects out of the box.</p>
<p>The Spring AI&#8217;s <em>entity()</em> method, which converts model output into a Java object, works with the blocking <em>call()</em> method but not with <em>stream()</em>. The <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://docs.spring.io/spring-ai/reference/api/chatclient.html">ChatClient API reference</a> spells this out directly: <strong>&#8220;In the future, we will offer a convenience method that will let you return a Java entity with the reactive stream() method. In the meantime, you should use the Structured Output Converter.&#8221; </strong></p>
<p>LangChain4j has the same limitation. Its <em>AiServices</em> allows returning custom POJOs, lists, and enums only for non-streaming calls. For streaming, the return type must be a <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://docs.langchain4j.dev/tutorials/ai-services#streaming"><em>TokenStream</em></a>, which <strong>gives us raw text as it arrives,</strong> plus rich callback hooks, but <strong>doesn&#8217;t provide typed objects as stream output.</strong></p>
<p><strong>In practice, anyone who wants a stream of structured objects with reasoning has to build that functionality themselves, as in this <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/spring-ai-chatclient-stream-response">Baeldung&#8217;s article</a>.</strong> This means choosing an NL-delimited format like JSONL, buffering tokens until a full line is available, parsing each line as JSON, and filtering out any thinking blocks along the way. Implementing this takes time away from the actual project.</p>
<p>This is where <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/embabel/embabel-agent">Embabel</a> Agentic AI comes in handy. It natively provides the desired functionality, which we further explore.</p>
<h2 id="bd-setup" data-id="setup">3. Setup</h2>
<div class="bd-anchor" id="setup"></div>
<p>First, let’s define the necessary Embabel dependencies:</p>
<pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;com.embabel.agent&lt;/groupId&gt;
    &lt;artifactId&gt;embabel-agent-starter-anthropic&lt;/artifactId&gt;
    &lt;version&gt;${embabel-agent.version}&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;com.embabel.agent&lt;/groupId&gt;
    &lt;artifactId&gt;embabel-agent-test-internal&lt;/artifactId&gt;
    &lt;version&gt;${embabel-agent.version}&lt;/version&gt;
    &lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;</code></pre>
<p>The <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://central.sonatype.com/artifact/com.embabel.agent/embabel-agent-starter-anthropic">embabel-agent-starter-anthropic</a> dependency provides the core classes we need to use Anthropic models with Embabel. In the examples in this tutorial, we use the Anthropic model <em>claude-sonnet-4-5.</em></p>
<p>Additionally, we include <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://central.sonatype.com/artifact/com.embabel.agent/embabel-agent-test-internal">embabel-agent-test-internal</a> to use the <em>AgentTestApplication</em> test support class with <em>@SpringBootTest</em>.</p>
<h3 id="bd-1-provider-support-in-embabel" data-id="1-provider-support-in-embabel">3.1. Provider Support in Embabel</h3>
<div class="bd-anchor" id="1-provider-support-in-embabel"></div>
<p>Note that model providers differ in how they combine streaming with thinking and tools. Anthropic&#8217;s native thinking produces explicit reasoning blocks, but other providers may not surface reasoning in the same way.</p>
<p><strong>Embabel supports over a dozen LLM providers, with <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/java-embabel-ai-agent-tool-call-reasoning">examples using OpenAI</a> published on Baeldung in the past.</strong></p>
<h3 id="bd-2-parkingrecommendation" data-id="2-parkingrecommendation">3.2. <em>ParkingRecommendation</em></h3>
<div class="bd-anchor" id="2-parkingrecommendation"></div>
<p>We&#8217;ll stream <em>ParkingRecommendation</em> objects in all tests throughout the article:</p>
<a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/wp-content/uploads/2026/08/img_6a8c6eb8d416e.svg"><img loading="lazy" decoding="async" class="alignnone wp-image-261115 size-full" src="https://www.baeldung.com/wp-content/uploads/2026/08/img_6a8c6eb8d416e.svg" alt="The ParkingRecommendation record" width="660" height="280" /></a>
<p>The record includes four scalar properties: parking scenario (<em>scenario</em>), chosen option cost (<em>estimatedTotalCost</em>), summary, and chosen option (enumeration <em>Option</em>).</p>
<h2 id="bd-streaming-a-single-object" data-id="streaming-a-single-object">4. Streaming a Single Object</h2>
<div class="bd-anchor" id="streaming-a-single-object"></div>
<div>
<p>First, we show how to stream a single object without reasoning and tool calls. The test method is <em>whenStreaming_thenReceivesParkingRecommendation():</em></p>
<pre><code class="language-java">void whenStreaming_thenReceivesParkingRecommendation() {
     streamParkingRecommendations(PARKING_PROMPT);
  }</code></pre>
<p><em>PARKING_PROMPT</em> asks for a single parking recommendation, and the private method <em>streamParkingRecommendations</em>() does the actual object streaming:</p>
<pre><code class="language-java">Flux&lt;ParkingRecommendation&gt; stream = new StreamingPromptRunnerBuilder(runner)
  .streaming()
  .withPrompt(PARKING_PROMPT)
  .createObjectStream(ParkingRecommendation.class);
stream
  .timeout(Duration.ofSeconds(120))
  .doOnNext(rec -&gt; {
    received.add(rec);
    logger.info(
      "Received parking recommendation: scenario={}, option={}, cost={}, summary={}",
      rec.scenario(),
      rec.chosenOption(),
      rec.estimatedTotalCost(),
      rec.summary());
    }).blockLast(Duration.ofSeconds(240));</code></pre>
<p>Here:</p>
<ul>
<li>We create a <em>Flux&lt;ParkingRecommendation&gt;</em> by calling <em>createObjectStream()</em>. <strong>A <em>Flux</em> is a <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://projectreactor.io/">Reactor</a> type representing an asynchronous sequence of zero or more elements that arrive over time rather than all at once.</strong> In this example, the model emits a <em>ParkingRecommendation</em> object.</li>
<li><em>PARKING_PROMPT</em> instructs the LLM to find the optimal parking option out of three available options: street parking, metered parking, and garage parking. The prompt specifies constraints and costs for each alternative.</li>
<li><strong>The pipeline subscribes with <em>doOnNext()</em> to collect and log each object.</strong></li>
<li><em>blockLast()</em> blocks the calling thread until the stream completes. This is appropriate in a test context, though production code would chain further reactive operators instead of blocking. For example, we would return the <em>Flux</em> directly to a web endpoint that streams the response to the client.</li>
</ul>
<p>We can run the unit test method <em>whenStreaming_thenReceivesParkingRecommendation()</em> like this:</p>
<pre><code class="language-bash">$ mvn test -pl embabel-streaming -P integration -Dtest=StreamingWithThinkingAndToolingIntegrationTest#whenStreaming_thenReceivesParkingRecommendation</code></pre>
<p>After the stream completes, logs confirm the stream delivers a complete <em>ParkingRecommendation </em>object:</p>
<pre><code class="language-plaintext">17:34:13.781 [boundedElastic-1] INFO StreamingWithThinkingAndToolingIntegrationTest - Received recommendation: scenario=Midtown Manhattan parking for 3-hour client meeting with 30-minute arrival buffer, option=GARAGE, cost=90, summary=Choose garage parking. The meeting duration (3 hours) exceeds the metered parking limit (2 hours), eliminating that option. Street parking in Midtown Manhattan is extremely unreliable and searching could make you late for the meeting. The guaranteed spot and ability to stay for the full meeting duration justifies the $90 cost for this professional context.</code></pre>
<p>The object includes all the properties we expect: <em>scenario</em>, <em>chosenOption</em>, <em>estimatedTotalCost</em>, and <em>summary</em>.</p>
<h2 id="bd-streaming-multiple-objects" data-id="streaming-multiple-objects">5. Streaming Multiple Objects</h2>
<div class="bd-anchor" id="streaming-multiple-objects"></div>
<p>The <em>streamParkingRecommendations()</em> method can stream multiple objects. To show it, we request three recommendations with  <em>TIMED_PARKING_PROMPT</em>:</p>
<pre><code class="language-plaintext">void whenStreamingMultipleScenarios_thenReceivesRecommendationPerScenario() {
    streamParkingRecommendations(TIMED_PARKING_PROMPT);
}</code></pre>
<p>When streaming:</p>
<ul>
<li><em>doOnNext()</em> collects and logs each object as it arrives</li>
<li><em>blockLast()</em> waits for the stream to complete</li>
</ul>
<p>We can run the unit test method <em>whenStreamingMultipleScenarios_thenReceivesRecommendationPerScenario</em> like this:</p>
<pre><code class="language-bash">$ mvn test -pl embabel-streaming -P integration -Dtest=StreamingWithThinkingAndToolingIntegrationTest#whenStreamingMultipleScenarios_thenReceivesRecommendationPerScenario</code></pre>
<p><strong>Instead of obtaining a single batched response with all objects, we get each <em>ParkingRecommendation</em> as a separate object as it is received:</strong></p>
<pre><code class="language-plaintext">18:31:24.200 [boundedElastic-1] INFO  StreamingWithThinkingAndToolingIntegrationTest - Received recommendation: scenario=Early morning (before 8am), option=STREET, cost=0, summary=Arrive before 8am to take advantage of free street parking. Meters are not enforced until 8am, providing zero-cost parking. Be prepared to feed the meter or move to a garage if your meeting extends past 8am.
18:31:27.354 [boundedElastic-1] INFO  StreamingWithThinkingAndToolingIntegrationTest - Received recommendation: scenario=Business hours (9am-5pm), option=GARAGE, cost=45, summary=For a 3-hour business meeting with only a 30-minute arrival window, a parking garage is the most reliable option. Street parking in Midtown is extremely competitive during peak hours, and the time spent searching could cause you to miss your meeting. Garage rates typically range $35-55 for 3 hours in Midtown Manhattan.
18:31:28.889 [HttpClient-4-Worker-0] WARN  StreamHelper - Unhandled event type: CONTENT_BLOCK_STOP
18:31:28.895 [boundedElastic-1] INFO  StreamingWithThinkingAndToolingIntegrationTest - Received recommendation: scenario=Evening (after 6pm), option=STREET, cost=0, summary=Street meters become free after 6pm, making this the best option if you're confident your 3-hour meeting will end before 9pm. However, have a backup plan: identify a 24-hour garage nearby in case your meeting runs late, as many garages close at 9pm and you could be locked in or locked out.
</code></pre>
<p>The logged timing of each object creation certifies that complete objects are obtained sequentially and that the collection is filled incrementally.</p>
<p>Note the entry in the log &#8220;Unhandled event type: <em>CONTENT_BLOCK_STOP</em>&#8220;. It just signals &#8220;this content block (a text block, tool call block, etc.) is finished.&#8221; It carries no payload and says that we received a server-side event, but there is no handler wired up for it.</p>
<h2 id="bd-streaming-with-reasoning" data-id="streaming-with-reasoning">6. Streaming with Reasoning</h2>
<div class="bd-anchor" id="streaming-with-reasoning"></div>
<p>First, we must enable deep thinking by configuring <em>LlmOptions</em> with a token budget:</p>
<pre><code class="language-java">void whenStreamingWithThinking_thenReceivesReasoningAndRecommendation() {
    LlmOptions thinkingOptions = new LlmOptions().withThinking(Thinking.withTokenBudget(8000));
    PromptRunner runner = ai.withDefaultLlm().withLlm(thinkingOptions);
    streamParkingRecommendationsWithThinking(runner, TIMED_PARKING_PROMPT);
}</code></pre>
<p>The budget controls how many tokens the model may spend on reasoning before producing the answer, and it must be lower than <em>max_tokens</em> (8192 for <em>claude-sonnet-4-5</em>).</p>
<p>Objects are streamed in the private method <em>streamParkingRecommendationsWithThinking()</em>. <strong>It uses <em>createObjectStreamWithThinking()</em> instead of <em>createObjectStream()</em></strong><em>, </em>and the return type changes accordingly:</p>
<pre><code class="language-java">Flux&lt;StreamingEvent&lt;ParkingRecommendation&gt;&gt; stream = new StreamingPromptRunnerBuilder(runner)
  .streaming()
  .withPrompt(prompt)
  .createObjectStreamWithThinking(ParkingRecommendation.class);</code></pre>
<p><strong><em>StreamingEvent</em> wraps typed objects and reasoning fragments, as the stream interleaves both kinds of events.</strong> The <em>doOnNext()</em> callback distinguishes them via the <em>event.isObject()</em> and <em>event.isThinking() </em>checks, collecting them into separate lists, <em>received</em> for objects and <em>reasoning</em> for thinking fragments:</p>
<pre><code class="language-java">.doOnNext(event -&gt; {
  if (event.isObject()) {
      ParkingRecommendation rec = event.getObject();
      if (rec != null) {
          received.add(rec);
          logger.info("Received recommendation: scenario={}, option={}, cost={}, summary={}",
            rec.scenario(), rec.chosenOption(), rec.estimatedTotalCost(), rec.summary());
      }
  } else if (event.isThinking()) {
      reasoning.add(event.getThinking());
      logger.info("Received reasoning: {}", event.getThinking());
  }
})</code></pre>
<p>We can run the unit test <em>whenStreamingWithThinking_thenReceivesReasoningAndRecommendation()</em> as follows:</p>
<pre><code class="language-bash">$ mvn test -pl embabel-streaming -P integration -Dtest=StreamingWithThinkingAndToolingIntegrationTest#whenStreamingWithThinking_thenReceivesReasoningAndRecommendation</code></pre>
<p>Logs demonstrate that recommendations and reasoning fragments are processed as they arrive.</p>
<pre><code class="language-plaintext">21:34:05.311 [boundedElastic-1] INFO  StreamingWithThinkingAndToolingIntegrationTest - Received reasoning: The user is asking for parking recommendations for Midtown Manhattan across three different time scenarios. I need to analyze each scenario and provide recommendations in JSONL format.
21:34:05.311 [boundedElastic-1] INFO  StreamingWithThinkingAndToolingIntegrationTest - Let me think through each scenario:
...
21:34:13.300 [boundedElastic-1] INFO StreamingWithThinkingAndToolingIntegrationTest - Received recommendation: scenario=Early morning (before 8am), option=STREET, cost=0, summary=Street parking is free before 8am in Midtown. Arrive early to secure a spot while meters are not enforced. No cost advantage to using garage or paid meter.
21:34:14.003 [boundedElastic-1] INFO StreamingWithThinkingAndToolingIntegrationTest - Received reasoning: For business hours, considering reliability vs cost. 30-minute window suggests tight schedule, and 3-hour stay during peak hours means garage offers certainty despite higher cost
21:34:16.867 [boundedElastic-1] INFO StreamingWithThinkingAndToolingIntegrationTest - Received recommendation: scenario=Business hours (9am-5pm, 3-hour stay), option=GARAGE, cost=35, summary=During peak business hours with only a 30-minute arrival window, garage parking ($30-40 for 3 hours) provides guaranteed availability and eliminates time spent searching. Street meters ($12-15 for 3 hours) are cheaper but finding spots in Midtown during business hours is challenging and risky given the tight timeline.
21:34:18.275 [boundedElastic-1] INFO StreamingWithThinkingAndToolingIntegrationTest - Received reasoning: Evening scenario has free street parking but garage closure risk. If arriving at 6pm with 3-hour stay, would exit at 9pm exactly when garages close - cutting it too close. Street parking is free and has no closure risk
21:34:19.337 [HttpClient-4-Worker-0] WARN StreamHelper - Unhandled event type: CONTENT_BLOCK_STOP
21:34:19.359 [boundedElastic-1] INFO StreamingWithThinkingAndToolingIntegrationTest - Received recommendation: scenario=Evening (after 6pm, 3-hour stay), option=STREET, cost=0, summary=Street meters are free after 6pm, making this the clear choice. Additionally, with garages closing at 9pm and a 3-hour stay, there's significant risk of garage closure before departure. Street parking eliminates both cost and time constraints.
</code></pre>
<p>Note that each reasoning block represents a single line, as Embabel aggregates mini-chunks into a single line.</p>
<h2 id="bd-streaming-with-tools-and-reasoning" data-id="streaming-with-tools-and-reasoning">7. Streaming with Tools and Reasoning</h2>
<div class="bd-anchor" id="streaming-with-tools-and-reasoning"></div>
<p>Finally, let&#8217;s combine tool calling with streaming and thinking. For a blocking call, the Baeldung article <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/java-embabel-ai-agent-tool-call-reasoning">LLM Tool Call Reasoning Using Embabel Agentic AI Framework</a> shows how tool calls benefit from LLM reasoning.</p>
<p>The API is very similar to the reasoning use case, except for the tool registration <em>withToolObject(new ParkingTooling())</em> and a logging inspector for observability:</p>
<pre><code class="language-java">void whenStreamingWithThinkingAndTooling_thenReceivesRecommendationAndReasoning() {
    PromptRunner runner = ai.withDefaultLlm()
      .withToolObject(new ParkingTooling())
      .withToolCallInspectors(new ToolCallLoggingInspector(LogLevel.INFO, logger));
    streamParkingRecommendationsWithThinking(runner, TOOL_PARKING_PROMPT);
}</code></pre>
<p><em>TOOL_PARKING_PROMPT</em> instructs the model to actively use the tools before giving three recommendations, and the <em>streamParkingRecommendationsWithThinking()</em> method streams them.</p>
<p>The pipeline uses the same <em>createObjectStreamWithThinking()</em> and the same <em>event.isObject() / event.isThinking()</em> pattern. The key difference is tool call timing: <strong>the model first calls tools, then reasons over the results, then emits the objects, and finally provides a reasoning summary. </strong>As Spring AI starts a new stream after all tool calls are complete, the reasoning blocks are emitted only after tool calls.</p>
<p>We can run the unit test <em>whenStreamingWithThinkingAndTooling_thenReceivesRecommendationAndReasoning()</em> like this:</p>
<pre><code class="language-bash">$ mvn test -pl embabel-streaming -P integration -Dtest=StreamingWithThinkingAndToolingIntegrationTest#whenStreamingWithThinkingAndTooling_thenReceivesRecommendationAndReasoning</code></pre>
<pre><code class="language-plaintext">12:48:52.342 [HttpClient-4-Worker-0] WARN  StreamHelper - Unhandled event type: CONTENT_BLOCK_STOP
12:48:53.275 [boundedElastic-2] INFO  StreamingWithThinkingAndToolingIntegrationTest - beforeToolCall: tool=reserveGarage, argsLength=28
12:48:53.278 [boundedElastic-2] INFO  Embabel - [suspicious_tharp] calling tool reserveGarage({"arg0":"Midtown Manhattan"})
12:48:53.281 [boundedElastic-2] INFO  Embabel - [suspicious_tharp] tool reserveGarage returned Garage reserved near Midtown Manhattan ($30/hour, guaranteed) in 2ms with payload {"arg0":"Midtown Manhattan"}
12:48:53.284 [boundedElastic-2] INFO  StreamingWithThinkingAndToolingIntegrationTest - afterToolCall: tool=reserveGarage, status=Text, resultLength=61, durationMs=6
12:48:55.299 [boundedElastic-3] INFO  StreamingWithThinkingAndToolingIntegrationTest - beforeToolCall: tool=findStreetParking, argsLength=38
12:48:55.299 [boundedElastic-3] INFO  Embabel - [suspicious_tharp] calling tool findStreetParking({"arg1":30,"arg0":"Midtown Manhattan"})
12:48:55.300 [boundedElastic-3] INFO  Embabel - [suspicious_tharp] tool findStreetParking returned Street parking found near Midtown Manhattan (free) in 1ms with payload {"arg1":30,"arg0":"Midtown Manhattan"}
12:48:55.300 [boundedElastic-3] INFO  StreamingWithThinkingAndToolingIntegrationTest - afterToolCall: tool=findStreetParking, status=Text, resultLength=50, durationMs=1
12:48:56.953 [boundedElastic-4] INFO  StreamingWithThinkingAndToolingIntegrationTest - beforeToolCall: tool=findMeterParking, argsLength=38
12:48:56.954 [boundedElastic-4] INFO  Embabel - [suspicious_tharp] calling tool findMeterParking({"arg1":30,"arg0":"Midtown Manhattan"})
12:48:56.954 [boundedElastic-4] INFO  Embabel - [suspicious_tharp] tool findMeterParking returned No metered parking found within 30 minutes in 0ms with payload {"arg1":30,"arg0":"Midtown Manhattan"}
12:48:56.954 [boundedElastic-4] INFO  StreamingWithThinkingAndToolingIntegrationTest - afterToolCall: tool=findMeterParking, status=Text, resultLength=42, durationMs=1
12:48:58.801 [boundedElastic-1] INFO  StreamingWithThinkingAndToolingIntegrationTest - Received reasoning: I'll help you find the best parking options for your client meeting in Midtown Manhattan. Let me check all available parking options for you.Based on the parking options available, here are my three recommendations with the best option first:
12:49:00.945 [boundedElastic-1] INFO  StreamingWithThinkingAndToolingIntegrationTest - Received recommendation: scenario=Advisor needs guaranteed parking for 3-hour meeting in 30 minutes, option=GARAGE, cost=90, summary=Reserve garage parking at $30/hour for 3 hours ($90 total). This is the BEST option because it's guaranteed and ensures you won't be late for your client meeting. With only 30 minutes until the meeting, reliability is critical.
12:49:03.093 [boundedElastic-1] INFO  StreamingWithThinkingAndToolingIntegrationTest - Received recommendation: scenario=Advisor needs guaranteed parking for 3-hour meeting in 30 minutes, option=STREET, cost=0, summary=Free street parking is available but highly risky. While it costs nothing, finding a spot is uncertain and time-consuming in Midtown Manhattan. Given that arriving late is not acceptable and you only have 30 minutes, this option could jeopardize your meeting.
12:49:03.706 [boundedElastic-1] INFO  StreamingWithThinkingAndToolingIntegrationTest - Received recommendation: scenario=Advisor needs guaranteed parking for 3-hour meeting in 30 minutes, option=METER, cost=0, summary=Metered parking is NOT available within your 30-minute timeframe in Midtown Manhattan. This option is not viable for your situation.
12:49:05.244 [HttpClient-4-Worker-0] WARN  StreamHelper - Unhandled event type: CONTENT_BLOCK_STOP
12:49:05.247 [boundedElastic-1] INFO  StreamingWithThinkingAndToolingIntegrationTest - Received reasoning: **Strong Recommendation: Choose the garage parking.** With only 30 minutes before your client meeting and the absolute requirement not to be late, the $90 guaranteed garage spot is the only responsible choice. The cost is a small price compared to the professional consequences of arriving late to a client meeting.
</code></pre>
<p>Similar to <em>whenStreamingWithThinking_thenReceivesReasoningAndRecommendation(),</em> the stream delivers both a recommendation and a reasoning block (after the tools do their tasks).</p>
<h2 id="bd-conclusion" data-id="conclusion">8. Conclusion</h2>
<div class="bd-anchor" id="conclusion"></div>
<p>In this article, we showed how to use the Embabel Fluent API to produce a stream of typed Java objects, with thinking and tool results handled transparently.</p>
<p>Streaming raw text is straightforward in both Spring AI and LangChain4j, but streaming typed objects is not something either framework handles out of the box. <strong>Embabel addresses this at the framework level, removing the need to manually buffer tokens, parse NDJSON, or filter reasoning blocks.</strong> The same pattern scales from a single object to a collection and from a simple prompt to a multi-tool reasoning chain. For applications where both structure and responsiveness matter, this removes a layer of boilerplate that would otherwise fall on every team building agentic AI applications.</p>
<p>The article code is available on <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/eugenp/tutorials/tree/master/embabel-modules">GitHub</a>.</p>
</div><p>The post <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/java-embabel-streaming-objects">LLM Streaming in the Embabel Agentic AI Framework</a> first appeared on <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com">Baeldung</a>.</p><Img align="left" border="0" height="1" width="1" alt="" style="border:0;float:left;margin:0;padding:0;width:1px!important;height:1px!important;" hspace="0" src="https://feeds.feedblitz.com/~/i/968282549/0/baeldung">
<div style="clear:both;padding-top:0.2em;"><a title="Like on Facebook" href="https://feeds.feedblitz.com/_/28/968282549/baeldung"><img height="20" src="https://assets.feedblitz.com/i/fblike20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Pin it!" href="https://feeds.feedblitz.com/_/29/968282549/baeldung,https%3a%2f%2fwww.baeldung.com%2fwp-content%2fuploads%2f2024%2f07%2fJava-Featured-14-1024x536.jpg"><img height="20" src="https://assets.feedblitz.com/i/pinterest20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Post to X.com" href="https://feeds.feedblitz.com/_/24/968282549/baeldung"><img height="20" src="https://assets.feedblitz.com/i/x.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by email" href="https://feeds.feedblitz.com/_/19/968282549/baeldung"><img height="20" src="https://assets.feedblitz.com/i/email20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by RSS" href="https://feeds.feedblitz.com/_/20/968282549/baeldung"><img height="20" src="https://assets.feedblitz.com/i/rss20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a rel="NOFOLLOW" title="View Comments" href="https://www.baeldung.com/java-embabel-streaming-objects#respond"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/comments20.png"></a>&#160;<a title="Follow Comments via RSS" href="https://www.baeldung.com/java-embabel-streaming-objects/feed"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/commentsrss20.png"></a>&#160;</div>]]>
</content:encoded>
					
					<wfw:commentRss>https://feeds.feedblitz.com/~/968282549/0/baeldung~LLM-Streaming-in-the-Embabel-Agentic-AI-Framework/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<webfeeds:featuredImage>https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-14-150x150.jpg</webfeeds:featuredImage></item>
<item>
<feedburner:origLink>https://www.baeldung.com/java-weekly-661</feedburner:origLink>
		<title>Java Weekly, Issue 661</title>
		<link>https://feeds.feedblitz.com/~/968274254/0/baeldung~Java-Weekly-Issue</link>
					<comments>https://feeds.feedblitz.com/~/968274254/0/baeldung~Java-Weekly-Issue#respond</comments>
		
		<dc:creator><![CDATA[baeldung]]></dc:creator>
		<pubDate>Wed, 26 Aug 2026 17:15:42 +0000</pubDate>
				<category><![CDATA[Weekly Review]]></category>
		<category><![CDATA[no-ads]]></category>
		<category><![CDATA[no-after-post]]></category>
		<category><![CDATA[no-before-post]]></category>
		<category><![CDATA[no-optins]]></category>
		<guid isPermaLink="false">https://www.baeldung.com/?p=204768</guid>
					<description><![CDATA[<img src="https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="max-width:100% !important;height:auto !important;float: left; margin-right: 5px;" loading="lazy" /><p>IntelliJ IDEA Conf is back, JDK 27 got its first RC and JDK 28 is already looking solid</p>
<p>The post <a rel="NOFOLLOW" href="https://feeds.feedblitz.com/~/968274254/0/baeldung~Java-Weekly-Issue">Java Weekly, Issue 661</a> first appeared on <a rel="NOFOLLOW" href="https://www.baeldung.com">Baeldung</a>.</p><div style="clear:both;padding-top:0.2em;"><a title="Like on Facebook" href="https://feeds.feedblitz.com/_/28/968274254/baeldung"><img height="20" src="https://assets.feedblitz.com/i/fblike20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Pin it!" href="https://feeds.feedblitz.com/_/29/968274254/baeldung,https%3a%2f%2fwww.baeldung.com%2fwp-content%2fuploads%2f2016%2f10%2fsocial-Weekly-Reviews-4.jpg"><img height="20" src="https://assets.feedblitz.com/i/pinterest20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Post to X.com" href="https://feeds.feedblitz.com/_/24/968274254/baeldung"><img height="20" src="https://assets.feedblitz.com/i/x.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by email" href="https://feeds.feedblitz.com/_/19/968274254/baeldung"><img height="20" src="https://assets.feedblitz.com/i/email20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by RSS" href="https://feeds.feedblitz.com/_/20/968274254/baeldung"><img height="20" src="https://assets.feedblitz.com/i/rss20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a rel="NOFOLLOW" title="View Comments" href="https://www.baeldung.com/java-weekly-661#respond"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/comments20.png"></a>&#160;<a title="Follow Comments via RSS" href="https://www.baeldung.com/java-weekly-661/feed"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/commentsrss20.png"></a>&#160;</div>]]>
</description>
										<content:encoded><![CDATA[<img src="https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="float: left; margin-right: 5px;" decoding="async" loading="lazy" srcset="https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4.jpg 952w, https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4-300x157.jpg 300w, https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4-768x402.jpg 768w" sizes="auto, (max-width: 580px) 100vw, 580px" /><h2 style="text-align: left;" id="bd-spring-and-java" data-id="spring-and-java">1.<strong> Spring and Java</strong></h2>
<div class="bd-anchor" id="spring-and-java"></div>
<p><strong><a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.infoq.com/news/2026/08/java-27-so-far/">&gt;&gt; JDK 27 and JDK 28: What We Know So Far</a></strong> [<span style="color: #993300;">infoq.com</span>]</p>
<p>JDK 27 has reached its first RC with compact object headers on by default, G1 as the default collector everywhere, and structured concurrency marching through its seventh preview. <strong>The JDK 28 train</strong> is already loading up, starting with an incubating Simple JSON API <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f680.png" alt="🚀" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h4><strong>Also worth reading:</strong></h4>
<ul>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.frankel.ch/security-baked-into-jvm/4/" target="_blank" rel="noopener"><strong>Security Baked Into the JVM: sixteen Subjects on the wire</strong></a> [<span style="color: #800000;">frankel.ch</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://foojay.io/today/scripting-in-small-apps-you-dont-need-a-container-for-a-macro/" target="_blank" rel="noopener"><strong>Scripting in small apps: you don’t need a container for a macro</strong></a> [<span style="color: #800000;">foojay.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://foojay.io/today/the-third-generation-gui-builder-one-workspace-for-every-form/" target="_blank" rel="noopener"><strong>The Third-Generation GUI Builder: One Workspace for Every Form</strong></a> [<span style="color: #800000;">foojay.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://johan-sjolen.github.io/post/compiler-sympathy/compiler-sympathy/" target="_blank" rel="noopener"><strong>Value Classes Still Need Compiler Sympathy</strong></a> [<span style="color: #800000;">johan-sjolen.github.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://inside.java/2026/08/21/quality-heads-up/" target="_blank" rel="noopener"><strong>Quality Outreach Heads-up &#8211; JDK 28: Named Group Information Captured in the TLSHandshake JFR Event</strong></a> [<span style="color: #800000;">inside.java</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.jetbrains.com/idea/2026/08/spring-boot-configuration-management-best-practices/" target="_blank" rel="noopener"><strong>Spring Boot Configuration Management Best Practices</strong></a> [<span style="color: #800000;">jetbrains.com</span>]</li>
</ul>
<h4><strong>Webinars and presentations:</strong></h4>
<ul>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://inside.java/2026/08/20/podcast-067/" target="_blank" rel="noopener"><strong>Episode 67 “Keeping Up With the Java Release Train” [I/O]</strong></a> [<span style="color: #800000;">inside.java</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://spring.io/blog/2026/08/20/a-bootiful-podcast-charles-nutter" target="_blank" rel="noopener"><strong>A Bootiful Podcast: JRuby lead Charles Nutter</strong></a> [<span style="color: #800000;">spring.io</span>]</li>
</ul>
<h4><strong>Time to upgrade:</strong></h4>
<ul>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://spring.io/blog/2026/08/20/spring-boot-4-1-1-available-now" target="_blank" rel="noopener"><strong>Spring Boot 4.1.1 available now</strong></a> and <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://spring.io/blog/2026/08/20/spring-boot-4-0-8-available-now" target="_blank" rel="noopener"><strong>4.0.8</strong></a> [<span style="color: #800000;">spring.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://spring.io/blog/2026/08/21/spring-ai-2-0-1-available-now" target="_blank" rel="noopener"><strong>Spring AI 2.0.1 Available Now</strong></a> [<span style="color: #800000;">spring.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://spring.io/blog/2026/08/20/spring-cloud-2025-1-3-has-been-released" target="_blank" rel="noopener"><strong>Spring Cloud 2025.1.3 (aka Oakwood) has been released</strong></a> [<span style="color: #800000;">spring.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://spring.io/blog/2026/08/20/spring-batch-6-0-5-and-6-1-0-M1-available-now" target="_blank" rel="noopener"><strong>Spring Batch 6.0.5 and 6.1.0-M1 available now</strong></a> [<span style="color: #800000;">spring.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://spring.io/blog/2026/08/26/spring-modulith-2-2-m1-2-1-1-2-0-8-and-1-4-13-released" target="_blank" rel="noopener"><strong>Spring Modulith 2.2 M1, 2.1.1, 2.0.8, and 1.4.13 released</strong></a> [<span style="color: #800000;">spring.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://foojay.io/today/boxlang-1-16-0-released/" target="_blank" rel="noopener"><strong>BoxLang 1.16.0 Released!</strong></a> [<span style="color: #800000;">foojay.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://inside.java/2026/08/25/jipher-cryptography-for-java/" target="_blank" rel="noopener"><strong>Announcing Oracle Jipher 10.37: FIPS 140-3 Cryptography for Java</strong></a> [<span style="color: #800000;">inside.java</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/graalvm/graalvm-ce-builds/releases/tag/graal-25.3.4.1" target="_blank" rel="noopener"><strong>GraalVM Community 25 Innovation 3 (graal 25.3.4.1)</strong></a> [<span style="color: #800000;">github.com/graalvm</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/quarkusio/quarkus/releases/tag/3.39.1" target="_blank" rel="noopener"><strong>Quarkus 3.39.1</strong></a> and <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/quarkusio/quarkus/releases/tag/3.39.0" target="_blank" rel="noopener"><strong>3.39.0</strong></a> [<span style="color: #800000;">github.com/quarkusio</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/elastic/elasticsearch/releases/tag/v9.5.2" target="_blank" rel="noopener"><strong>Elasticsearch 9.5.2</strong></a> [<span style="color: #800000;">github.com/elastic</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/apache/grails-core/releases/tag/v7.2.3" target="_blank" rel="noopener"><strong>Grails 7.2.3</strong></a>, <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/apache/grails-core/releases/tag/v7.1.6" target="_blank" rel="noopener"><strong>7.1.6</strong></a> and <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/apache/grails-core/releases/tag/v7.0.16" target="_blank" rel="noopener"><strong>7.0.16</strong></a> [<span style="color: #800000;">github.com/apache</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/micronaut-projects/micronaut-core/releases/tag/v5.1.12" target="_blank" rel="noopener"><strong>Micronaut Core 5.1.12</strong></a> [<span style="color: #800000;">github.com/micronaut-projects</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/payara/Payara/releases/tag/payara-server-7.2026.8" target="_blank" rel="noopener"><strong>Azul Payara Community 7.2026.8</strong></a> [<span style="color: #800000;">github.com/payara</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/micrometer-metrics/micrometer/releases/tag/v1.17.1" target="_blank" rel="noopener"><strong>Micrometer 1.17.1</strong></a> and <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/micrometer-metrics/micrometer/releases/tag/v1.16.7" target="_blank" rel="noopener"><strong>1.16.7</strong></a> [<span style="color: #800000;">github.com/micrometer-metrics</span>]</li>
</ul>
<h2 style="text-align: left;" id="bd-technical-amp-musings" data-id="technical-amp-musings">2.<strong> Technical &amp; Musings</strong></h2>
<div class="bd-anchor" id="technical-amp-musings"></div>
<p><strong><a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://netflixtechblog.medium.com/netflix-conductor-the-next-chapter-41ad21067649">&gt;&gt; Netflix Conductor: The Next Chapter</a></strong> [<span style="color: #993300;">netflixtechblog.com</span>]</p>
<p>Netflix open-sourced Conductor back in 2016, and this post traces its evolution into a mature orchestration engine — plus what’s next for the project now that it has a dedicated maintainer team and a renewed roadmap. Good context for anyone running workflow orchestration at scale.</p>
<h4><strong>Also worth reading:</strong></h4>
<ul>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://lucumr.pocoo.org/2026/8/22/fast-hard-code/" target="_blank" rel="noopener"><strong>Fast and Hard Code</strong></a> [<span style="color: #800000;">pocoo.org</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://nurkiewicz.com/2026/08/your-first-virtual-machine-write-yourself-a-compiler.html" target="_blank" rel="noopener"><strong>Your First Virtual Machine: Write yourself a compiler, Part IV</strong></a> [<span style="color: #800000;">nurkiewicz.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.christianposta.com/human-in-the-loop-authorization-patterns-for-agents/" target="_blank" rel="noopener"><strong>Human-in-the-loop Authorization Patterns for Agents</strong></a> [<span style="color: #800000;">christianposta.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://hamvocke.com/blog/git-revisions-by-text/" target="_blank" rel="noopener"><strong>Find Git Commits by Commit Message Text</strong></a> [<span style="color: #800000;">hamvocke.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.satisfice.com/blog/archives/488189" target="_blank" rel="noopener"><strong>This Fact About KPIs May Surprise You</strong></a> [<span style="color: #800000;">satisfice.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.scottlogic.com/2026/08/21/why-agentic-ai-starts-with-legacy-modernisation.html" target="_blank" rel="noopener"><strong>Why agentic AI starts with legacy modernisation by Suzanne Angell</strong></a> [<span style="color: #800000;">scottlogic.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://netflixtechblog.com/a-tale-of-two-flink-autoscalers-e9f6a1b1492b" target="_blank" rel="noopener"><strong>A Tale of Two Flink Autoscalers</strong></a> [<span style="color: #800000;">netflixtechblog.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.tfnico.com/2026/08/some-reflections-after-year-with-ai.html" target="_blank" rel="noopener"><strong>Some reflections after a year with AI</strong></a> [<span style="color: #800000;">tfnico.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.hanselman.com/blog/migrating-a-synology-nas-to-a-unifi-unas-pro-8-with-robocopy-smb-multichannel-and-surprising-performance-traps" target="_blank" rel="noopener"><strong>Migrating a Synology NAS to a UniFi UNAS Pro 8 with Robocopy, SMB Multichannel, and Surprising Performance Traps</strong></a> [<span style="color: #800000;">hanselman.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.troyhunt.com/a-cautionary-tale-about-data-breach-claims-verification-and-carhartt/" target="_blank" rel="noopener"><strong>A Cautionary Tale About Data Breach Claims, Verification and Carhartt</strong></a> [<span style="color: #800000;">troyhunt.com</span>]</li>
</ul>
<h2 style="text-align: left;" id="bd-pick-of-the-week" data-id="pick-of-the-week">3.<strong> Pick of the Week</strong></h2>
<div class="bd-anchor" id="pick-of-the-week"></div>
<p>JetBrains is bringing IntelliJ IDEA Conf back in just one week, on September 8–9, 2026.</p>
<p>A <strong>free, fully virtual conference</strong> for developers working across the Java and JVM ecosystem:</p>
<p><strong><a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/jetbrains-conf-jw-agof" rel="sponsored ">&gt;&gt; IntelliJ IDEA Conf 2026</a></strong> [<span style="color: #993300;">jetbrains.com</span>]</p><p>The post <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/java-weekly-661">Java Weekly, Issue 661</a> first appeared on <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com">Baeldung</a>.</p><Img align="left" border="0" height="1" width="1" alt="" style="border:0;float:left;margin:0;padding:0;width:1px!important;height:1px!important;" hspace="0" src="https://feeds.feedblitz.com/~/i/968274254/0/baeldung">
<div style="clear:both;padding-top:0.2em;"><a title="Like on Facebook" href="https://feeds.feedblitz.com/_/28/968274254/baeldung"><img height="20" src="https://assets.feedblitz.com/i/fblike20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Pin it!" href="https://feeds.feedblitz.com/_/29/968274254/baeldung,https%3a%2f%2fwww.baeldung.com%2fwp-content%2fuploads%2f2016%2f10%2fsocial-Weekly-Reviews-4.jpg"><img height="20" src="https://assets.feedblitz.com/i/pinterest20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Post to X.com" href="https://feeds.feedblitz.com/_/24/968274254/baeldung"><img height="20" src="https://assets.feedblitz.com/i/x.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by email" href="https://feeds.feedblitz.com/_/19/968274254/baeldung"><img height="20" src="https://assets.feedblitz.com/i/email20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by RSS" href="https://feeds.feedblitz.com/_/20/968274254/baeldung"><img height="20" src="https://assets.feedblitz.com/i/rss20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a rel="NOFOLLOW" title="View Comments" href="https://www.baeldung.com/java-weekly-661#respond"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/comments20.png"></a>&#160;<a title="Follow Comments via RSS" href="https://www.baeldung.com/java-weekly-661/feed"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/commentsrss20.png"></a>&#160;</div>]]>
</content:encoded>
					
					<wfw:commentRss>https://feeds.feedblitz.com/~/968274254/0/baeldung~Java-Weekly-Issue/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<webfeeds:featuredImage>https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4-150x150.jpg</webfeeds:featuredImage></item>
<item>
<feedburner:origLink>https://www.baeldung.com/java-fix-jackson-json-parse-error</feedburner:origLink>
		<title>How to Fix Jackson JSON Parse Error Can not construct instance of java.time.LocalDate</title>
		<link>https://feeds.feedblitz.com/~/968126756/0/baeldung~How-to-Fix-Jackson-JSON-Parse-Error-Can-not-construct-instance-of-javatimeLocalDate</link>
					<comments>https://feeds.feedblitz.com/~/968126756/0/baeldung~How-to-Fix-Jackson-JSON-Parse-Error-Can-not-construct-instance-of-javatimeLocalDate#respond</comments>
		
		<dc:creator><![CDATA[Hiks Gerganov]]></dc:creator>
		<pubDate>Sat, 22 Aug 2026 23:26:23 +0000</pubDate>
				<category><![CDATA[Jackson]]></category>
		<category><![CDATA[Java Dates]]></category>
		<category><![CDATA[Exception]]></category>
		<category><![CDATA[LocalDate]]></category>
		<guid isPermaLink="false">https://www.baeldung.com/java-fix-jackson-json-parse-error</guid>
					<description><![CDATA[<img src="https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-11-1024x536.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="max-width:100% !important;height:auto !important;float: left; margin-right: 5px;" loading="lazy" /><p>Learn why sometimes Jackson can't construct an instance of LocalDate or LocalDateTime and how to fix the error it throws.</p>
<p>The post <a rel="NOFOLLOW" href="https://feeds.feedblitz.com/~/968126756/0/baeldung~How-to-Fix-Jackson-JSON-Parse-Error-Can-not-construct-instance-of-javatimeLocalDate">How to Fix Jackson JSON Parse Error <em>Can not construct instance of java.time.LocalDate</em></a> first appeared on <a rel="NOFOLLOW" href="https://www.baeldung.com">Baeldung</a>.</p><div style="clear:both;padding-top:0.2em;"><a title="Like on Facebook" href="https://feeds.feedblitz.com/_/28/968126756/baeldung"><img height="20" src="https://assets.feedblitz.com/i/fblike20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Pin it!" href="https://feeds.feedblitz.com/_/29/968126756/baeldung,https%3a%2f%2fwww.baeldung.com%2fwp-content%2fuploads%2f2024%2f07%2fJava-Featured-11-1024x536.jpg"><img height="20" src="https://assets.feedblitz.com/i/pinterest20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Post to X.com" href="https://feeds.feedblitz.com/_/24/968126756/baeldung"><img height="20" src="https://assets.feedblitz.com/i/x.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by email" href="https://feeds.feedblitz.com/_/19/968126756/baeldung"><img height="20" src="https://assets.feedblitz.com/i/email20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by RSS" href="https://feeds.feedblitz.com/_/20/968126756/baeldung"><img height="20" src="https://assets.feedblitz.com/i/rss20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a rel="NOFOLLOW" title="View Comments" href="https://www.baeldung.com/java-fix-jackson-json-parse-error#respond"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/comments20.png"></a>&#160;<a title="Follow Comments via RSS" href="https://www.baeldung.com/java-fix-jackson-json-parse-error/feed"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/commentsrss20.png"></a>&#160;</div>]]>
</description>
										<content:encoded><![CDATA[<img src="https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-11-1024x536.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="float: left; margin-right: 5px;" decoding="async" loading="lazy" srcset="https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-11-1024x536.jpg 1024w, https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-11-300x157.jpg 300w, https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-11-768x402.jpg 768w, https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-11-100x52.jpg 100w, https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-11.jpg 1200w" sizes="auto, (max-width: 580px) 100vw, 580px" /><h2 id="bd-overview" data-id="overview">1. Overview</h2>
<div class="bd-anchor" id="overview"></div>
<p>When working with JSON in Java, <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/jackson-object-mapper-tutorial">Jackson</a> usually handles Java 8 date and time types without much configuration. Yet, we can still encounter problems such as one of the common <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/java-jackson-deserialize-particular-type">deserialization</a> parse errors:</p>
<pre><code class="language-text">JSON parse error: Can not construct instance of java.time.LocalDate:
no String-argument constructor/factory method to deserialize from String value</code></pre>
<p>In newer Jackson versions, the error text takes another form:</p>
<pre><code class="language-text">Cannot deserialize value of type java.time.LocalDate from String</code></pre>
<p>Although the wording differs slightly, both errors indicate that Jackson is unable to convert the JSON value into a <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/java-creating-localdate-with-values"><em>LocalDate</em></a> or <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/java-date-to-localdate-and-localdatetime"><em>LocalDateTime</em></a>.</p>
<p>In this tutorial, we&#8217;ll explore the JSON deserialization exception and ways around it. First, we demonstrate the issue with an example. After that, we go through different reasons for the problem at hand and how they can be addressed.</p>
<h2 id="bd-understanding-the-problem" data-id="understanding-the-problem">2. Understanding the Problem</h2>
<div class="bd-anchor" id="understanding-the-problem"></div>
<p>Let&#8217;s write a basic <em>User</em> class:</p>
<pre><code class="language-java">public class User {
    private String name;
    private LocalDate dob;
    // getters and setters
}</code></pre>
<p>Now, let&#8217;s assume we receive a matching JSON:</p>
<pre><code class="language-json">{
  "name": "x",
  "dob": "2006-06-06"
}</code></pre>
<p>Instead of successfully deserializing the JSON, Jackson throws an exception similar to:</p>
<pre><code class="language-text">Can not construct instance of java.time.LocalDate:
no String-argument constructor/factory method to deserialize from String value</code></pre>
<p>Notably, <strong>the JSON data appears to be valid, because the date already follows the ISO-8601 format expected by <em>LocalDate</em></strong>. The problem usually isn&#8217;t the JSON itself but the configuration of Jackson. In fact, the root cause may be a missing Java Time module, an unsupported date format, or using the wrong Java date-time type for the incoming JSON.</p>
<h2 id="bd-verify-jackson-version" data-id="verify-jackson-version">3. Verify Jackson Version</h2>
<div class="bd-anchor" id="verify-jackson-version"></div>
<p>Support for the Java Time API has evolved considerably across Jackson releases. Before delving into specific technical issues and code changes, <strong>we might consider the versions we&#8217;re using</strong>.</p>
<p>In Jackson <em>2.x</em>, Java Time support is provided by the separate <em>jackson-datatype-jsr310</em> module. <strong>In Jackson <em>3.x</em>, this support is built into <em>jackson-databind</em>, so separate module registration isn’t required</strong>.</p>
<p>If the project uses an outdated Jackson version, upgrading Jackson may resolve deserialization issues without any code changes. When it comes to Jackson <em>2.x</em>, we should also verify that the Java Time module is present and properly configured.</p>
<h2 id="bd-ensure-java-time-support" data-id="ensure-java-time-support">4. Ensure Java Time Support</h2>
<div class="bd-anchor" id="ensure-java-time-support"></div>
<p>The most common cause for the JSON error is that the Jackson configuration doesn&#8217;t support the Java 8 Date and Time API.</p>
<p>Unlike older types such as <em>java.util.Date</em>, classes like <em>LocalDate</em>, <em>LocalDateTime</em>, and <em>Instant</em> are provided through the Jackson Java Time module.</p>
<p>If this module is missing, Jackson attempts to instantiate <em>LocalDate</em> like a regular Java object. Since <em>LocalDate</em> has no default constructor or string constructor, deserialization fails.</p>
<p>So, assuming we decide on Jackson <em>2.x</em>, let&#8217;s include the Java Time module dependency:</p>
<pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;com.fasterxml.jackson.datatype&lt;/groupId&gt;
    &lt;artifactId&gt;jackson-datatype-jsr310&lt;/artifactId&gt;
    &lt;version&gt;2.22.1&lt;/version&gt;
&lt;/dependency&gt;</code></pre>
<p>If an <em>ObjectMapper</em> is created manually, it should register the module:</p>
<pre><code class="language-java">ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());</code></pre>
<p>Still, <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/spring-boot-customize-jackson-objectmapper">Spring Boot</a> performs this registration automatically when the module is present on the classpath.</p>
<h2 id="bd-match-json-date-format" data-id="match-json-date-format">5. Match JSON Date Format</h2>
<div class="bd-anchor" id="match-json-date-format"></div>
<p>Another common cause for JSON deserialization errors is the exact date format. If the incoming JSON doesn&#8217;t use the default ISO-8601 format expected by <em>LocalDate</em> and <em>LocalDateTime</em>, we might see an error.</p>
<p>For example, let&#8217;s see a JSON string that can&#8217;t be parsed automatically:</p>
<pre><code class="language-json">{
  "time": "10/20/2020 10:00:10 AM"
}</code></pre>
<p>With the Java Time module registered, Jackson expects an ISO-8601 date-time for a <em>LocalDateTime</em> field by default:</p>
<pre><code class="language-text">2020-10-20T10:00:10</code></pre>
<p>In such cases, when the JSON data uses another format, the field should specify the expected pattern:</p>
<pre><code class="language-java">public class ImportTrans {
    @JsonFormat(pattern = "M/d/yyyy h:mm:ss a")
    private LocalDateTime time;
}</code></pre>
<p>However, this example also contains another hint. The <strong>JSON data can actually contain both a date and a time</strong>, while Java <em>LocalDate</em> fields only store a date.</p>
<h2 id="bd-correct-java-time-type" data-id="correct-java-time-type">6. Correct Java Time Type</h2>
<div class="bd-anchor" id="correct-java-time-type"></div>
<p><em>LocalDate</em> represents only a calendar date. In particular, it doesn&#8217;t contain any information about time of the day.</p>
<p><strong>If the JSON data includes hours, minutes, or seconds, <em>LocalDate</em> is no longer the appropriate target type</strong>:</p>
<pre><code class="language-java">private LocalDate time;</code></pre>
<p>Instead, we should probably use <em>LocalDateTime</em>:</p>
<pre><code class="language-java">private LocalDateTime time;</code></pre>
<p>Furthermore, the matching format can then be declared with <em>@JsonFormat</em>:</p>
<pre><code class="language-java">@JsonFormat(pattern = "M/d/yyyy h:mm:ss a")
private LocalDateTime time;</code></pre>
<p>Choosing the Java type that accurately represents the JSON payload is often the simplest solution.</p>
<h2 id="bd-localdatetime-has-similar-requirements" data-id="localdatetime-has-similar-requirements">7. <em>LocalDateTime</em> Has Similar Requirements</h2>
<div class="bd-anchor" id="localdatetime-has-similar-requirements"></div>
<p>The same type of exception frequently appears when using <em>LocalDateTime</em>:</p>
<pre><code class="language-text">Can not deserialize value of type
java.time.LocalDateTime from String</code></pre>
<p>Let&#8217;s consider the following JSON:</p>
<pre><code class="language-json">{
  "creationTime": "2016-06-16 06:56:00"
}</code></pre>
<p>Same as before, since this format differs from the default ISO-8601 representation, Jackson requires an explicit pattern:</p>
<pre><code class="language-java">@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime creationTime;</code></pre>
<p>The <em>ObjectMapper</em> must also have the Java Time module registered, while the explicit pattern is still required for this non-ISO format.</p>
<h2 id="bd-comparison" data-id="comparison">8. Comparison</h2>
<div class="bd-anchor" id="comparison"></div>
<p>Now that we know different reasons for errors around parsing JSON with date-time values with Jackson, let&#8217;s see an overview of what we found out:</p>
<table class="table-styled" style="width: 100%">
<thead>
<tr>
<th style="width: 16.8878%">Problem</th>
<th style="width: 41.3752%">Typical Error Message</th>
<th style="width: 41.6164%">Likely Cause</th>
</tr>
</thead>
<tbody>
<tr>
<td style="width: 16.8878%">Missing module</td>
<td style="width: 41.3752%"><em>Java 8 date/time type java.time.LocalDate not supported by default</em></td>
<td style="width: 41.6164%">The Java Time module isn&#8217;t registered when using Jackson 2.x</td>
</tr>
<tr>
<td style="width: 16.8878%">Wrong format</td>
<td style="width: 41.3752%"><em>Cannot deserialize value of type java.time.LocalDate from String</em></td>
<td style="width: 41.6164%">The input string doesn&#8217;t match the expected date format</td>
</tr>
<tr>
<td style="width: 16.8878%">Wrong JSON type</td>
<td style="width: 41.3752%"><em>Cannot deserialize value of type java.time.LocalDate from Object value</em></td>
<td style="width: 41.6164%">The JSON value has an incompatible type instead of the expected string</td>
</tr>
</tbody>
</table>
<p>So, we can check the reference table whenever we encounter an unexpected error.</p>
<h2 id="bd-conclusion" data-id="conclusion">9. Conclusion</h2>
<div class="bd-anchor" id="conclusion"></div>
<p>In this article, we explored the error stating that Jackson can&#8217;t construct an instance of <em>LocalDate</em> or <em>LocalDateTime</em>.</p>
<p>Specifically, we established that it generally indicates Jackson can&#8217;t determine how to convert the incoming JSON into a Java date-time object.</p>
<p>In conclusion, once the Java type, JSON payload format, and Jackson configuration are aligned, such deserialization errors should disappear and Java Time types should be handled transparently.</p><p>The post <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/java-fix-jackson-json-parse-error">How to Fix Jackson JSON Parse Error <em>Can not construct instance of java.time.LocalDate</em></a> first appeared on <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com">Baeldung</a>.</p><Img align="left" border="0" height="1" width="1" alt="" style="border:0;float:left;margin:0;padding:0;width:1px!important;height:1px!important;" hspace="0" src="https://feeds.feedblitz.com/~/i/968126756/0/baeldung">
<div style="clear:both;padding-top:0.2em;"><a title="Like on Facebook" href="https://feeds.feedblitz.com/_/28/968126756/baeldung"><img height="20" src="https://assets.feedblitz.com/i/fblike20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Pin it!" href="https://feeds.feedblitz.com/_/29/968126756/baeldung,https%3a%2f%2fwww.baeldung.com%2fwp-content%2fuploads%2f2024%2f07%2fJava-Featured-11-1024x536.jpg"><img height="20" src="https://assets.feedblitz.com/i/pinterest20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Post to X.com" href="https://feeds.feedblitz.com/_/24/968126756/baeldung"><img height="20" src="https://assets.feedblitz.com/i/x.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by email" href="https://feeds.feedblitz.com/_/19/968126756/baeldung"><img height="20" src="https://assets.feedblitz.com/i/email20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by RSS" href="https://feeds.feedblitz.com/_/20/968126756/baeldung"><img height="20" src="https://assets.feedblitz.com/i/rss20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a rel="NOFOLLOW" title="View Comments" href="https://www.baeldung.com/java-fix-jackson-json-parse-error#respond"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/comments20.png"></a>&#160;<a title="Follow Comments via RSS" href="https://www.baeldung.com/java-fix-jackson-json-parse-error/feed"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/commentsrss20.png"></a>&#160;</div>]]>
</content:encoded>
					
					<wfw:commentRss>https://feeds.feedblitz.com/~/968126756/0/baeldung~How-to-Fix-Jackson-JSON-Parse-Error-Can-not-construct-instance-of-javatimeLocalDate/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<webfeeds:featuredImage>https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-11-150x150.jpg</webfeeds:featuredImage></item>
<item>
<feedburner:origLink>https://www.baeldung.com/java-microks-api-mocking-testing</feedburner:origLink>
		<title>API Mocking and Testing With Microcks</title>
		<link>https://feeds.feedblitz.com/~/968126759/0/baeldung~API-Mocking-and-Testing-With-Microcks</link>
					<comments>https://feeds.feedblitz.com/~/968126759/0/baeldung~API-Mocking-and-Testing-With-Microcks#respond</comments>
		
		<dc:creator><![CDATA[Andrei Branza]]></dc:creator>
		<pubDate>Sat, 22 Aug 2026 23:20:24 +0000</pubDate>
				<category><![CDATA[Testing]]></category>
		<category><![CDATA[Testcontainers]]></category>
		<guid isPermaLink="false">https://www.baeldung.com/?p=204680</guid>
					<description><![CDATA[<img src="https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-12-1024x536.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="max-width:100% !important;height:auto !important;float: left; margin-right: 5px;" loading="lazy" /><p>Learn how to use Testcontainers integration to spin up Microcks inside a JUnit5 test.</p>
<p>The post <a rel="NOFOLLOW" href="https://feeds.feedblitz.com/~/968126759/0/baeldung~API-Mocking-and-Testing-With-Microcks">API Mocking and Testing With Microcks</a> first appeared on <a rel="NOFOLLOW" href="https://www.baeldung.com">Baeldung</a>.</p><div style="clear:both;padding-top:0.2em;"><a title="Like on Facebook" href="https://feeds.feedblitz.com/_/28/968126759/baeldung"><img height="20" src="https://assets.feedblitz.com/i/fblike20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Pin it!" href="https://feeds.feedblitz.com/_/29/968126759/baeldung,https%3a%2f%2fwww.baeldung.com%2fwp-content%2fuploads%2f2024%2f07%2fJava-Featured-12-1024x536.jpg"><img height="20" src="https://assets.feedblitz.com/i/pinterest20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Post to X.com" href="https://feeds.feedblitz.com/_/24/968126759/baeldung"><img height="20" src="https://assets.feedblitz.com/i/x.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by email" href="https://feeds.feedblitz.com/_/19/968126759/baeldung"><img height="20" src="https://assets.feedblitz.com/i/email20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by RSS" href="https://feeds.feedblitz.com/_/20/968126759/baeldung"><img height="20" src="https://assets.feedblitz.com/i/rss20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a rel="NOFOLLOW" title="View Comments" href="https://www.baeldung.com/java-microks-api-mocking-testing#respond"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/comments20.png"></a>&#160;<a title="Follow Comments via RSS" href="https://www.baeldung.com/java-microks-api-mocking-testing/feed"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/commentsrss20.png"></a>&#160;</div>]]>
</description>
										<content:encoded><![CDATA[<img src="https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-12-1024x536.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="float: left; margin-right: 5px;" decoding="async" loading="lazy" srcset="https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-12-1024x536.jpg 1024w, https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-12-300x157.jpg 300w, https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-12-768x402.jpg 768w, https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-12-100x52.jpg 100w, https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-12.jpg 1200w" sizes="auto, (max-width: 580px) 100vw, 580px" /><h2 id="bd-introduction" data-id="introduction">1. Introduction</h2>
<div class="bd-anchor" id="introduction"></div>
<p>In this tutorial, we&#8217;ll look at Microcks, an open-source, Kubernetes-native platform for turning API and microservices contracts into live mocks. We&#8217;ll also see how to reuse the same contracts to run conformance tests against a real implemenation.</p>
<p>We&#8217;ll cover the core concepts first, then use the Testcontainers integration to spin up Microcks inside a <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/junit-5">JUnit5</a> test, import an OpenAPI contract, call the generated mock, and validate a running service against it.</p>
<h2 id="bd-what-is-microcks" data-id="what-is-microcks">2. What Is Microcks?</h2>
<div class="bd-anchor" id="what-is-microcks"></div>
<p><strong>Microcks is a CNCF (Cloud Native Computing Foundation) project for API mocking and testing. </strong>It consumes API and microservice artifacts, such as OpenAPI specs, AsyncAPI specs, <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/grpc-introduction">gRPC</a> protobuf files, GraphQL schemas, Postman collections, and SoapUI projects. Then, it turns them into live, stateful mocks in seconds.</p>
<p><strong>Beyond mocking, Microcks reuses these same artifacts to run contract conformance and non-regression tests against a real API implementation. </strong>It also integrates with Jenkins, GitHub Actions and Tekton pipelines through a CLI, so both mocking and testing can be automated across the whole delivery chain.</p>
<p>We can run Microcks as a standalone server, using Docker Compose or a Kubernetes/Helm deployment. For local development and CI, though, it&#8217;s more convenient to embed it directly inside a test using the <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/docker-test-containers">Testcontainers</a> module.</p>
<p>Microcks isn&#8217;t limited to synchronous REST APIs, either. Through its ensemble mode, it can also mock and contract-test SOAP, <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/graphql">GraphQL</a>, and gRPC services, as well as event-driven APIs described with AsyncAPI, over brokers like <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/apache-kafka">Kafka</a>, SQS or SNS. <strong>That means the same tool and the same workflow can cover most of the API styles a typical microservices architecture is likely to use.</strong></p>
<h2 id="bd-setting-up-microcks-with-testcontainers" data-id="setting-up-microcks-with-testcontainers">3. Setting Up Microcks With Testcontainers</h2>
<div class="bd-anchor" id="setting-up-microcks-with-testcontainers"></div>
<p>The <em>microcks-testcontainers </em>library lets us start a lightweight, throwaway Microcks instance for the lifetime of a test class.</p>
<p>First, let&#8217;s add the <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://mvnrepository.com/artifact/io.github.microcks/microcks-testcontainers">Maven</a> dependency:</p>
<pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;io.github.microcks&lt;/groupId&gt;
    &lt;artifactId&gt;microcks-testcontainers&lt;/artifactId&gt;
    &lt;version&gt;0.4.4&lt;/version&gt;
&lt;/dependency&gt;</code></pre>
<p>Next, let&#8217;s create and start a <em>MicrocksContainer, </em>pointing it to an OpenAPI contract for our API:</p>
<pre><code class="language-java">MicrocksContainer microcks = new MicrocksContainer(
  DockerImageName.parse("quay.io/microcks/microcks-uber:1.14.0"))
  .withMainArtifacts("apipastries-openapi.yaml");
microcks.start();</code></pre>
<p>The <em>uber </em>image bundles everything Microcks needs without an external MongoDB dependency, which keeps startup fast. We can also import artifacts after startup, using <em>importAsMainArtifact() </em>or <em>importAsSecondaryArtifact() </em>on the running container.</p>
<h2 id="bd-mocking-the-api" data-id="mocking-the-api">4. Mocking the API</h2>
<div class="bd-anchor" id="mocking-the-api"></div>
<p>Once the container has imported the contract, Microcks automatically exposes a mock endpoint for every operation. We can retrieve the base URL and call it like any real API:</p>
<pre><code class="language-java">String baseApiUrl = microcks.getRestMockEndpoint("API Pastries", "0.0.1");
HttpResponse&lt;String&gt; response = client.send(
  HttpRequest.newBuilder(URI.create(baseApiUrl + "/pastries/Millefeuille")).GET().build(),
  HttpResponse.BodyHandlers.ofString());
assertEquals(200, response.statusCode());</code></pre>
<p><strong>Microcks generates realistic responses straight from the examples defined in the OpenAPI contract.</strong> Then, it cycles through them so repeated calls don&#8217;t always return the same payload. This means consumers can be developed and tested against a dependency that doesn&#8217;t exist yet, or isn&#8217;t reacheable from a test environment.</p>
<p>Furthermore, there are ways we can confirm a mock was actually called. We&#8217;ll do this by either calling <em>microcks.verify(&#8220;API Pastries&#8221;,&#8221;0.0.1&#8243;)</em>, or by checking the exact invocation count through <em>microcks.getServiceInvocationsCount(&#8220;API Pastries&#8221;,&#8221;0.0.1&#8243;)</em>.</p>
<h2 id="bd-running-contract-tests" data-id="running-contract-tests">5. Running Contract Tests</h2>
<div class="bd-anchor" id="running-contract-tests"></div>
<p>The same contract can validate a real implementation running on our machine. First, we need to expose the port with Testcontainers.exposeHostPorts() before starting the container, not after. We do this since the Microcks container needs to reach back out to our host:</p>
<pre><code class="language-java">Testcontainers.exposeHostPorts(port);
microcks.start();</code></pre>
<p>Then, let&#8217;s launch a conformance test against our locally running server:</p>
<pre><code class="language-java">TestRequest testRequest = new TestRequest.Builder()
  .serviceId("API Pastries:0.0.1")
  .runnerType(TestRunnerType.OPEN_API_SCHEMA.name())
  .testEndpoint("http://host.testcontainers.internal:" + port)
  .filteredOperations(List.of("GET /pastries/{name}"))
  .timeout(Duration.ofSeconds(2))
  .build();
TestResult testResult = microcks.testEndpoint(testRequest);
assertTrue(testResult.isSuccess());</code></pre>
<p>We scoped the test to a single operation with filteredOperations(). Without it, Microcks also replays the GET /pastries and PATCH /pastries/{name} examples from the contract, which fail against an implementation that doesn&#8217;t cover them yet.</p>
<p><strong>Here, Microcks replays every example from the OpenAPI contract against our running application and checks each response against the schema. </strong>For a more JUnit-friendly failure report, we can use the <em>Assertions </em>helper instead, <em>Assertions.assertSuccess(testResult);</em></p>
<p>This single call runs the same conformance check that a CI pipeline would run against a live deployment, only locally and in milliseconds.</p>
<h2 id="bd-conclusion" data-id="conclusion">6. Conclusion</h2>
<div class="bd-anchor" id="conclusion"></div>
<p>In this article, we introduced Microcks as a tool for turning API contracts into live mocks and reusing those same contracts for conformance testing. We used its Testcontainers integration to start a throwaway Microcks instance inside a JUnit test, import an OpenAPI contract, call the generated mock, and run a contract test against a live implementation.</p>
<p><strong>Because mocks and tests are dervide from the same source contract, both stay in sync with the API definition. </strong>That removes a common source of drift between what an API promises and what it actually does.</p>
<p>As always, the code is available over on <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/eugenp/tutorials/tree/master/testing-modules">Github</a>.</p><p>The post <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/java-microks-api-mocking-testing">API Mocking and Testing With Microcks</a> first appeared on <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com">Baeldung</a>.</p><Img align="left" border="0" height="1" width="1" alt="" style="border:0;float:left;margin:0;padding:0;width:1px!important;height:1px!important;" hspace="0" src="https://feeds.feedblitz.com/~/i/968126759/0/baeldung">
<div style="clear:both;padding-top:0.2em;"><a title="Like on Facebook" href="https://feeds.feedblitz.com/_/28/968126759/baeldung"><img height="20" src="https://assets.feedblitz.com/i/fblike20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Pin it!" href="https://feeds.feedblitz.com/_/29/968126759/baeldung,https%3a%2f%2fwww.baeldung.com%2fwp-content%2fuploads%2f2024%2f07%2fJava-Featured-12-1024x536.jpg"><img height="20" src="https://assets.feedblitz.com/i/pinterest20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Post to X.com" href="https://feeds.feedblitz.com/_/24/968126759/baeldung"><img height="20" src="https://assets.feedblitz.com/i/x.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by email" href="https://feeds.feedblitz.com/_/19/968126759/baeldung"><img height="20" src="https://assets.feedblitz.com/i/email20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by RSS" href="https://feeds.feedblitz.com/_/20/968126759/baeldung"><img height="20" src="https://assets.feedblitz.com/i/rss20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a rel="NOFOLLOW" title="View Comments" href="https://www.baeldung.com/java-microks-api-mocking-testing#respond"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/comments20.png"></a>&#160;<a title="Follow Comments via RSS" href="https://www.baeldung.com/java-microks-api-mocking-testing/feed"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/commentsrss20.png"></a>&#160;</div>]]>
</content:encoded>
					
					<wfw:commentRss>https://feeds.feedblitz.com/~/968126759/0/baeldung~API-Mocking-and-Testing-With-Microcks/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<webfeeds:featuredImage>https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-12-150x150.jpg</webfeeds:featuredImage></item>
<item>
<feedburner:origLink>https://www.baeldung.com/spring-boot-http-client-ssrf-mitigation-inetaddressfilter</feedburner:origLink>
		<title>HTTP Client SSRF Mitigation with InetAddressFilter in Spring Boot</title>
		<link>https://feeds.feedblitz.com/~/968126762/0/baeldung~HTTP-Client-SSRF-Mitigation-with-InetAddressFilter-in-Spring-Boot</link>
					<comments>https://feeds.feedblitz.com/~/968126762/0/baeldung~HTTP-Client-SSRF-Mitigation-with-InetAddressFilter-in-Spring-Boot#respond</comments>
		
		<dc:creator><![CDATA[Hamid Reza Sharifi]]></dc:creator>
		<pubDate>Sat, 22 Aug 2026 23:16:19 +0000</pubDate>
				<category><![CDATA[Spring Boot]]></category>
		<category><![CDATA[Spring Boot 4]]></category>
		<guid isPermaLink="false">https://www.baeldung.com/?p=204677</guid>
					<description><![CDATA[<img src="https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-13-1024x536.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="max-width:100% !important;height:auto !important;float: left; margin-right: 5px;" loading="lazy" /><p>Learn how to use the InetAddressFilter class in a Spring-based application to limit the address that an HTTP client can call.</p>
<p>The post <a rel="NOFOLLOW" href="https://feeds.feedblitz.com/~/968126762/0/baeldung~HTTP-Client-SSRF-Mitigation-with-InetAddressFilter-in-Spring-Boot">HTTP Client SSRF Mitigation with InetAddressFilter in Spring Boot</a> first appeared on <a rel="NOFOLLOW" href="https://www.baeldung.com">Baeldung</a>.</p><div style="clear:both;padding-top:0.2em;"><a title="Like on Facebook" href="https://feeds.feedblitz.com/_/28/968126762/baeldung"><img height="20" src="https://assets.feedblitz.com/i/fblike20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Pin it!" href="https://feeds.feedblitz.com/_/29/968126762/baeldung,https%3a%2f%2fwww.baeldung.com%2fwp-content%2fuploads%2f2024%2f07%2fJava-Featured-13-1024x536.jpg"><img height="20" src="https://assets.feedblitz.com/i/pinterest20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Post to X.com" href="https://feeds.feedblitz.com/_/24/968126762/baeldung"><img height="20" src="https://assets.feedblitz.com/i/x.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by email" href="https://feeds.feedblitz.com/_/19/968126762/baeldung"><img height="20" src="https://assets.feedblitz.com/i/email20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by RSS" href="https://feeds.feedblitz.com/_/20/968126762/baeldung"><img height="20" src="https://assets.feedblitz.com/i/rss20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a rel="NOFOLLOW" title="View Comments" href="https://www.baeldung.com/spring-boot-http-client-ssrf-mitigation-inetaddressfilter#respond"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/comments20.png"></a>&#160;<a title="Follow Comments via RSS" href="https://www.baeldung.com/spring-boot-http-client-ssrf-mitigation-inetaddressfilter/feed"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/commentsrss20.png"></a>&#160;</div>]]>
</description>
										<content:encoded><![CDATA[<img src="https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-13-1024x536.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="float: left; margin-right: 5px;" decoding="async" loading="lazy" srcset="https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-13-1024x536.jpg 1024w, https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-13-300x157.jpg 300w, https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-13-768x402.jpg 768w, https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-13-100x52.jpg 100w, https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-13.jpg 1200w" sizes="auto, (max-width: 580px) 100vw, 580px" /><h2 id="bd-introduction" data-id="introduction">1. Introduction</h2><div class="bd-anchor" id="introduction"></div>
<p>It’s sometimes useful to limit the remote addresses that an HTTP client is permitted to call. It helps prevent applications from connecting to unintended destinations and provides an effective defense against Server-Side Request Forgery (<a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/cs/server-side-request-forgery">SSRF</a>) attacks. <strong>To limit the address that an HTTP client can call, we can use an <em>InetAddressFilter.</em> The filter determines which addresses are allowed for outgoing requests.</strong> In this tutorial, we’ll explore how to use the <em>InetAddressFilter</em> class in a Spring-based application.</p>
<h2 id="bd-understanding-ssrf-in-the-context-of-http-clients" data-id="understanding-ssrf-in-the-context-of-http-clients">2. Understanding SSRF in the Context of HTTP Clients</h2><div class="bd-anchor" id="understanding-ssrf-in-the-context-of-http-clients"></div>
<p><strong>SSRF is a critical security vulnerability that allows an attacker to force a server to make requests to unintended locations. These requests may target internal systems, cloud metadata endpoints, or loopback addresses.</strong> To demonstrate how SSRF can occur in an HTTP client, let&#8217;s consider a simple endpoint that accepts a URL and uses <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/spring-boot-restclient"><em>RestClient</em></a> to retrieve its contents:</p>
<pre><code class="language-java">@GetMapping("/fetch")
public String fetch(@RequestParam String url) {
    return restClient.get()
      .uri(url)
      .retrieve()
      .body(String.class);
}</code></pre>
<p>The endpoint accepts a URL as a request parameter and forwards it to <em>RestClient</em>. The intention is to allow users to retrieve content from public URLs. For example, a legitimate request might look like:</p>
<pre><code class="language-plaintext">curl --location http://127.0.0.1:8080/fetch?url=https://www.baeldung.com</code></pre>
<p>However, an attacker could provide a URL that points to an internal resource instead:</p>
<pre><code class="language-plaintext">curl --location http://127.0.0.1:8080/fetch?url=admin</code></pre>
<p>Instead of contacting a public website, our server now makes requests to its own internal network. This can expose internal admin APIs, databases, Kubernetes services, etc. This class of vulnerability is known as SSRF. Historically, mitigating SSRF in Spring Boot applications required custom <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/spring-rest-template-interceptor#interceptor"><em>ClientHttpRequestInterceptor</em></a> implementations or complex network-level firewalls. <strong>Spring Boot 4.1.0 introduces first-class support for SSRF mitigation via the <em>InetAddressFilter</em> interface</strong>.</p>
<h2 id="bd-maven-dependency" data-id="maven-dependency">3. Maven Dependency</h2><div class="bd-anchor" id="maven-dependency"></div>
<p>Let’s start by importing the <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-restclient"><em>spring-boot-starter-restclient</em></a> dependency to our <em>pom.xml</em>:</p>
<pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-restclient&lt;/artifactId&gt;
    &lt;version&gt;4.1.1-SNAPSHOT&lt;/version&gt;
&lt;/dependency&gt;</code></pre>
<h2 id="bd-introducing-inetaddressfilter" data-id="introducing-inetaddressfilter">4. Introducing <em>InetAddressFilter</em></h2><div class="bd-anchor" id="introducing-inetaddressfilter"></div>
<p>The <em>InetAddressFilter</em> interface is a <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/java-8-functional-interfaces">functional interface</a>. Unlike validating the hostname or URL, <em>InetAddressFilter</em> performs its checks after DNS resolution. Spring Boot provides several built-in filters for common scenarios, such as allowing only public addresses (<em>InetAddressFilter.externalAddresses()</em>) or blocking special-purpose network ranges (<em>InetAddressFilter.internalAddresses()</em>). <strong>We can combine these filters using logical operators</strong>:</p>
<pre><code class="language-java">InetAddressFilter.of("192.168.0.0/16")
  .andNot("192.168.1.100");</code></pre>
<p>The API also supports <em>and()</em>, <em>or()</em>, and <em>negate()</em> for composing more complex rules:</p>
<pre><code class="language-java">InetAddressFilter.externalAddresses()
  .or(InetAddressFilter.of("10.20.0.0"));</code></pre>
<p>This enables expressive allowlists and blocklists without custom networking code.</p>
<h2 id="bd-applying-the-filter-globally" data-id="applying-the-filter-globally">5. Applying the Filter Globally</h2><div class="bd-anchor" id="applying-the-filter-globally"></div>
<p><strong>The simplest and most common approach is to declare an <em>InetAddressFilter</em> bean</strong>. Spring Boot then automatically applies it to every auto-configured HTTP client builder (<em>RestClient</em>, <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/spring-5-webclient"><em>WebClient</em></a>, <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/rest-template"><em>RestTemplate</em></a>, etc.). Let&#8217;s configure the <em>InetAddressFilter</em> to allow connections only to external IP addresses:</p>
<pre><code class="language-java">@Configuration(proxyBeanMethods = false)
public class HttpClientSecurityConfig {
    @Bean
    public InetAddressFilter httpClientInetAddressFilter() {
        return InetAddressFilter.externalAddresses();
    }
}</code></pre>
<p>With this single bean, any client created from the auto-configured builders will reject requests whose resolved address is not external. Now, we can create a <em>RestClient</em> without any additional security configuration:</p>
<pre><code class="language-java">@Bean
RestClient restClient(RestClient.Builder builder) {
    return builder.build();
}</code></pre>
<p>Because the <em>RestClient.Builder</em> has already been customized by Spring Boot, the resulting <em>RestClient</em> automatically uses the configured <em>InetAddressFilter</em>. As a result, any request whose resolved destination is not an external address—for example, a loopback or private IP address—is rejected before a connection is established. This approach centralizes the application&#8217;s outbound network policy in a single place and ensures that all auto-configured HTTP clients consistently enforce the same SSRF protection.</p>
<h2 id="bd-applying-the-filter-per-client" data-id="applying-the-filter-per-client">6. Applying the Filter Per-Client</h2><div class="bd-anchor" id="applying-the-filter-per-client"></div>
<p>In some scenarios, applying a global <em>InetAddressFilter</em> may be too restrictive. For example, an application might communicate with both public APIs and specific internal services, each requiring different outbound access rules. In such cases, <strong>we can apply an <em>InetAddressFilter</em> to an individual HTTP client by configuring the underlying <em>HttpClientSettings</em> when creating the client</strong>. Let&#8217;s create a <em>RestClient</em> that only allows requests to external addresses:</p>
<pre><code class="language-java">@Bean
RestClient restClient(RestClient.Builder builder) {
    InetAddressFilter onlyExternalAddresses = InetAddressFilter.externalAddresses();
    HttpClientSettings settings = HttpClientSettings.defaults()
      .withInetAddressFilter(onlyExternalAddresses);
    ClientHttpRequestFactory requestFactory = ClientHttpRequestFactoryBuilder
      .jdk().build(settings);
    return builder
      .requestFactory(requestFactory)
      .build();
}</code></pre>
<p>Here, the filter is attached only to this specific <em>RestClient</em> instance. Other HTTP clients in the application can use different security policies or remain unaffected. This approach is useful when different integrations have different network requirements. For example, a client used to call third-party APIs can be restricted to public addresses, while another client used for communication with trusted internal services can use a more specific allowlist. By configuring the filter at the client level, we can apply fine-grained outbound request controls without changing the behavior of the entire application.</p>
<h2 id="bd-test-1" data-id="test-1">7. Test</h2><div class="bd-anchor" id="test-1"></div>
<p>To verify that the configured <em>InetAddressFilter</em> is applied, we can write a unit test that uses the configured <em>RestClient</em> bean to access a loopback address.<strong> The application is configured with <em>InetAddressFilter.externalAddresses()</em>, which permits only publicly routable IP addresses.</strong> Since <em>127.0.0.1</em> is a loopback address, the request is rejected before an HTTP connection is established. Let&#8217;s create a test case:</p>
<pre><code class="language-java">@Test
void whenCallingLoopbackAddress_thenFilteredHostExceptionIsThrown() {
    assertThatThrownBy(() -&gt;
        restClient.get()
          .uri("https://127.0.0.1:8080")
          .retrieve()
          .toBodilessEntity()
    ).isInstanceOf(FilteredHostException.class)
     .hasMessageContaining("127.0.0.1");
}</code></pre>
<p>When the test executes, the <em>RestClient</em> resolves the destination host and passes the resulting <em>InetAddress</em> to the configured <em>InetAddressFilter</em>. Because the address does not satisfy the <em>externalAddresses()</em> policy, Spring Boot throws a <em>FilteredHostException</em> and aborts the request before opening a network connection.</p>
<h2 id="bd-conclusion" data-id="conclusion">8. Conclusion</h2><div class="bd-anchor" id="conclusion"></div>
<p>In this tutorial, we explored how SSRF can affect applications that make outbound HTTP requests based on user-controlled input. We also looked at how Spring Boot 4.1 simplifies SSRF mitigation with <em>InetAddressFilter</em>. It allows applications to validate the resolved destination address before establishing an outbound connection. The feature integrates with Spring&#8217;s HTTP client infrastructure, including both blocking and reactive clients, and provides a flexible API for composing allowlists and blocklists. By applying <em>InetAddressFilter</em>, we can enforce outbound network access policies at the HTTP client level. This reduces the risk of unauthorized access to internal resources while keeping the application code clean and maintainable.</p>
<p>As always, the source code is available <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/eugenp/tutorials/tree/master/spring-boot-modules/spring-boot-client-2">over on GitHub</a>.</p><p>The post <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/spring-boot-http-client-ssrf-mitigation-inetaddressfilter">HTTP Client SSRF Mitigation with InetAddressFilter in Spring Boot</a> first appeared on <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com">Baeldung</a>.</p><Img align="left" border="0" height="1" width="1" alt="" style="border:0;float:left;margin:0;padding:0;width:1px!important;height:1px!important;" hspace="0" src="https://feeds.feedblitz.com/~/i/968126762/0/baeldung">
<div style="clear:both;padding-top:0.2em;"><a title="Like on Facebook" href="https://feeds.feedblitz.com/_/28/968126762/baeldung"><img height="20" src="https://assets.feedblitz.com/i/fblike20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Pin it!" href="https://feeds.feedblitz.com/_/29/968126762/baeldung,https%3a%2f%2fwww.baeldung.com%2fwp-content%2fuploads%2f2024%2f07%2fJava-Featured-13-1024x536.jpg"><img height="20" src="https://assets.feedblitz.com/i/pinterest20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Post to X.com" href="https://feeds.feedblitz.com/_/24/968126762/baeldung"><img height="20" src="https://assets.feedblitz.com/i/x.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by email" href="https://feeds.feedblitz.com/_/19/968126762/baeldung"><img height="20" src="https://assets.feedblitz.com/i/email20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by RSS" href="https://feeds.feedblitz.com/_/20/968126762/baeldung"><img height="20" src="https://assets.feedblitz.com/i/rss20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a rel="NOFOLLOW" title="View Comments" href="https://www.baeldung.com/spring-boot-http-client-ssrf-mitigation-inetaddressfilter#respond"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/comments20.png"></a>&#160;<a title="Follow Comments via RSS" href="https://www.baeldung.com/spring-boot-http-client-ssrf-mitigation-inetaddressfilter/feed"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/commentsrss20.png"></a>&#160;</div>]]>
</content:encoded>
					
					<wfw:commentRss>https://feeds.feedblitz.com/~/968126762/0/baeldung~HTTP-Client-SSRF-Mitigation-with-InetAddressFilter-in-Spring-Boot/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<webfeeds:featuredImage>https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-13-150x150.jpg</webfeeds:featuredImage></item>
<item>
<feedburner:origLink>https://www.baeldung.com/java-db-scheduler</feedburner:origLink>
		<title>DB-Scheduler: A Persistent, Cluster-Friendly Scheduler for Java</title>
		<link>https://feeds.feedblitz.com/~/968126534/0/baeldung~DBScheduler-A-Persistent-ClusterFriendly-Scheduler-for-Java</link>
					<comments>https://feeds.feedblitz.com/~/968126534/0/baeldung~DBScheduler-A-Persistent-ClusterFriendly-Scheduler-for-Java#respond</comments>
		
		<dc:creator><![CDATA[Graham Cox]]></dc:creator>
		<pubDate>Sat, 22 Aug 2026 23:11:36 +0000</pubDate>
				<category><![CDATA[Persistence]]></category>
		<category><![CDATA[popular]]></category>
		<category><![CDATA[Quartz]]></category>
		<guid isPermaLink="false">https://www.baeldung.com/java-db-scheduler</guid>
					<description><![CDATA[<img src="https://www.baeldung.com/wp-content/uploads/2024/11/Persistence-Featured-Image-04-1024x536.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="max-width:100% !important;height:auto !important;float: left; margin-right: 5px;" loading="lazy" /><p>Learn how to use the db-scheduler library to store, manage and execute persistent scheduled tasks in Java.</p>
<p>The post <a rel="NOFOLLOW" href="https://feeds.feedblitz.com/~/968126534/0/baeldung~DBScheduler-A-Persistent-ClusterFriendly-Scheduler-for-Java">DB-Scheduler: A Persistent, Cluster-Friendly Scheduler for Java</a> first appeared on <a rel="NOFOLLOW" href="https://www.baeldung.com">Baeldung</a>.</p><div style="clear:both;padding-top:0.2em;"><a title="Like on Facebook" href="https://feeds.feedblitz.com/_/28/968126534/baeldung"><img height="20" src="https://assets.feedblitz.com/i/fblike20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Pin it!" href="https://feeds.feedblitz.com/_/29/968126534/baeldung,https%3a%2f%2fwww.baeldung.com%2fwp-content%2fuploads%2f2024%2f11%2fPersistence-Featured-Image-04-1024x536.jpg"><img height="20" src="https://assets.feedblitz.com/i/pinterest20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Post to X.com" href="https://feeds.feedblitz.com/_/24/968126534/baeldung"><img height="20" src="https://assets.feedblitz.com/i/x.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by email" href="https://feeds.feedblitz.com/_/19/968126534/baeldung"><img height="20" src="https://assets.feedblitz.com/i/email20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by RSS" href="https://feeds.feedblitz.com/_/20/968126534/baeldung"><img height="20" src="https://assets.feedblitz.com/i/rss20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a rel="NOFOLLOW" title="View Comments" href="https://www.baeldung.com/java-db-scheduler#respond"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/comments20.png"></a>&#160;<a title="Follow Comments via RSS" href="https://www.baeldung.com/java-db-scheduler/feed"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/commentsrss20.png"></a>&#160;</div>]]>
</description>
										<content:encoded><![CDATA[<img src="https://www.baeldung.com/wp-content/uploads/2024/11/Persistence-Featured-Image-04-1024x536.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="float: left; margin-right: 5px;" decoding="async" loading="lazy" srcset="https://www.baeldung.com/wp-content/uploads/2024/11/Persistence-Featured-Image-04-1024x536.jpg 1024w, https://www.baeldung.com/wp-content/uploads/2024/11/Persistence-Featured-Image-04-300x157.jpg 300w, https://www.baeldung.com/wp-content/uploads/2024/11/Persistence-Featured-Image-04-768x402.jpg 768w, https://www.baeldung.com/wp-content/uploads/2024/11/Persistence-Featured-Image-04-100x52.jpg 100w, https://www.baeldung.com/wp-content/uploads/2024/11/Persistence-Featured-Image-04-600x314.jpg 600w, https://www.baeldung.com/wp-content/uploads/2024/11/Persistence-Featured-Image-04.jpg 1200w" sizes="auto, (max-width: 580px) 100vw, 580px" /><h2 id="bd-introduction" data-id="introduction"><strong>1. Introduction</strong></h2>
<div class="bd-anchor" id="introduction"></div>
<p>In this tutorial, we’ll take a look at <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/kagkarlsson/db-scheduler">db-scheduler</a>, a simpler alternative to <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/quartz">Quartz</a>. We’ll see what it is, how to use it and what we can do with it.</p>
<h2 id="bd-what-is-db-scheduler" data-id="what-is-db-scheduler"><strong>2. What Is DB-Scheduler?</strong></h2>
<div class="bd-anchor" id="what-is-db-scheduler"></div>
<p><strong>DB-Scheduler is a cluster-aware, persistent scheduling library</strong>. It&#8217;s designed to be simple to include in our application. Our only requirements are Java 17+ and a JDBC connection to a database with a single database table in which we store and manage our tasks.</p>
<p>Using db-scheduler, we can easily support:</p>
<ul>
<li>One-time tasks: tasks that are scheduled to run once at a specific point in time</li>
<li>Simple recurring tasks: tasks that are set up to run on a statically defined regular schedule</li>
<li>Dynamic recurring tasks: tasks that run on a regular schedule, but that we can schedule dynamically at runtime</li>
</ul>
<p>The db-scheduler library will then handle everything for us. <strong>This includes ensuring that these jobs run correctly across a cluster of services without dropping them or running them multiple times.</strong></p>
<h2 id="bd-setting-up" data-id="setting-up"><strong>3. Setting Up</strong></h2>
<div class="bd-anchor" id="setting-up"></div>
<p>Before we can use db-scheduler, we need to first set it up in our application. This includes adding it to our project and getting it running.</p>
<h3 id="bd-1-dependencies" data-id="1-dependencies"><strong>3.1. Dependencies</strong></h3>
<div class="bd-anchor" id="1-dependencies"></div>
<p><strong>Before using db-scheduler, we need to include the <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://mvnrepository.com/artifact/com.github.kagkarlsson/db-scheduler">latest version</a> in our build, which is <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://mvnrepository.com/artifact/com.github.kagkarlsson/db-scheduler/16.12.0">16.12.0</a> at the time of writing.</strong></p>
<p>If we’re using Maven, we can include this dependency in our <em>pom.xml</em> file:</p>
<pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;com.github.kagkarlsson&lt;/groupId&gt;
    &lt;artifactId&gt;db-scheduler&lt;/artifactId&gt;
    &lt;version&gt;16.12.0&lt;/version&gt;
&lt;/dependency&gt;</code></pre>
<p>This will bring in everything that we need to be able to use it in our application. The only required dependencies are <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/java-jspecify-null-safety">JSpecify</a> and <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/slf4j-with-log4j2-logback">SLF4J</a> &#8211; everything else is self-contained.</p>
<h3 id="bd-2-database-schema" data-id="2-database-schema"><strong>3.2. Database Schema</strong></h3>
<div class="bd-anchor" id="2-database-schema"></div>
<p><strong>In order to work, db-scheduler needs a single database table in the database we connect it to.</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/kagkarlsson/db-scheduler#database-compatibility">DDL scripts</a> are provided for most major RDBMS engines, though it might work on others as well. For example, to use with <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/kagkarlsson/db-scheduler/blob/master/db-scheduler/src/test/resources/postgresql_tables.sql">PostgreSQL</a>, we&#8217;d use DDL like this:</p>
<pre><code class="language-sql">CREATE TABLE scheduled_tasks (
  task_name TEXT NOT NULL,
  task_instance TEXT NOT NULL,
  task_data BYTEA,
  execution_time TIMESTAMP WITH TIME ZONE NOT NULL,
  picked BOOLEAN NOT NULL,
  picked_by TEXT,
  last_success TIMESTAMP WITH TIME ZONE,
  last_failure TIMESTAMP WITH TIME ZONE,
  consecutive_failures INT,
  last_heartbeat TIMESTAMP WITH TIME ZONE,
  version BIGINT NOT NULL,
  priority SMALLINT,
  PRIMARY KEY (task_name, task_instance)
);
CREATE INDEX execution_time_idx ON scheduled_tasks (execution_time);
CREATE INDEX last_heartbeat_idx ON scheduled_tasks (last_heartbeat);
CREATE INDEX priority_execution_time_idx on scheduled_tasks (priority desc, execution_time asc);</code></pre>
<p>This creates our <em>scheduled_tasks </em>table with the expected structure, ready for db-scheduler to use.</p>
<h3 id="bd-3-scheduler" data-id="3-scheduler"><strong>3.3. Scheduler</strong></h3>
<div class="bd-anchor" id="3-scheduler"></div>
<p>Finally, before we can use db-scheduler, we need to actually set up a <em>Scheduler </em>instance. <strong>This is the central class that manages scheduling all of our jobs.</strong></p>
<p>We create an instance of this by providing a <em>DataSource </em>pointing to the database containing our <em>scheduled_tasks</em> table, and a collection of all the tasks that we want to manage. We&#8217;ll see later how to create these:</p>
<pre><code class="language-java">Scheduler scheduler = Scheduler.create(dataSource)
  .startTasks(tasks)
  .registerShutdownHook()
  .build();</code></pre>
<p>This comes with a reasonable set of default configuration values:</p>
<ul>
<li>Poll every 10 seconds</li>
<li>Heartbeat every 5 minutes</li>
<li>Consider a task dead if it misses 6 heartbeats</li>
<li>Run jobs across 10 worker threads</li>
</ul>
<p>And many more. We can configure all of these when we create our scheduler:</p>
<pre><code class="language-java">Scheduler scheduler = Scheduler.create(dataSource)
  .startTasks(tasks)
  .registerShutdownHook()
  .pollingInterval(Duration.ofSeconds(2))
  .heartbeatInterval(Duration.ofMinutes(2))
  .missedHeartbeatsLimit(10)
  .threads(5)
  .build();</code></pre>
<p><strong>It&#8217;s important that multiple instances in the same cluster use at least the same heartbeat settings.</strong> If not, db-scheduler might incorrectly consider some jobs to be dead.</p>
<p>Once we have a scheduler, we need to start it running:</p>
<pre><code class="language-java">scheduler.start();</code></pre>
<p>At this point, the scheduler will now manage all of the tasks assigned to it. This includes ensuring they&#8217;re correctly scheduled and running them at the appropriate times.</p>
<h2 id="bd-simple-recurring-tasks" data-id="simple-recurring-tasks"><strong>4. Simple Recurring Tasks</strong></h2>
<div class="bd-anchor" id="simple-recurring-tasks"></div>
<p><strong>The easiest tasks to work with are simple recurring tasks. For these, we need a unique name, a frequency at which the task runs, and the task itself:</strong></p>
<pre><code class="language-java">RecurringTask&lt;Void&gt; task = Tasks.recurring("my-hourly-task", FixedDelay.ofHours(1))
  .execute((instance, context) -&gt; {
    LOG.info("Executed!");
  });</code></pre>
<p>If this is a new task that the scheduler has never seen before, it will be scheduled to run immediately. After that, the scheduler will run it according to the provided schedule. In this case, that means 1 hour after the previous iteration finished. We can also use <em>Daily </em>for running at a particular time every day, and <em>CronSchedule </em>for using a more complicated <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/cron-expressions">cron expression</a>.</p>
<p>Our task itself is provided as an instance of <em>VoidExecutionHandler&lt;T&gt;</em>, which is itself a <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/java-8-lambda-expressions-tips#functionalInterface">functional interface</a> and can be implemented by a lambda if we wish. The only method of this interface takes two parameters:</p>
<ul>
<li><em>TaskInstance&lt;T&gt; instance</em> &#8211; Details about this instance of the task. Useful if we&#8217;ve got multiple schedules using the same task implementation.</li>
<li><em>ExecutionContext context </em>&#8211; Details about the executor itself. Useful if we want to access the scheduler itself from within our task.</li>
</ul>
<p>Note that we have <em>VoidExecutionHandler</em> and <em>RecurringTask&lt;Void&gt;</em> here. This is because our example task is stateless. We can also support stateful tasks, which have an initial state that can be updated as a result of the task executing. However, these are out of the scope of this article.</p>
<h2 id="bd-one-time-tasks" data-id="one-time-tasks"><strong>5. One-Time Tasks</strong></h2>
<div class="bd-anchor" id="one-time-tasks"></div>
<p><strong>One-time tasks work a bit differently. In this case, we need to distinguish between the task itself and a descriptor that identifies the task:</strong></p>
<pre><code class="language-java">TaskDescriptor&lt;String&gt; taskDescriptor = TaskDescriptor.of("my-onetime-task", String.class);
Task&lt;String&gt; task = Tasks.oneTime(taskDescriptor)
  .execute((inst, ctx) -&gt; {
    LOG.info("Executed! Custom data {}, Instance {}", inst.getData(), inst.getId());
  });
</code></pre>
<p>This task definition accepts some data that we provide when we schedule the task, and can act on that data as appropriate. In this case, the data is a <em>String</em>, but it can be any type that db-scheduler is able to serialize. By default, this uses <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/java-serialization">Java Serialization</a> but db-scheduler also supports other mechanisms, such as Jackson and Gson.</p>
<p>We then need to register the task with our scheduler. However, we do this by passing the task to the <em>Scheduler.create()</em> method so that db-scheduler knows about it but doesn&#8217;t immediately start running it:</p>
<pre><code class="language-java">Scheduler scheduler = Scheduler.create(dataSource, task)
  .build();
</code></pre>
<p><strong>At this point, we can run our task at any time using our scheduler and task descriptor:</strong></p>
<pre><code class="language-java">scheduler.schedule(taskDescriptor.instance(UUID.randomUUID().toString())
  .data("Hello")
  .scheduledTo(Instant.now().plusSeconds(5))
);
</code></pre>
<p>Here we have to provide a unique instance ID for our task, and indicate when the task should run. We&#8217;ve also provided some data for our task to act on. The scheduler will then ensure that this task runs correctly at the desired time.</p>
<h2 id="bd-dynamic-recurring-tasks" data-id="dynamic-recurring-tasks"><strong>6. Dynamic Recurring Tasks</strong></h2>
<div class="bd-anchor" id="dynamic-recurring-tasks"></div>
<p><strong>Dynamic recurring tasks are tasks that are registered dynamically at runtime, similar to our one-time tasks. However, once registered, they&#8217;ll continue to run on a schedule similar to our simple recurring tasks.</strong></p>
<p>We create and start these exactly the same as for our one-time task, only using <em>Tasks.recurring()</em> instead:</p>
<pre><code class="language-java">TaskDescriptor&lt;String&gt; taskDescriptor = TaskDescriptor.of("my-dynamic-recurring-task", String.class);
Task&lt;String&gt; task = Tasks.recurring(taskDescriptor, new CronSchedule("*/5 * * * * ?", ZoneId.of("UTC")))
  .execute((inst, ctx) -&gt; {
    LOG.info("Executed! Custom data {}, Instance {}", inst.getData(), inst.getId());
  });
scheduler.schedule(taskDescriptor.instance(UUID.randomUUID().toString())
  .data("Hello")
  .scheduledTo(Instant.now().plusSeconds(15))
);
</code></pre>
<p>This will now start our task at the given time and then, once started, it&#8217;ll run at the given schedule. In this case, it first runs 15 seconds after the current time, and then every 5 seconds after that.</p>
<p>If we wish, we can schedule as many different instances of these as we need, and they&#8217;ll all run on their own schedule without interfering with each other.</p>
<h2 id="bd-use-with-spring-boot" data-id="use-with-spring-boot"><strong>7. Use With Spring Boot</strong></h2>
<div class="bd-anchor" id="use-with-spring-boot"></div>
<p><strong>If we&#8217;re using Spring Boot, db-scheduler provides a <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/spring-boot-starters">starter</a> dependency that we can use:</strong></p>
<pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;com.github.kagkarlsson&lt;/groupId&gt;
    &lt;artifactId&gt;db-scheduler-spring-boot-4-starter&lt;/artifactId&gt;
    &lt;version&gt;16.12.0&lt;/version&gt;
&lt;/dependency&gt;</code></pre>
<p>If we&#8217;re using Spring Boot 4.x then we need to use <em><a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://mvnrepository.com/artifact/com.github.kagkarlsson/db-scheduler-spring-boot-4-starter">db-scheduler-spring-boot-4-starter</a></em>. If we&#8217;re still on Spring Boot 3.x then instead it&#8217;s <em><a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://mvnrepository.com/artifact/com.github.kagkarlsson/db-scheduler-spring-boot-starter">db-scheduler-spring-boot-starter</a></em>.</p>
<p>This will automatically create and start our <em>Scheduler </em>instance as a Spring bean. On doing this, it&#8217;ll also automatically discover any tasks that have been created as Spring beans and register them with this scheduler. This means that the only thing we need to do is create the tasks themselves.</p>
<p>If we want to configure the <em>Scheduler </em>in any way, we can do this using properties in our standard way:</p>
<pre><code class="language-properties">db-scheduler.enabled=true
db-scheduler.polling-interval=5s
db-scheduler.heartbeat-interval=2m
db-scheduler.missed-heartbeats-limit=10
db-scheduler.threads=5</code></pre>
<p><strong>Everything else about using db-scheduler, including how we create and schedule tasks, is exactly the same as before</strong>. The starter just makes it simpler to get started in the first place.</p>
<h2 id="bd-conclusion" data-id="conclusion"><strong>8. Conclusion</strong></h2>
<div class="bd-anchor" id="conclusion"></div>
<p>In this article, we took a very quick look at db-scheduler, including how to schedule recurring and one-time tasks. There’s a lot more that we can do with this. Next time we need to manage scheduled jobs for your applications, why not give it a try?</p>
<p>As always, all the code from this article is available over on <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/eugenp/tutorials/tree/master/libraries-7">GitHub</a>.</p><p>The post <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/java-db-scheduler">DB-Scheduler: A Persistent, Cluster-Friendly Scheduler for Java</a> first appeared on <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com">Baeldung</a>.</p><Img align="left" border="0" height="1" width="1" alt="" style="border:0;float:left;margin:0;padding:0;width:1px!important;height:1px!important;" hspace="0" src="https://feeds.feedblitz.com/~/i/968126534/0/baeldung">
<div style="clear:both;padding-top:0.2em;"><a title="Like on Facebook" href="https://feeds.feedblitz.com/_/28/968126534/baeldung"><img height="20" src="https://assets.feedblitz.com/i/fblike20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Pin it!" href="https://feeds.feedblitz.com/_/29/968126534/baeldung,https%3a%2f%2fwww.baeldung.com%2fwp-content%2fuploads%2f2024%2f11%2fPersistence-Featured-Image-04-1024x536.jpg"><img height="20" src="https://assets.feedblitz.com/i/pinterest20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Post to X.com" href="https://feeds.feedblitz.com/_/24/968126534/baeldung"><img height="20" src="https://assets.feedblitz.com/i/x.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by email" href="https://feeds.feedblitz.com/_/19/968126534/baeldung"><img height="20" src="https://assets.feedblitz.com/i/email20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by RSS" href="https://feeds.feedblitz.com/_/20/968126534/baeldung"><img height="20" src="https://assets.feedblitz.com/i/rss20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a rel="NOFOLLOW" title="View Comments" href="https://www.baeldung.com/java-db-scheduler#respond"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/comments20.png"></a>&#160;<a title="Follow Comments via RSS" href="https://www.baeldung.com/java-db-scheduler/feed"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/commentsrss20.png"></a>&#160;</div>]]>
</content:encoded>
					
					<wfw:commentRss>https://feeds.feedblitz.com/~/968126534/0/baeldung~DBScheduler-A-Persistent-ClusterFriendly-Scheduler-for-Java/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<webfeeds:featuredImage>https://www.baeldung.com/wp-content/uploads/2024/11/Persistence-Featured-Image-04-150x150.jpg</webfeeds:featuredImage></item>
<item>
<feedburner:origLink>https://www.baeldung.com/java-weekly-660</feedburner:origLink>
		<title>Java Weekly, Issue 660</title>
		<link>https://feeds.feedblitz.com/~/968116088/0/baeldung~Java-Weekly-Issue</link>
					<comments>https://feeds.feedblitz.com/~/968116088/0/baeldung~Java-Weekly-Issue#respond</comments>
		
		<dc:creator><![CDATA[baeldung]]></dc:creator>
		<pubDate>Sat, 22 Aug 2026 11:26:18 +0000</pubDate>
				<category><![CDATA[Weekly Review]]></category>
		<category><![CDATA[no-ads]]></category>
		<category><![CDATA[no-after-post]]></category>
		<category><![CDATA[no-before-post]]></category>
		<category><![CDATA[no-optins]]></category>
		<guid isPermaLink="false">https://www.baeldung.com/?p=204721</guid>
					<description><![CDATA[<img src="https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="max-width:100% !important;height:auto !important;float: left; margin-right: 5px;" loading="lazy" /><p>A clear Hibernate speedup and a new JSON API coming to Java.</p>
<p>The post <a rel="NOFOLLOW" href="https://feeds.feedblitz.com/~/968116088/0/baeldung~Java-Weekly-Issue">Java Weekly, Issue 660</a> first appeared on <a rel="NOFOLLOW" href="https://www.baeldung.com">Baeldung</a>.</p><div style="clear:both;padding-top:0.2em;"><a title="Like on Facebook" href="https://feeds.feedblitz.com/_/28/968116088/baeldung"><img height="20" src="https://assets.feedblitz.com/i/fblike20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Pin it!" href="https://feeds.feedblitz.com/_/29/968116088/baeldung,https%3a%2f%2fwww.baeldung.com%2fwp-content%2fuploads%2f2016%2f10%2fsocial-Weekly-Reviews-4.jpg"><img height="20" src="https://assets.feedblitz.com/i/pinterest20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Post to X.com" href="https://feeds.feedblitz.com/_/24/968116088/baeldung"><img height="20" src="https://assets.feedblitz.com/i/x.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by email" href="https://feeds.feedblitz.com/_/19/968116088/baeldung"><img height="20" src="https://assets.feedblitz.com/i/email20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by RSS" href="https://feeds.feedblitz.com/_/20/968116088/baeldung"><img height="20" src="https://assets.feedblitz.com/i/rss20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a rel="NOFOLLOW" title="View Comments" href="https://www.baeldung.com/java-weekly-660#respond"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/comments20.png"></a>&#160;<a title="Follow Comments via RSS" href="https://www.baeldung.com/java-weekly-660/feed"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/commentsrss20.png"></a>&#160;</div>]]>
</description>
										<content:encoded><![CDATA[<img src="https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="float: left; margin-right: 5px;" decoding="async" loading="lazy" srcset="https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4.jpg 952w, https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4-300x157.jpg 300w, https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4-768x402.jpg 768w" sizes="auto, (max-width: 580px) 100vw, 580px" /><h2 style="text-align: left;" id="bd-spring-and-java" data-id="spring-and-java">1.<strong> Spring and Java</strong></h2>
<div class="bd-anchor" id="spring-and-java"></div>
<p><strong><a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://in.relation.to/2026/08/19/hibernate-immutable-2lc-9x-speedup/">&gt;&gt; When @Immutable Made All the Difference: A 3.2x Speedup with Hibernate Second-Level Cache</a></strong> [<span style="color: #993300;">relation.to</span>]</p>
<p>Hibernate’s second-level cache jumps from 286 seconds to 90 seconds when <code>@Immutable</code>, <code>@Cache(READ_ONLY)</code>, and field-level <code>@Mutability(Immutability.class)</code> work together. This case study also shows why omitting the JSONB field annotation caused an 8.1x slowdown. Interesting read.</p>
<p><strong><a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.infoq.com/news/2026/08/java-native-json-api/">&gt;&gt; JEP 540 Proposed to Target JDK 28 with a Simple JSON API</a></strong> [<span style="color: #993300;">infoq.com</span>]</p>
<p>JEP 540 is moving toward JDK 28 with an incubating, dependency-free, deliberately-small JSON API for straightforward parsing and generation.</p>
<h4><strong>Also worth reading:</strong></h4>
<ul>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.frankel.ch/gradle-metadata-renovate-integration/" target="_blank" rel="noopener"><strong>Solving Gradle metadata and Renovate integration</strong></a> [<span style="color: #800000;">frankel.ch</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://foojay.io/today/introducing-boxlang-ai-explorer-a-local-catalog-for-every-ai-pattern/" target="_blank" rel="noopener"><strong>Introducing BoxLang AI Explorer: A Local Catalog for Every AI Pattern</strong></a> [<span style="color: #800000;">foojay.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://foojay.io/today/vibe-coding-maven-and-the-dependencies-you-didnt-choose/" target="_blank" rel="noopener"><strong>Vibe Coding, Maven, and the Dependencies You Didn&#8217;t Choose</strong></a> [<span style="color: #800000;">foojay.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://inside.java/2026/08/14/java-exhaustiveness-guide/" target="_blank" rel="noopener"><strong>Preparing for Change: Safe Switching over Sealed APIs</strong></a> [<span style="color: #800000;">inside.java</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blogs.oracle.com/java/jdk-21-approaches-end-of-permissive-license" target="_blank" rel="noopener"><strong>JDK 21 approaches end-of-permissive license</strong></a> [<span style="color: #800000;">oracle.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.jetbrains.com/kotlin/2026/08/exploring-compose-html-for-server-side-rendering/" target="_blank" rel="noopener"><strong>Exploring Compose HTML for Server Side Rendering</strong></a> [<span style="color: #800000;">jetbrains.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://marcphilipp.de/blog/2026/08/16/renovate-teaching-an-old-dog-new-tricks/" target="_blank" rel="noopener"><strong>Renovate: teaching an old dog new tricks</strong></a> [<span style="color: #800000;">marcphilipp.de</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.wimdeblauwe.com/blog/2026/08/17/writing-a-thymeleaf-component-library---part-4/" target="_blank" rel="noopener"><strong>Writing a Thymeleaf component library &#8211; part 4</strong></a> [<span style="color: #800000;">wimdeblauwe.com</span>]</li>
</ul>
<h4><strong>Webinars and presentations:</strong></h4>
<ul>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://foojay.io/today/build-secure-ai-chat-applications-with-boxlang-rag-ollama-and-amazon-bedrock-with-dan-card/" target="_blank" rel="noopener"><strong>Build Secure AI Chat Applications with BoxLang, RAG, Ollama, and Amazon Bedrock with Dan Card</strong></a> [<span style="color: #800000;">foojay.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://inside.java/2026/08/15/podcast-066/" target="_blank" rel="noopener"><strong>Episode 66 “JSON API, Valhalla Progress, LTS <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2764.png" alt="❤" class="wp-smiley" style="height: 1em; max-height: 1em;" /> PQC” [IJN]</strong></a> [<span style="color: #800000;">inside.java</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://nipafx.dev/inside-java-newscast-114" target="_blank" rel="noopener"><strong>JSON API, Valhalla Progress, LTS <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2764.png" alt="❤" class="wp-smiley" style="height: 1em; max-height: 1em;" /> PQC &#8211; Inside Java Newscast #114</strong></a> [<span style="color: #800000;">nipafx.dev</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://spring.io/blog/2026/08/17/spring-office-hours-podcast-S5E20" target="_blank" rel="noopener"><strong>Spring Office Hours Podcast: S5E20 &#8211; The Developer&#8217;s Guide to AI with Danny Thompson</strong></a> [<span style="color: #800000;">spring.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://spring.io/blog/2026/08/13/a-bootiful-podcast-andrey-santrosyan" target="_blank" rel="noopener"><strong>A Bootiful Podcast: Redouble AI CTO and founder Andrey Santrosyan</strong></a> [<span style="color: #800000;">spring.io</span>]</li>
</ul>
<h4><strong>Time to upgrade:</strong></h4>
<ul>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://quarkus.io/blog/quarkus-3-38-3-released/" target="_blank" rel="noopener"><strong>Quarkus 3.38.3 &#8211; Maintenance release</strong></a> [<span style="color: #800000;">quarkus.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://quarkus.io/blog/a2a-java-sdk-1-2-0-final-released/" target="_blank" rel="noopener"><strong>A2A Java SDK 1.2.0.Final released</strong></a> [<span style="color: #800000;">quarkus.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://quarkus.io/blog/quarkus-3-38-2-released/" target="_blank" rel="noopener"><strong>Quarkus 3.38.2 &#8211; Maintenance release</strong></a> [<span style="color: #800000;">quarkus.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://toomuchcoding.com/post/2026-08-20-stubborn-0-1-0-release/" target="_blank" rel="noopener"><strong>Stubborn Contract 0.1.0 released!</strong></a> [<span style="color: #800000;">toomuchcoding.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/apache/hive/releases/tag/rel%2Frelease-4.2.1" target="_blank" rel="noopener"><strong>Apache Hive 4.2.1</strong></a> [<span style="color: #800000;">github.com/apache</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/micronaut-projects/micronaut-core/releases/tag/v5.1.11" target="_blank" rel="noopener"><strong>Micronaut Core 5.1.11</strong></a> [<span style="color: #800000;">github.com/micronaut-projects</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/langchain4j/langchain4j/releases/tag/1.19.0" target="_blank" rel="noopener"><strong>LangChain4j 1.19.0</strong></a> [<span style="color: #800000;">github.com/langchain4j</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/helidon-io/helidon/releases/tag/4.5.3" target="_blank" rel="noopener"><strong>Helidon 4.5.3</strong></a> and <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/helidon-io/helidon/releases/tag/3.2.20" target="_blank" rel="noopener"><strong>3.2.20</strong></a> [<span style="color: #800000;">github.com/helidon-io</span>]</li>
</ul>
<h2 style="text-align: left;" id="bd-technical-amp-musings" data-id="technical-amp-musings">2.<strong> Technical &amp; Musings</strong></h2>
<div class="bd-anchor" id="technical-amp-musings"></div>
<p><strong><a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://martinfowler.com/rachels-ramblings/citizens-agents-experts.html">&gt;&gt; Citizens Build, Agents Execute, Experts Govern</a></strong> [<span style="color: #993300;">martinfowler.com</span>]</p>
<p>AI agents are making it dramatically easier to build working software, but enterprise delivery still depends on engineering judgment. Of course it does.</p>
<h4><strong>Also worth reading:</strong></h4>
<ul>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://lucumr.pocoo.org/2026/8/19/what-is-reasoning/" target="_blank" rel="noopener"><strong>What Is Reasoning</strong></a> [<span style="color: #800000;">pocoo.org</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://nurkiewicz.com/2026/08/compiling-to-intermediate-representation-write-yourself-a-compiler.html" target="_blank" rel="noopener"><strong>Compiling to intermediate representation: Write yourself a compiler, Part III</strong></a> [<span style="color: #800000;">nurkiewicz.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://foojay.io/today/idempotent-write-paths-keeping-an-integration-pipeline-correct-when-messages-repeat/" target="_blank" rel="noopener"><strong>Idempotent Write Paths: Keeping an Integration Pipeline Correct When Messages Repeat</strong></a> [<span style="color: #800000;">foojay.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://henrikwarne.com/2026/08/15/lessons-learned-from-fixing-flaky-tests-with-claude/" target="_blank" rel="noopener"><strong>Lessons Learned from Fixing Flaky Tests with Claude</strong></a> [<span style="color: #800000;">henrikwarne.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://martinfowler.com/articles/practitioner-voice.html" target="_blank" rel="noopener"><strong>Practitioner Voice: The Writing Category Nobody has Named Yet</strong></a> [<span style="color: #800000;">martinfowler.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.scottlogic.com/2026/08/17/incremental-steps-data-democratisation.html" target="_blank" rel="noopener"><strong>How to take incremental steps towards data democratisation by Andy Scotland</strong></a> [<span style="color: #800000;">scottlogic.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.scottlogic.com/2026/08/14/ai-success-depends-on-data-foundations.html" target="_blank" rel="noopener"><strong>AI success depends on data foundations by James Heward</strong></a> [<span style="color: #800000;">scottlogic.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://netflixtechblog.medium.com/behind-the-scenes-evolving-netflixs-ads-event-pipeline-for-live-part-ii-826ebf9ad9fb" target="_blank" rel="noopener"><strong>Behind the Scenes: Evolving Netflix&#8217;s Ads Event Pipeline for Live &#8211; Part II</strong></a> [<span style="color: #800000;">medium.com</span>]</li>
</ul>
<h2 style="text-align: left;" id="bd-pick-of-the-week" data-id="pick-of-the-week">3.<strong> Pick of the Week</strong></h2>
<div class="bd-anchor" id="pick-of-the-week"></div>
<p><strong><a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://alex.draftist.io/blog/the-bedrock-of-software-design-ycqvcedsj">&gt;&gt; The Bedrock of Software Design</a></strong> [<span style="color: #993300;">alex.draftist.io</span>]</p><p>The post <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/java-weekly-660">Java Weekly, Issue 660</a> first appeared on <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com">Baeldung</a>.</p><Img align="left" border="0" height="1" width="1" alt="" style="border:0;float:left;margin:0;padding:0;width:1px!important;height:1px!important;" hspace="0" src="https://feeds.feedblitz.com/~/i/968116088/0/baeldung">
<div style="clear:both;padding-top:0.2em;"><a title="Like on Facebook" href="https://feeds.feedblitz.com/_/28/968116088/baeldung"><img height="20" src="https://assets.feedblitz.com/i/fblike20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Pin it!" href="https://feeds.feedblitz.com/_/29/968116088/baeldung,https%3a%2f%2fwww.baeldung.com%2fwp-content%2fuploads%2f2016%2f10%2fsocial-Weekly-Reviews-4.jpg"><img height="20" src="https://assets.feedblitz.com/i/pinterest20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Post to X.com" href="https://feeds.feedblitz.com/_/24/968116088/baeldung"><img height="20" src="https://assets.feedblitz.com/i/x.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by email" href="https://feeds.feedblitz.com/_/19/968116088/baeldung"><img height="20" src="https://assets.feedblitz.com/i/email20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by RSS" href="https://feeds.feedblitz.com/_/20/968116088/baeldung"><img height="20" src="https://assets.feedblitz.com/i/rss20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a rel="NOFOLLOW" title="View Comments" href="https://www.baeldung.com/java-weekly-660#respond"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/comments20.png"></a>&#160;<a title="Follow Comments via RSS" href="https://www.baeldung.com/java-weekly-660/feed"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/commentsrss20.png"></a>&#160;</div>]]>
</content:encoded>
					
					<wfw:commentRss>https://feeds.feedblitz.com/~/968116088/0/baeldung~Java-Weekly-Issue/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<webfeeds:featuredImage>https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4-150x150.jpg</webfeeds:featuredImage></item>
<item>
<feedburner:origLink>https://www.baeldung.com/java-weekly-659</feedburner:origLink>
		<title>Java Weekly, Issue 659</title>
		<link>https://feeds.feedblitz.com/~/967864352/0/baeldung~Java-Weekly-Issue</link>
					<comments>https://feeds.feedblitz.com/~/967864352/0/baeldung~Java-Weekly-Issue#comments</comments>
		
		<dc:creator><![CDATA[baeldung]]></dc:creator>
		<pubDate>Sat, 15 Aug 2026 10:23:09 +0000</pubDate>
				<category><![CDATA[Weekly Review]]></category>
		<category><![CDATA[no-ads]]></category>
		<category><![CDATA[no-after-post]]></category>
		<category><![CDATA[no-before-post]]></category>
		<category><![CDATA[no-optins]]></category>
		<guid isPermaLink="false">https://www.baeldung.com/?p=204657</guid>
					<description><![CDATA[<img src="https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="max-width:100% !important;height:auto !important;float: left; margin-right: 5px;" loading="lazy" /><p>Equality in Java is evolving.</p>
<p>The post <a rel="NOFOLLOW" href="https://feeds.feedblitz.com/~/967864352/0/baeldung~Java-Weekly-Issue">Java Weekly, Issue 659</a> first appeared on <a rel="NOFOLLOW" href="https://www.baeldung.com">Baeldung</a>.</p><div style="clear:both;padding-top:0.2em;"><a title="Like on Facebook" href="https://feeds.feedblitz.com/_/28/967864352/baeldung"><img height="20" src="https://assets.feedblitz.com/i/fblike20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Pin it!" href="https://feeds.feedblitz.com/_/29/967864352/baeldung,https%3a%2f%2fwww.baeldung.com%2fwp-content%2fuploads%2f2016%2f10%2fsocial-Weekly-Reviews-4.jpg"><img height="20" src="https://assets.feedblitz.com/i/pinterest20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Post to X.com" href="https://feeds.feedblitz.com/_/24/967864352/baeldung"><img height="20" src="https://assets.feedblitz.com/i/x.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by email" href="https://feeds.feedblitz.com/_/19/967864352/baeldung"><img height="20" src="https://assets.feedblitz.com/i/email20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by RSS" href="https://feeds.feedblitz.com/_/20/967864352/baeldung"><img height="20" src="https://assets.feedblitz.com/i/rss20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a rel="NOFOLLOW" title="View Comments" href="https://www.baeldung.com/java-weekly-659#comments"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/comments20.png"></a>&#160;<a title="Follow Comments via RSS" href="https://www.baeldung.com/java-weekly-659/feed"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/commentsrss20.png"></a>&nbsp;
<div style="clear:left;"><a rel="NOFOLLOW" href="https://www.baeldung.com/java-weekly-659#comments"><h3>Comments</h3></a><ul><li><a rel="NOFOLLOW" href="https://www.baeldung.com/java-weekly-659#comment-15701">In reply to Vincent.   Hey, Vincent.   Thanks for the feedback. ...</a> <i>by Ulisses Lima</i><li><a rel="NOFOLLOW" href="https://www.baeldung.com/java-weekly-659#comment-15700">You have encoding prolem on your URL :    with Helidonâ€ ...</a> <i>by Vincent</i></ul></div>&#160;</div>]]>
</description>
										<content:encoded><![CDATA[<img src="https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="float: left; margin-right: 5px;" decoding="async" loading="lazy" srcset="https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4.jpg 952w, https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4-300x157.jpg 300w, https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4-768x402.jpg 768w" sizes="auto, (max-width: 580px) 100vw, 580px" /><h2 style="text-align: left" id="bd-spring-and-java" data-id="spring-and-java">1.<strong> Spring and Java</strong></h2>
<div class="bd-anchor" id="spring-and-java"></div>
<p><strong><a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://inside.java/2026/08/12/java-mcp-migration/">&gt;&gt; Evolving a Java MCP Server During MCP Specification Upgrades</a></strong> [<span style="color: #993300">inside.java</span>]</p>
<p>A practical guide to evolving a Helidon-based Java MCP server to the stateless. Header-based routing and a temporary adapter keep older clients working while tests exercise both protocol paths. Good stuff.</p>
<p><strong><a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.infoq.com/news/2026/08/jep401-value-objects-preview/">&gt;&gt; Project Valhalla&#8217;s First Preview: JEP 401 Redefines == for Java Objects</a></strong> [<span style="color: #993300">infoq.com</span>]</p>
<p>JEP 401 brings identity-free value classes and records to preview, changing <code>==</code> for value objects while identity classes retain reference semantics. Good weekend read.</p>
<h4><strong>Also worth reading:</strong></h4>
<ul>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.frankel.ch/security-baked-into-jvm/3/" target="_blank" rel="noopener"><strong>Security Baked Into the JVM: two Subjects, one call</strong></a> [<span style="color: #800000">frankel.ch</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://foojay.io/today/duckdb-in-spring-batch-replace-in-memory-java-loops-with-one-sql-statement/" target="_blank" rel="noopener"><strong>DuckDB in Spring Batch: Replace In-Memory Java Loops with One SQL Statement</strong></a> [<span style="color: #800000">foojay.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://foojay.io/today/push-v3-one-message-from-your-server-to-every-surface/" target="_blank" rel="noopener"><strong>Push V3: One Message From Your Server to Every Surface</strong></a> [<span style="color: #800000">foojay.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://foojay.io/today/embedding-duckdb-in-a-maven-app-and-using-it-for-things-that-arent-databases/" target="_blank" rel="noopener"><strong>Embedding DuckDB in a Maven App (and Using It for Things That Aren&#8217;t Databases)</strong></a> [<span style="color: #800000">foojay.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://inside.java/2026/08/11/efficient-java-apps-profiling-troubleshooting/" target="_blank" rel="noopener"><strong>The Power of JDK Flight Recorder: Efficient Profiling and Troubleshooting for Java Applications</strong></a> [<span style="color: #800000">inside.java</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://inside.java/2026/08/07/post-quantum-cryptography-in-long-term-support-jdk-releases/" target="_blank" rel="noopener"><strong>Post-Quantum Cryptography in Long-Term Support JDK Releases</strong></a> [<span style="color: #800000">inside.java</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.jetbrains.com/idea/2026/08/how-to-use-ai-agents-in-intellij-idea-with-acp/" target="_blank" rel="noopener"><strong>How to Use AI Agents in IntelliJ IDEA With ACP</strong></a> [<span style="color: #800000">jetbrains.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.jetbrains.com/idea/2026/08/top-5-ai-features-in-intellij-idea/" target="_blank" rel="noopener"><strong>Top 5 AI Features in IntelliJ IDEA</strong></a> [<span style="color: #800000">jetbrains.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.jetbrains.com/idea/2026/08/agent-skills-in-intellij-idea/" target="_blank" rel="noopener"><strong>Agent Skills in IntelliJ IDEA</strong></a> [<span style="color: #800000">jetbrains.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.wimdeblauwe.com/blog/2026/08/10/writing-a-thymeleaf-component-library---part-3/" target="_blank" rel="noopener"><strong>Writing a Thymeleaf component library &#8211; part 3</strong></a> [<span style="color: #800000">wimdeblauwe.com</span>]</li>
</ul>
<h4><strong>Webinars and presentations:</strong></h4>
<ul>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://inside.java/2026/08/06/podcast-065/" target="_blank" rel="noopener"><strong>Episode 65 &#8220;Embracing Virtual Threads with Helidon&#8221; [I/O]</strong></a> [<span style="color: #800000">inside.java</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://quarkus.io/blog/quarkus-insights-256-what-is-floci/" target="_blank" rel="noopener"><strong>Quarkus Insights #256: What is Floci?</strong></a> [<span style="color: #800000">quarkus.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://spring.io/blog/2026/08/06/a-bootiful-podcast-gregory-green/" target="_blank" rel="noopener"><strong>A Bootiful Podcast: Data guru Gregory Green on RabbitMQ, Valkey, Gemfire, Data Flow, and more</strong></a> [<span style="color: #800000">spring.io</span>]</li>
</ul>
<h4><strong>Time to upgrade:</strong></h4>
<ul>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.jetbrains.com/idea/2026/08/intellij-idea-2026-2-1/" target="_blank" rel="noopener"><strong>IntelliJ IDEA 2026.2.1 Is Out!</strong></a> [<span style="color: #800000">jetbrains.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/gradle/gradle/releases/tag/v9.7.0" target="_blank" rel="noopener"><strong>Gradle 9.7.0</strong></a> [<span style="color: #800000">github.com/gradle</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/eclipse-vertx/vert.x/releases/tag/4.5.32" target="_blank" rel="noopener"><strong>Eclipse Vert.x 4.5.32</strong></a> [<span style="color: #800000">github.com/eclipse-vertx</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/elastic/elasticsearch/releases/tag/v9.5.1" target="_blank" rel="noopener"><strong>Elasticsearch 9.5.1</strong></a> and <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/elastic/elasticsearch/releases/tag/v9.4.5" target="_blank" rel="noopener"><strong>9.4.5</strong></a> and <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/elastic/elasticsearch/releases/tag/v8.19.20" target="_blank" rel="noopener"><strong>8.19.20</strong></a> [<span style="color: #800000">github.com/elastic</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/Netflix/zuul/releases/tag/v4.1.4" target="_blank" rel="noopener"><strong>Netflix Zuul 4.1.4</strong></a> [<span style="color: #800000">github.com/Netflix</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/apache/camel/releases/tag/camel-4.18.4" target="_blank" rel="noopener"><strong>Apache Camel 4.18.4</strong></a> and <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/apache/camel/releases/tag/camel-4.14.9" target="_blank" rel="noopener"><strong>4.14.9</strong></a> and <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/apache/camel/releases/tag/camel-4.22.0" target="_blank" rel="noopener"><strong>4.22.0</strong></a> [<span style="color: #800000">github.com/apache</span>]</li>
</ul>
<h2 style="text-align: left" id="bd-technical-amp-musings" data-id="technical-amp-musings">2.<strong> Technical &amp; Musings</strong></h2>
<div class="bd-anchor" id="technical-amp-musings"></div>
<p><strong><a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://mnot.net/blog/2026/linting_the_web.html">&gt;&gt; Informing HTTP Extension Design with Data</a></strong> [<span style="color: #993300">mnot.net</span>]</p>
<p>Mark Nottingham tests HTTP extensions against 120 million Common Crawl responses. The results show how invisible failures and weak migration incentives undermine adoption, yielding a practical lesson: design for repeatable deployment, make errors visible, and study real usage before extending a protocol.</p>
<h4><strong>Also worth reading:</strong></h4>
<ul>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.jetbrains.com/research/2026/08/programming-language-migration/" target="_blank" rel="noopener"><strong>Programming Language Migration 2025: Why Developers Switch</strong></a> [<span style="color: #800000">jetbrains.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://marcphilipp.de/blog/2026/08/10/one-required-check-to-rule-them-all/" target="_blank" rel="noopener"><strong>One required check to rule them all</strong></a> [<span style="color: #800000">marcphilipp.de</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://event-driven.io/en/vertical-slices-and-dependencies/" target="_blank" rel="noopener"><strong>Vertical slices, their ownership and external dependencies</strong></a> [<span style="color: #800000">event-driven.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://martinfowler.com/articles/exploring-gen-ai/tdd-in-the-agent-loop.html" target="_blank" rel="noopener"><strong>TDD inside the agent loop &#8211; theater or actual value?</strong></a> [<span style="color: #800000">martinfowler.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.scottlogic.com/2026/08/12/unlocking-your-data-in-collaboration.html" target="_blank" rel="noopener"><strong>Unlocking your data: the value is in collaboration</strong></a> [<span style="color: #800000">scottlogic.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.scottlogic.com/2026/08/10/agile-leadership.html" target="_blank" rel="noopener"><strong>Agile Leadership</strong></a> [<span style="color: #800000">scottlogic.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.scottlogic.com/2026/08/06/how-to-accelerate-agentic-adoption.html" target="_blank" rel="noopener"><strong>How to accelerate agentic adoption</strong></a> [<span style="color: #800000">scottlogic.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://netflixtechblog.com/how-and-why-netflix-built-a-real-time-distributed-graph-part-3-querying-the-graph-with-grpc-0f3468349607" target="_blank" rel="noopener"><strong>How and Why Netflix Built a Real-Time Distributed Graph: Part 3 &#8211; Querying the graph with gRPC</strong></a> [<span style="color: #800000">netflixtechblog.com</span>]</li>
</ul>
<h2 style="text-align: left" id="bd-pick-of-the-week" data-id="pick-of-the-week">3.<strong> Pick of the Week</strong></h2>
<div class="bd-anchor" id="pick-of-the-week"></div>
<p><strong><a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://jon.chrt.dev/2026/04/15/things-you-didnt-know-about-indexes.html">&gt;&gt; Things you didn&#8217;t know about indexes</a></strong> [<span style="color: #993300">jon.chrt.dev</span>]</p><p>The post <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/java-weekly-659">Java Weekly, Issue 659</a> first appeared on <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com">Baeldung</a>.</p><Img align="left" border="0" height="1" width="1" alt="" style="border:0;float:left;margin:0;padding:0;width:1px!important;height:1px!important;" hspace="0" src="https://feeds.feedblitz.com/~/i/967864352/0/baeldung">
<div style="clear:both;padding-top:0.2em;"><a title="Like on Facebook" href="https://feeds.feedblitz.com/_/28/967864352/baeldung"><img height="20" src="https://assets.feedblitz.com/i/fblike20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Pin it!" href="https://feeds.feedblitz.com/_/29/967864352/baeldung,https%3a%2f%2fwww.baeldung.com%2fwp-content%2fuploads%2f2016%2f10%2fsocial-Weekly-Reviews-4.jpg"><img height="20" src="https://assets.feedblitz.com/i/pinterest20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Post to X.com" href="https://feeds.feedblitz.com/_/24/967864352/baeldung"><img height="20" src="https://assets.feedblitz.com/i/x.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by email" href="https://feeds.feedblitz.com/_/19/967864352/baeldung"><img height="20" src="https://assets.feedblitz.com/i/email20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by RSS" href="https://feeds.feedblitz.com/_/20/967864352/baeldung"><img height="20" src="https://assets.feedblitz.com/i/rss20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a rel="NOFOLLOW" title="View Comments" href="https://www.baeldung.com/java-weekly-659#comments"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/comments20.png"></a>&#160;<a title="Follow Comments via RSS" href="https://www.baeldung.com/java-weekly-659/feed"><img height="20" style="border:0;margin:0;padding:0;" src="https://assets.feedblitz.com/i/commentsrss20.png"></a>&nbsp;
<div style="clear:left;"><a rel="NOFOLLOW" href="https://www.baeldung.com/java-weekly-659#comments"><h3>Comments</h3></a><ul><li><a rel="NOFOLLOW" href="https://www.baeldung.com/java-weekly-659#comment-15701">In reply to Vincent.   Hey, Vincent.   Thanks for the feedback. ...</a> <i>by Ulisses Lima</i><li><a rel="NOFOLLOW" href="https://www.baeldung.com/java-weekly-659#comment-15700">You have encoding prolem on your URL :    with Helidonâ€ ...</a> <i>by Vincent</i></ul></div>&#160;</div>]]>
</content:encoded>
					
					<wfw:commentRss>https://feeds.feedblitz.com/~/967864352/0/baeldung~Java-Weekly-Issue/feed</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
		<webfeeds:featuredImage>https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4-150x150.jpg</webfeeds:featuredImage></item>
<item>
<feedburner:origLink>https://www.baeldung.com/java-weekly-658</feedburner:origLink>
		<title>Java Weekly, Issue 658</title>
		<link>https://feeds.feedblitz.com/~/967388204/0/baeldung~Java-Weekly-Issue</link>
		
		<dc:creator><![CDATA[baeldung]]></dc:creator>
		<pubDate>Sat, 08 Aug 2026 10:07:32 +0000</pubDate>
				<category><![CDATA[Weekly Review]]></category>
		<category><![CDATA[no-ads]]></category>
		<category><![CDATA[no-after-post]]></category>
		<category><![CDATA[no-before-post]]></category>
		<category><![CDATA[no-optins]]></category>
		<guid isPermaLink="false">https://www.baeldung.com/?p=204617</guid>
					<description><![CDATA[<img src="https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="max-width:100% !important;height:auto !important;float: left; margin-right: 5px;" loading="lazy" /><p>A Java movie? Cool. And Spring Cloud Contract has a new home.</p>
<p>The post <a rel="NOFOLLOW" href="https://feeds.feedblitz.com/~/967388204/0/baeldung~Java-Weekly-Issue">Java Weekly, Issue 658</a> first appeared on <a rel="NOFOLLOW" href="https://www.baeldung.com">Baeldung</a>.</p><div style="clear:both;padding-top:0.2em;"><a title="Like on Facebook" href="https://feeds.feedblitz.com/_/28/967388204/baeldung"><img height="20" src="https://assets.feedblitz.com/i/fblike20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Pin it!" href="https://feeds.feedblitz.com/_/29/967388204/baeldung,https%3a%2f%2fwww.baeldung.com%2fwp-content%2fuploads%2f2016%2f10%2fsocial-Weekly-Reviews-4.jpg"><img height="20" src="https://assets.feedblitz.com/i/pinterest20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Post to X.com" href="https://feeds.feedblitz.com/_/24/967388204/baeldung"><img height="20" src="https://assets.feedblitz.com/i/x.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by email" href="https://feeds.feedblitz.com/_/19/967388204/baeldung"><img height="20" src="https://assets.feedblitz.com/i/email20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by RSS" href="https://feeds.feedblitz.com/_/20/967388204/baeldung"><img height="20" src="https://assets.feedblitz.com/i/rss20.png" style="border:0;margin:0;padding:0;"></a>&nbsp;&#160;</div>]]>
</description>
										<content:encoded><![CDATA[<img src="https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="float: left; margin-right: 5px;" decoding="async" loading="lazy" srcset="https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4.jpg 952w, https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4-300x157.jpg 300w, https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4-768x402.jpg 768w" sizes="auto, (max-width: 580px) 100vw, 580px" /><h2 style="text-align: left;" id="bd-spring-and-java" data-id="spring-and-java">1.<strong> Spring and Java</strong></h2>
<div class="bd-anchor" id="spring-and-java"></div>
<p><strong><a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.infoq.com/articles/virtual-threads-after-jdk24/">&gt;&gt; Virtual Threads after JDK 24: What Changed for Production Java</a></strong> [<span style="color: #993300;">infoq.com</span>]</p>
<p>If you haven&#8217;t looked at virtual threads in a while, this is the update you need. Benchmarks, code, and a repo you can run, plus concrete Spring Boot 3 adoption guidance.</p>
<h4><strong>Also worth reading:</strong></h4>
<ul>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://foojay.io/today/idempotent-spring-boot-starter/" target="_blank" rel="noopener"><strong>Idempotent Spring Boot: Safe REST with One Annotation</strong></a> [<span style="color: #800000;">foojay.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.petrikainulainen.net/programming/testing/introduction-to-spring-mvc-test-framework/" target="_blank" rel="noopener"><strong>Introduction to Spring MockMvc</strong></a> [<span style="color: #800000;">petrikainulainen.net</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.wimdeblauwe.com/blog/2026/08/03/writing-a-thymeleaf-component-library---part-2/" target="_blank" rel="noopener"><strong>Writing a Thymeleaf component library &#8211; part 2</strong></a> [<span style="color: #800000;">wimdeblauwe.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://foojay.io/today/how-to-create-a-spring-boot-fraud-scoring-service/" target="_blank" rel="noopener"><strong>How to Create a Spring Boot Fraud Scoring Service</strong></a> [<span style="color: #800000;">foojay.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://foojay.io/today/jakarta-agentic-ai-hits-its-first-milestone/" target="_blank" rel="noopener"><strong>Jakarta Agentic AI Hits Its First Milestone</strong></a> [<span style="color: #800000;">foojay.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://inside.java/2026/07/31/transitioning-java-to-more-frequent-security-updates/" target="_blank" rel="noopener"><strong>Transitioning Java to More Frequent Security Updates</strong></a> [<span style="color: #800000;">inside.java</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.jetbrains.com/idea/2026/08/intellij-idea-goes-lsp/" target="_blank" rel="noopener"><strong>IntelliJ IDEA Goes LSP: Java and Kotlin Intelligence Comes to VS Code, Cursor, and Agentic Flows</strong></a> [<span style="color: #800000;">jetbrains.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.infoq.com/news/2026/08/embabel-1/" target="_blank" rel="noopener"><strong>Embabel Agent Framework Reaches 1.0</strong></a> [<span style="color: #800000;">infoq.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://foojay.io/today/the-codename-one-javascript-port-is-now-free-and-open-source/" target="_blank" rel="noopener"><strong>The Codename One JavaScript Port Is Now Free and Open Source</strong></a> [<span style="color: #800000;">foojay.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://marcphilipp.de/blog/2026/08/02/reflecting-on-stf-support-and-looking-ahead/" target="_blank" rel="noopener"><strong>Reflecting on STF support and looking ahead</strong></a> [<span style="color: #800000;">marcphilipp.de</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://quarkus.io/blog/quarkus-banner-extension/" target="_blank" rel="noopener"><strong>Custom ASCII-art startup banners for Quarkus</strong></a> [<span style="color: #800000;">quarkus.io</span>]</li>
</ul>
<h4><strong>Webinars and presentations:</strong></h4>
<ul>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://inside.java/2026/07/30/under-the-hat-empowering-gpu-acceleration-for-java/" target="_blank" rel="noopener"><strong>Under the HAT: Empowering GPU Acceleration for Java</strong></a> [<span style="color: #800000;">inside.java</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://inside.java/2026/07/30/podcast-064/" target="_blank" rel="noopener"><strong>Episode 64 &#8220;JIT Compiler From the Ground Up&#8221; [AtA]</strong></a> [<span style="color: #800000;">inside.java</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://spring.io/blog/2026/07/30/a-bootiful-podcast-phil-webb" target="_blank" rel="noopener"><strong>A Bootiful Podcast: Spring Boot founder and lead Phil Webb</strong></a> [<span style="color: #800000;">spring.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://quarkus.io/blog/quarkus-insights-255-faster-leaner-predictable-quarkus/" target="_blank" rel="noopener"><strong>Quarkus Insights #255: Faster, Leaner, Predictable — Quarkus in the Real World</strong></a> [<span style="color: #800000;">quarkus.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.infoq.com/podcasts/feature-evolution-performance-transition-endive/" target="_blank" rel="noopener"><strong>WebAssembly on the JVM: Feature Evolution, Performance, and the Transition to Endive</strong></a> [<span style="color: #800000;">infoq.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.infoq.com/presentations/yk-meta-tracing-jit-compiler/" target="_blank" rel="noopener"><strong>Automatically Retrofitting JIT Compilers</strong></a> [<span style="color: #800000;">infoq.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.infoq.com/presentations/tech-hype-cycles-architectural-tradeoffs/" target="_blank" rel="noopener"><strong>The Free-Lunch Guide to Idea Circularity</strong></a> [<span style="color: #800000;">infoq.com</span>]</li>
</ul>
<h4><strong>Time to upgrade:</strong></h4>
<ul>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://quarkus.io/blog/quarkus-3-38-1-released/" target="_blank" rel="noopener"><strong>Quarkus 3.38.1</strong></a> and <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://quarkus.io/blog/quarkus-3-33-3-1-released/" target="_blank" rel="noopener"><strong>3.33.3.1</strong></a> and <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://quarkus.io/blog/quarkus-3-27-5-1-released/" target="_blank" rel="noopener"><strong>3.27.5.1</strong></a> [<span style="color: #800000;">quarkus.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/jetty/jetty.project/releases/tag/jetty-12.1.12" target="_blank" rel="noopener"><strong>Jetty 12.1.12</strong></a> and <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/jetty/jetty.project/releases/tag/jetty-12.0.38" target="_blank" rel="noopener"><strong>12.0.38</strong></a> [<span style="color: #800000;">github.com/jetty</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/eclipse-vertx/vert.x/releases/tag/5.1.6" target="_blank" rel="noopener"><strong>Vert.x 5.1.6</strong></a> [<span style="color: #800000;">github.com/eclipse-vertx</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/helidon-io/helidon/releases/tag/4.5.2" target="_blank" rel="noopener"><strong>Helidon 4.5.2</strong></a> [<span style="color: #800000;">github.com/helidon-io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/elastic/elasticsearch/releases/tag/v9.5.0" target="_blank" rel="noopener"><strong>Elasticsearch 9.5.0</strong></a> [<span style="color: #800000;">github.com/elastic</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.jetbrains.com/qodana/2026/07/qodana-2026-2-more-security-better-coverage-less-configuration/" target="_blank" rel="noopener"><strong>Qodana 2026.2: More Security, Better Coverage, Less Configuration</strong></a> [<span style="color: #800000;">jetbrains.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://inside.java/2026/08/05/java-vscode-extension-update/" target="_blank" rel="noopener"><strong>Oracle Java Platform Extension for Visual Studio Code &#8211; Version 26.0.1 Is Now Available</strong></a> [<span style="color: #800000;">inside.java</span>]</li>
</ul>
<h2 style="text-align: left;" id="bd-technical-amp-musings" data-id="technical-amp-musings">2.<strong> Technical &amp; Musings</strong></h2>
<div class="bd-anchor" id="technical-amp-musings"></div>
<p><strong><a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://martinfowler.com/articles/exploring-gen-ai/refactoring-economic-benefit.html">&gt;&gt; The Economic Benefit of Refactoring</a></strong> [<span style="color: #993300;">martinfowler.com</span>]</p>
<p>We have always known that clean code pays for itself; here is someone putting a number on it in the agentic era. Giles Edwards-Alexander took a bloated 17,155-line data-access layer, refactored it systematically, and then measured what a representative feature change cost an AI agent before and after: input tokens dropped from 159,564 to 27,360, an 83% cut. Definitely a weekend read.</p>
<h4><strong>Also worth reading:</strong></h4>
<ul>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://martinfowler.com/rachels-ramblings/conductor-developer.html" target="_blank" rel="noopener"><strong>The Conductor Developer</strong></a> [<span style="color: #800000;">martinfowler.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://event-driven.io/en/fixing-bugs-in-event-sourcing-is-hard/" target="_blank" rel="noopener"><strong>Fixing bugs in Event Sourcing is hard, for real?</strong></a> [<span style="color: #800000;">event-driven.io</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.allthingsdistributed.com/2026/08/on-building-scalable-control-planes.html" target="_blank" rel="noopener"><strong>On building scalable control planes</strong></a> [<span style="color: #800000;">allthingsdistributed.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://netflixtechblog.medium.com/timeseries-tiered-storage-journey-kafka-flink-streams-to-native-cassandra-cold-reads-e59d597c9d60" target="_blank" rel="noopener"><strong>TimeSeries Tiered Storage Journey: Kafka/Flink Streams to Native Cassandra Cold Reads</strong></a> [<span style="color: #800000;">netflixtechblog.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://netflixtechblog.com/genrec-towards-llm-native-recommendation-at-netflix-f20be6f643e3" target="_blank" rel="noopener"><strong>GenRec: Towards LLM-Native Recommendation at Netflix</strong></a> [<span style="color: #800000;">netflixtechblog.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.christianposta.com/credential-brokering-patterns-for-ai-agent-egress-part2/" target="_blank" rel="noopener"><strong>Credential Brokering Patterns for AI Agents Part 2: Protecting the Token Vault with AWS KMS</strong></a> [<span style="color: #800000;">christianposta.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.scottlogic.com/2026/08/04/ai-migration-techniques.html" target="_blank" rel="noopener"><strong>Confidence Techniques for Guiding AI Migration</strong></a> [<span style="color: #800000;">scottlogic.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.frankel.ch/github-agentic-workflow-renovate/" target="_blank" rel="noopener"><strong>GitHub agentic workflows and Renovate</strong></a> [<span style="color: #800000;">frankel.ch</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://hamvocke.com/blog/task-runners/" target="_blank" rel="noopener"><strong>Use Task Runners for Common Coding Tasks</strong></a> [<span style="color: #800000;">hamvocke.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://netflixtechblog.com/modeling-device-capabilities-for-analytics-e7607acebde8" target="_blank" rel="noopener"><strong>Modeling Device Capabilities for Analytics</strong></a> [<span style="color: #800000;">netflixtechblog.com</span>]</li>
<li><strong>&gt;&gt;</strong> <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://blog.scottlogic.com/2026/07/29/four-pillars-of-agentic-ai-success.html" target="_blank" rel="noopener"><strong>Four pillars of agentic AI success</strong></a> [<span style="color: #800000;">scottlogic.com</span>]</li>
</ul>
<h2 style="text-align: left;" id="bd-pick-of-the-week" data-id="pick-of-the-week">3.<strong> Pick of the Week</strong></h2>
<div class="bd-anchor" id="pick-of-the-week"></div>
<p><a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.paulgraham.com/vb.html"><strong>&gt;&gt; Life is Short</strong></a> [<span style="color: #993300;">paulgraham.com</span>]</p><p>The post <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/java-weekly-658">Java Weekly, Issue 658</a> first appeared on <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com">Baeldung</a>.</p><Img align="left" border="0" height="1" width="1" alt="" style="border:0;float:left;margin:0;padding:0;width:1px!important;height:1px!important;" hspace="0" src="https://feeds.feedblitz.com/~/i/967388204/0/baeldung">
<div style="clear:both;padding-top:0.2em;"><a title="Like on Facebook" href="https://feeds.feedblitz.com/_/28/967388204/baeldung"><img height="20" src="https://assets.feedblitz.com/i/fblike20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Pin it!" href="https://feeds.feedblitz.com/_/29/967388204/baeldung,https%3a%2f%2fwww.baeldung.com%2fwp-content%2fuploads%2f2016%2f10%2fsocial-Weekly-Reviews-4.jpg"><img height="20" src="https://assets.feedblitz.com/i/pinterest20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Post to X.com" href="https://feeds.feedblitz.com/_/24/967388204/baeldung"><img height="20" src="https://assets.feedblitz.com/i/x.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by email" href="https://feeds.feedblitz.com/_/19/967388204/baeldung"><img height="20" src="https://assets.feedblitz.com/i/email20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by RSS" href="https://feeds.feedblitz.com/_/20/967388204/baeldung"><img height="20" src="https://assets.feedblitz.com/i/rss20.png" style="border:0;margin:0;padding:0;"></a>&nbsp;&#160;</div>]]>
</content:encoded>
					
		
		
		<webfeeds:featuredImage>https://www.baeldung.com/wp-content/uploads/2016/10/social-Weekly-Reviews-4-150x150.jpg</webfeeds:featuredImage></item>
<item>
<feedburner:origLink>https://www.baeldung.com/spring-ai-google-genai-chat</feedburner:origLink>
		<title>Introduction to Google GenAI Chat and Spring AI</title>
		<link>https://feeds.feedblitz.com/~/967131092/0/baeldung~Introduction-to-Google-GenAI-Chat-and-Spring-AI</link>
		
		<dc:creator><![CDATA[Sudarshan Hiray]]></dc:creator>
		<pubDate>Fri, 07 Aug 2026 03:22:20 +0000</pubDate>
				<category><![CDATA[Artificial Intelligence]]></category>
		<category><![CDATA[popular]]></category>
		<category><![CDATA[Spring AI ChatClient]]></category>
		<guid isPermaLink="false">https://www.baeldung.com/?p=204581</guid>
					<description><![CDATA[<img src="https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-12-1024x536.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="max-width:100% !important;height:auto !important;float: left; margin-right: 5px;" loading="lazy" /><p>Learn how to integrate Google’s Gemini models into a Spring Boot application using the Gemini Developer API.</p>
<p>The post <a rel="NOFOLLOW" href="https://feeds.feedblitz.com/~/967131092/0/baeldung~Introduction-to-Google-GenAI-Chat-and-Spring-AI">Introduction to Google GenAI Chat and Spring AI</a> first appeared on <a rel="NOFOLLOW" href="https://www.baeldung.com">Baeldung</a>.</p><div style="clear:both;padding-top:0.2em;"><a title="Like on Facebook" href="https://feeds.feedblitz.com/_/28/967131092/baeldung"><img height="20" src="https://assets.feedblitz.com/i/fblike20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Pin it!" href="https://feeds.feedblitz.com/_/29/967131092/baeldung,https%3a%2f%2fwww.baeldung.com%2fwp-content%2fuploads%2f2024%2f07%2fJava-Featured-12-1024x536.jpg"><img height="20" src="https://assets.feedblitz.com/i/pinterest20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Post to X.com" href="https://feeds.feedblitz.com/_/24/967131092/baeldung"><img height="20" src="https://assets.feedblitz.com/i/x.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by email" href="https://feeds.feedblitz.com/_/19/967131092/baeldung"><img height="20" src="https://assets.feedblitz.com/i/email20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by RSS" href="https://feeds.feedblitz.com/_/20/967131092/baeldung"><img height="20" src="https://assets.feedblitz.com/i/rss20.png" style="border:0;margin:0;padding:0;"></a>&#160;</div>]]>
</description>
										<content:encoded><![CDATA[<img src="https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-12-1024x536.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="float: left; margin-right: 5px;" decoding="async" loading="lazy" srcset="https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-12-1024x536.jpg 1024w, https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-12-300x157.jpg 300w, https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-12-768x402.jpg 768w, https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-12-100x52.jpg 100w, https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-12.jpg 1200w" sizes="auto, (max-width: 580px) 100vw, 580px" /><h2 data-path-to-node="0" id="bd-introduction" data-id="introduction">1. Introduction</h2>
<div class="bd-anchor" id="introduction"></div>
<p data-path-to-node="1">The generative AI landscape is evolving rapidly, pushing framework maintainers to rethink how applications connect to diverse large language models (LLMs). With the release of <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/spring-ai">Spring AI</a>, Java developers gained a unified, portable interface to interact with cognitive services.</p>
<p data-path-to-node="2">However, as the ecosystem matured, integrating multiple foundational models within a single runtime classpath introduced configuration conflicts. To solve this, <strong>Spring AI introduced major architectural updates. These include the specialized <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://mvnrepository.com/artifact/org.springframework.ai/spring-ai-starter-model-google-genai"><em>spring-ai-starter-model-google-genai</em> starter</a> module and an explicit model selection paradigm.</strong></p>
<p data-path-to-node="3">In this tutorial, we&#8217;ll explore how to integrate Google’s Gemini models into a Spring Boot application using the <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://docs.spring.io/spring-ai/reference/api/chat/google-genai-chat.html#_option_1_gemini_developer_api_api_key">Gemini Developer API</a> (via Google AI Studio). We&#8217;ll walk through a streamlined project setup, dive into text generation patterns using both low-level and high-level APIs, apply prompt templates, enable live web grounding, and stream tokens reactively with WebFlux.</p>
<h2 data-path-to-node="4" id="bd-project-setup" data-id="project-setup">2. Project Setup</h2>
<div class="bd-anchor" id="project-setup"></div>
<p data-path-to-node="5">To get started, we need a standard Spring Boot 3.x application. Because Spring AI modules are actively updated, it&#8217;s highly recommended to manage dependency versions using the Spring AI <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/spring-maven-bom">Bill of Materials (BOM)</a>.</p>
<h3 data-path-to-node="6" id="bd-1-maven-dependencies" data-id="1-maven-dependencies">2.1. Maven Dependencies</h3>
<div class="bd-anchor" id="1-maven-dependencies"></div>
<p data-path-to-node="7">First, let&#8217;s configure the <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://mvnrepository.com/artifact/org.springframework.ai/spring-ai-starter-model-google-genai">Spring AI Starter Google GenAI</a> and <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://mvnrepository.com/artifact/org.springframework.ai/spring-ai-bom/2.0.0">Spring AI BOM</a> in our <em>pom.xml </em>file. <strong>Since Spring AI artifacts are hosted in the Spring Milestones repository during release cycles, we ensure both the repository and dependency management blocks are properly declared</strong>:</p>
<pre><code class="language-java">&lt;project&gt;
    &lt;dependencyManagement&gt;
        &lt;dependencies&gt;
            &lt;dependency&gt;
                &lt;groupId&gt;org.springframework.ai&lt;/groupId&gt;
                &lt;artifactId&gt;spring-ai-bom&lt;/artifactId&gt;
                &lt;version&gt;2.0.0&lt;/version&gt;
                &lt;type&gt;pom&lt;/type&gt;
                &lt;scope&gt;import&lt;/scope&gt;
            &lt;/dependency&gt;
        &lt;/dependencies&gt;
    &lt;/dependencyManagement&gt;
    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.ai&lt;/groupId&gt;
            &lt;artifactId&gt;spring-ai-starter-model-google-genai&lt;/artifactId&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;
&lt;/project&gt;</code></pre>
<h3 data-path-to-node="5" id="bd-2-activating-the-chat-provider-via-springaimodelchat" data-id="2-activating-the-chat-provider-via-springaimodelchat">2.2. Activating the Chat Provider via <em>spring.ai.model.chat</em></h3>
<div class="bd-anchor" id="2-activating-the-chat-provider-via-springaimodelchat"></div>
<p data-path-to-node="6">In the latest versions of Spring AI, dropping a starter module onto the classpath no longer automatically activates it. This prevents runtime configuration conflicts when your application references multiple LLM providers.</p>
<p data-path-to-node="7"><strong>We must explicitly declare our active chat provider inside our <em>application.properties</em> file</strong>:</p>
<pre><code class="language-java">spring.ai.model.chat=google-genai
spring.ai.google.genai.chat.model=gemini-2.5-flash
spring.ai.google.genai.api-key=${SPRING_AI_GOOGLE_GENAI_API_KEY}</code></pre>
<p>Obtain an API key from the <a class="external" href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://aistudio.google.com/app/apikey" target="_blank" rel="noopener">Google AI Studio</a>. Using this placeholder syntax explicitly documents the application&#8217;s external runtime dependency while preventing hardcoded secrets from leaking into our source control repository.</p>
<h3 data-path-to-node="10" id="bd-3-environment-setup" data-id="3-environment-setup">2.3. Environment Setup</h3>
<div class="bd-anchor" id="3-environment-setup"></div>
<p data-path-to-node="11">We can satisfy the property placeholder defined above without writing any custom Java decryption or binding code. <strong>We&#8217;ll simply export the credentials directly into our environment</strong>. The auto-configuration engine will automatically resolve the placeholder value at startup:</p>
<pre><code class="language-java">export SPRING_AI_GOOGLE_GENAI_API_KEY="YourActualSecureGoogleAIStudioKeyHere"</code></pre>
<p>Once this environment variable is set, Spring Boot seamlessly bridges the variable to the properties file placeholder, making the<em> GoogleGenAiChatModel</em> ready for injection.</p>
<h2 data-path-to-node="22" id="bd-core-text-and-content-generation-strategies" data-id="core-text-and-content-generation-strategies">3. Core Text and Content Generation Strategies</h2>
<div class="bd-anchor" id="core-text-and-content-generation-strategies"></div>
<p data-path-to-node="23">Spring AI gives us two primary abstraction layers for communicating with Gemini models: <strong>the foundational <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/spring-artificial-intelligence-structure-output#brief-introduction-to-the-chat-model"><em>ChatModel</em></a> bean and the highly configurable, fluent <em>ChatClient</em> API. </strong> We isolate our interaction logic into a dedicated service layer to follow enterprise design patterns and keep code clean. Then, we expose these capabilities through standard Spring REST controllers.</p>
<h3 data-path-to-node="24" id="bd-1-injecting-and-using-googlegenaichatmodel" data-id="1-injecting-and-using-googlegenaichatmodel">3.1. Injecting and Using <em>GoogleGenAiChatModel</em></h3>
<div class="bd-anchor" id="1-injecting-and-using-googlegenaichatmodel"></div>
<p data-path-to-node="24">The <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://docs.spring.io/spring-ai/docs/current/api/org/springframework/ai/google/genai/GoogleGenAiChatModel.html"><em>GoogleGenAiChatModel </em></a>represents the foundational, low-level client abstraction.<strong> It handles request serialization, HTTP execution against Google&#8217;s RPC endpoints, and raw response parsing.</strong></p>
<p data-path-to-node="25">Let&#8217;s look at our <em>ChatService, </em>where we inject this bean along with a <em>ChatClient.Builder</em> to initialize our operational layers:</p>
<pre><code class="language-java">@Service
public class ChatService {
    private final GoogleGenAiChatModel chatModel;
    private final ChatClient defaultChatClient;
    private final ChatClient fluentChatClient;
    public ChatService(GoogleGenAiChatModel chatModel, ChatClient.Builder chatClientBuilder) {
        this.chatModel = chatModel;
        this.defaultChatClient = chatClientBuilder.build();
        this.fluentChatClient = chatClientBuilder
          .defaultSystem("You are a concise technical writer summarizing software concepts.")
          .build();
    }
    public String simplifiedPrompt(String message) {
        return chatModel.call(message);
    }
}</code></pre>
<p data-path-to-node="28">When we invoke <em>chatModel.call(message)</em>, the framework packages the raw string into a default prompt context. It then sends this to the configured model variant, extracts the text block from the response payload, and returns it. We expose this in our <em>ChatController</em>:</p>
<pre><code class="language-java">@RestController
public class ChatController {
    private final ChatService chatService;
    public ChatController(ChatService chatService) {
        this.chatService = chatService;
    }
    @GetMapping("/v1/chat/simple")
    public String simplifiedPrompt(@RequestParam(defaultValue = "Hello") String message) {
        return chatService.simplifiedPrompt(message);
    }
}</code></pre>
<p data-path-to-node="29">This establishes a simple string-in, string-out endpoint. It uses the lowest-level model client abstraction available to verify basic connectivity with the Gemini engine.</p>
<h3 data-path-to-node="29" id="bd-2-constructing-prompts-with-the-fluent-chatclient-api" data-id="2-constructing-prompts-with-the-fluent-chatclient-api">3.2. Constructing Prompts With the Fluent <em>ChatClient</em> API</h3>
<div class="bd-anchor" id="2-constructing-prompts-with-the-fluent-chatclient-api"></div>
<p data-path-to-node="30">While working directly with the model handles quick operations, production architectures favor the fluent <em>ChatClient </em>API. <strong>The <em>ChatClient </em>acts as a facade layer that simplifies prompt assembly and attaches default configuration baselines.</strong></p>
<p data-path-to-node="31">As shown in our service constructor, we pre-configured a specialized <span style="margin: 0px;padding: 0px">fluent<em>ChatClient </em>with</span> system instructions. Let&#8217;s add the corresponding execution method to <em>ChatService</em>:</p>
<pre><code class="language-java">public String fluentPrompt(String prompt) {
    return this.fluentChatClient.prompt()
      .user(prompt)
      .call()
      .content();
}</code></pre>
<p data-path-to-node="33">By routing requests through this pre-configured instance, every user query automatically appends the target system instructions before execution. We expose this endpoint in the controller:</p>
<pre><code class="language-java">@GetMapping("/v1/chat/fluent")
public String fluentPrompt(@RequestParam String prompt) {
    return chatService.fluentPrompt(prompt);
}</code></pre>
<p data-path-to-node="35">This allows us to encapsulate recurring personas or systemic behavior directly within the client wrapper instance. As a result, we don&#8217;t need to modify individual incoming text arguments manually.</p>
<h3 data-path-to-node="35" id="bd-3-handling-dynamic-inputs-via-prompt-templates" data-id="3-handling-dynamic-inputs-via-prompt-templates">3.3. Handling Dynamic Inputs via Prompt Templates</h3>
<div class="bd-anchor" id="3-handling-dynamic-inputs-via-prompt-templates"></div>
<p data-path-to-node="36">Hardcoding parameter logic directly inside strings leads to messy string concatenation and fragile codebases. <strong>Spring AI solves this with structured prompt templates, separating instructions from user variables.</strong></p>
<p data-path-to-node="37">Let&#8217;s implement a code review capability inside our <em>ChatService </em>using a multiline text block placeholder layout:</p>
<pre><code class="language-java">public String reviewCode(String language, String codeSnippet) {
    String template = """
            Analyze the following {language} code snippet for memory leaks or inefficiencies.
            Provide an optimized version.
            Code:
            {code}
            """;
    return this.defaultChatClient.prompt()
      .user(u -&gt; u.text(template).params(Map.of("language", language, "code", codeSnippet)))
      .call()
      .content();
}</code></pre>
<p>The client replaces the target tags (<em>{language}</em>,<em> {code}</em>) at runtime, formatting the text cleanly before submission. We expose this via a <em>POST </em>request wrapper in our controller:</p>
<pre><code class="language-java">@PostMapping("/v1/chat/review")
public String reviewCode(@RequestParam(defaultValue = "Java") String language, @RequestBody String codeSnippet) {
    return chatService.reviewCode(language, codeSnippet);
}</code></pre>
<p data-path-to-node="39">This pattern cleanly decouples structural prompt engineering boundaries from volatile business data, producing a highly reusable, parameter-driven invocation pattern.</p>
<h3 data-path-to-node="39" id="bd-4-grounding-responses-with-live-google-search-retrieval" data-id="4-grounding-responses-with-live-google-search-retrieval">3.4. Grounding Responses With Live Google Search Retrieval</h3>
<div class="bd-anchor" id="4-grounding-responses-with-live-google-search-retrieval"></div>
<p data-path-to-node="40">LLMs naturally suffer from training cut-off windows and knowledge gaps regarding live, real-world developments.<strong> The Google GenAI module exposes an explicit grounding property to seamlessly connect our model with live Google Search indexing.</strong></p>
<p data-path-to-node="41">To activate live search grounding across our entire application, we need to toggle the grounding flag to <em>true</em> inside our configuration properties:</p>
<pre><code class="language-java">spring.ai.google.genai.chat.google-search-retrieval=true</code></pre>
<p>When this property is enabled, queries regarding current events or breaking news are automatically grounded using fresh search results. Let&#8217;s write an endpoint to handle real-time informational requests:</p>
<pre><code class="language-java">public String searchGroundedPrompt(String currentEventQuery) {
    return this.defaultChatClient.prompt()
      .user(currentEventQuery)
      .call()
      .content();
}</code></pre>
<p data-path-to-node="45">We map this method to our HTTP layer inside our controller configuration:</p>
<pre><code class="language-java">@GetMapping("/v1/chat/grounded")
public String searchGroundedPrompt(@RequestParam String currentEventQuery) {
    return chatService.searchGroundedPrompt(currentEventQuery);
}</code></pre>
<p data-path-to-node="45">If we pass a request like <i data-path-to-node="33" data-index-in-node="26">&#8220;Who won the most recent football tournament match yesterday?&#8221;</i>, the model uses live Google Search indexing to anchor its response in verified facts, drastically reducing hallucinations. This configuration bridges the gap between static training cutoffs and live, real-world events.</p>
<h3 data-path-to-node="49" id="bd-5-processing-real-time-streaming-responses-with-flux" data-id="5-processing-real-time-streaming-responses-with-flux">3.5. Processing Real-Time Streaming Responses With <em>Flux</em></h3>
<div class="bd-anchor" id="5-processing-real-time-streaming-responses-with-flux"></div>
<p data-path-to-node="50">For user-facing interfaces, waiting for a long text block to fully generate on the server results in noticeable latency.<strong> Instead, we can stream chunks back to the user, token by token, using Spring Boot&#8217;s WebFlux integration.</strong></p>
<p data-path-to-node="51">Let&#8217;s add a streaming method to our <span style="margin: 0px;padding: 0px"><em>ChatService </em>that</span> returns a reactive <em>Flux&lt;String&gt;</em> structure:</p>
<pre><code class="language-java">public Flux&lt;String&gt; streamChatTokens(String prompt) {
    return this.defaultChatClient.prompt()
      .user(prompt)
      .stream()
      .content();
}</code></pre>
<p data-path-to-node="50">We&#8217;ll create a specialized <em>StreamingChatController </em>to deliver these tokens cleanly over an open connection pipeline. This controller explicitly produces a <em>text/event-stream</em> media response:</p>
<pre><code class="language-java">@RestController
public class StreamingChatController {
    private final ChatService chatService;
    public StreamingChatController(ChatService chatService) {
        this.chatService = chatService;
    }
    @GetMapping(value = "/v1/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux&lt;String&gt; streamChatTokens(@RequestParam String prompt) {
        return chatService.streamChatTokens(prompt);
    }
}</code></pre>
<p data-path-to-node="50">When hitting this endpoint, consumers read token data reactively.  This establishes a non-blocking execution pipeline that builds an interactive, low-latency UI flow chunk by chunk.</p>
<h2 data-path-to-node="55" id="bd-testing" data-id="testing">4. Testing</h2>
<div class="bd-anchor" id="testing"></div>
<p data-path-to-node="56">Testing applications that rely on external generative AI endpoints requires a clear separation of concerns. <strong>Running tests shouldn&#8217;t trigger external network calls, hit rate limits, or exhaust API quotas during local compilation</strong>. To prevent this, we isolate our HTTP layer using Spring Boot&#8217;s slice testing along with <em>@MockitoBean</em>.</p>
<h3 data-path-to-node="57" id="bd-1-unit-testing-via-weblayer-mockmvc-mocking" data-id="1-unit-testing-via-weblayer-mockmvc-mocking">4.1. Unit Testing via WebLayer MockMvc Mocking</h3>
<div class="bd-anchor" id="1-unit-testing-via-weblayer-mockmvc-mocking"></div>
<p data-path-to-node="58">Since our implementation encapsulates all LLM communications within <em>ChatService</em>, we can completely mock this business component. <strong>This permits us to cleanly assert controller routing, request parameters, response body expectations, and stream-handling patterns.</strong></p>
<p data-path-to-node="59">Let&#8217;s write an isolated test using <em>MockMvc </em>to verify all text-based, template-driven, and reactive stream endpoints without triggering an actual network handshake. First, we set up our simple prompt test to verify basic GET parameter routing and raw response mapping:</p>
<pre><code class="language-java">@SpringBootTest(properties = {
    "spring.ai.model.chat=google-genai",
    "spring.ai.google.genai.chat.model=gemini-2.5-flash",
    "spring.ai.google.genai.api-key=test-key"
})
@AutoConfigureMockMvc
class ChatControllerUnitTest {
    @Autowired
    private MockMvc mockMvc;
    @MockBean
    private ChatService chatService;
    @Test
    void whenSimpleEndpointIsInvoked_thenReturnsMockedModelResponse() throws Exception {
        when(chatService.simplifiedPrompt(anyString())).thenReturn("Mocked Gemini Response");
        mockMvc.perform(get("/v1/chat/simple").param("message", "Hello"))
          .andExpect(status().isOk())
          .andExpect(content().string("Mocked Gemini Response"));
        verify(chatService).simplifiedPrompt("Hello");
    }
}
</code></pre>
<p>Next, we confirm that our fluent prompt endpoint forwards query parameters accurately to the underlying service. Then it returns the configured <em>system-prompt-backed</em> response:</p>
<pre><code class="language-java">@Test
void whenFluentEndpointIsInvoked_thenReturnsModelContent() throws Exception {
    when(chatService.fluentPrompt(anyString())).thenReturn("Fluent Response");
    mockMvc.perform(get("/v1/chat/fluent").param("prompt", "Explain DI"))
      .andExpect(status().isOk())
      .andExpect(content().string("Fluent Response"));
    verify(chatService).fluentPrompt("Explain DI");
}</code></pre>
<p>For our template-driven endpoint, we validate that the controller correctly processes a multipart payload consisting of query parameters and a <em>text/plain</em> request body:</p>
<pre><code class="language-java">@Test
void whenReviewEndpointIsInvoked_thenReturnsModelContent() throws Exception {
    when(chatService.reviewCode(anyString(), anyString())).thenReturn("Optimized Code");
    mockMvc.perform(post("/v1/chat/review")
        .param("language", "Java")
        .contentType(MediaType.TEXT_PLAIN)
        .content("class A { }"))
      .andExpect(status().isOk())
      .andExpect(content().string("Optimized Code"));
    verify(chatService).reviewCode("Java", "class A { }");
}</code></pre>
<pre></pre>
<p>We&#8217;ll now verify that search-grounded queries pass their parameters cleanly through the controller layer without parameter binding errors:</p>
<pre><code class="language-java">@Test
void whenGroundedEndpointIsInvoked_thenReturnsModelContent() throws Exception {
    when(chatService.searchGroundedPrompt(anyString())).thenReturn("Grounded Response");
    mockMvc.perform(get("/v1/chat/grounded").param("currentEventQuery", "latest match winner"))
      .andExpect(status().isOk())
      .andExpect(content().string("Grounded Response"));
    verify(chatService).searchGroundedPrompt("latest match winner");
}</code></pre>
<p>Finally, we test our reactive endpoint to ensure Spring MVC properly negotiates the <em>text/event-stream</em> media type. It will also format the reactive <em>Flux </em>elements into standard Server-Sent Event (<em>data:</em>) frames:</p>
<pre><code class="language-java">@Test
void whenStreamEndpointIsInvoked_thenReturnsEventStreamContent() throws Exception {
    when(chatService.streamChatTokens(anyString()))
      .thenReturn(reactor.core.publisher.Flux.just("token-1", "token-2"));
    mockMvc.perform(get("/v1/chat/stream").param("prompt", "Stream tokens"))
      .andExpect(status().isOk())
      .andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_EVENT_STREAM))
      .andExpect(content().string("data:token-1\n\ndata:token-2\n\n"));
    
    verify(chatService).streamChatTokens("Stream tokens");
}</code></pre>
<p>By leveraging the modern <em>@MockBean </em>annotation, we inject our mock definitions directly into the application context wrapper, replacing the real bean configuration. <strong>This allows us to stub out standard synchronous operations as well as complex reactive <em>Flux </em>stream responses safely and reliably without network overhead.</strong></p>
<h2 data-path-to-node="57" id="bd-conclusion" data-id="conclusion">5. Conclusion</h2>
<div class="bd-anchor" id="conclusion"></div>
<p data-path-to-node="64">In this tutorial, we configured a Spring Boot application using Spring AI&#8217;s updated <em>spring-ai-starter-model-google-genai</em> engine to interact directly with Google AI Studio.</p>
<p data-path-to-node="64">We saw how explicitly defining <em>spring.ai.model.chat=google-genai</em> resolves modern multi-model classpath dependencies. From there, we established a clean architecture using a dedicated <em>ChatService</em>. We built out flexible prompt workflows with the fluent <em>ChatClient </em>API and isolated dynamic parameters using templates. Finally, we enabled live web grounding and streamed responsive outputs via WebFlux <em>Flux.</em></p>
<p data-path-to-node="43">As always, the complete code samples used in this article are available <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://github.com/eugenp/tutorials/tree/master/spring-ai-modules/spring-ai-chat">over on GitHub</a>.</p><p>The post <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com/spring-ai-google-genai-chat">Introduction to Google GenAI Chat and Spring AI</a> first appeared on <a href="http://feeds.feedblitz.com/~/t/0/0/baeldung/~https://www.baeldung.com">Baeldung</a>.</p><Img align="left" border="0" height="1" width="1" alt="" style="border:0;float:left;margin:0;padding:0;width:1px!important;height:1px!important;" hspace="0" src="https://feeds.feedblitz.com/~/i/967131092/0/baeldung">
<div style="clear:both;padding-top:0.2em;"><a title="Like on Facebook" href="https://feeds.feedblitz.com/_/28/967131092/baeldung"><img height="20" src="https://assets.feedblitz.com/i/fblike20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Pin it!" href="https://feeds.feedblitz.com/_/29/967131092/baeldung,https%3a%2f%2fwww.baeldung.com%2fwp-content%2fuploads%2f2024%2f07%2fJava-Featured-12-1024x536.jpg"><img height="20" src="https://assets.feedblitz.com/i/pinterest20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Post to X.com" href="https://feeds.feedblitz.com/_/24/967131092/baeldung"><img height="20" src="https://assets.feedblitz.com/i/x.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by email" href="https://feeds.feedblitz.com/_/19/967131092/baeldung"><img height="20" src="https://assets.feedblitz.com/i/email20.png" style="border:0;margin:0;padding:0;"></a>&#160;<a title="Subscribe by RSS" href="https://feeds.feedblitz.com/_/20/967131092/baeldung"><img height="20" src="https://assets.feedblitz.com/i/rss20.png" style="border:0;margin:0;padding:0;"></a>&#160;</div>]]>
</content:encoded>
					
		
		
		<webfeeds:featuredImage>https://www.baeldung.com/wp-content/uploads/2024/07/Java-Featured-12-150x150.jpg</webfeeds:featuredImage></item>
</channel></rss>

