<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Java on Devops Monk</title><link>https://blog.devops-monk.com/tags/java/</link><description>Recent content in Java on Devops Monk</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Sat, 23 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.devops-monk.com/tags/java/index.xml" rel="self" type="application/rss+xml"/><item><title>Container Reuse for Fast Feedback Loops</title><link>https://blog.devops-monk.com/tutorials/testcontainers/container-reuse/</link><pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/testcontainers/container-reuse/</guid><description>The singleton pattern shares a container within a single test run. Container reuse goes further — it keeps the container alive after the JVM exits and reuses it in the next test run. The first run pays the startup cost (8 seconds for Kafka). Every subsequent run skips it entirely. For a developer who runs the test suite dozens of times per day, this saves minutes of waiting.
What You&amp;rsquo;ll Learn Enabling container reuse with .</description></item><item><title>Database Migration Testing — Flyway and Liquibase with Testcontainers</title><link>https://blog.devops-monk.com/tutorials/testcontainers/flyway-liquibase-testing/</link><pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/testcontainers/flyway-liquibase-testing/</guid><description>Database migrations are the most dangerous code in most applications. A failed migration in production means downtime. A migration that succeeds but corrupts data is worse. Testing migrations against the actual target database — not H2, not a manual inspection — is the only way to catch migration bugs before they reach production.
This article covers testing Flyway and Liquibase migrations with Testcontainers, verifying schema correctness after migration, and testing rollback scenarios.</description></item><item><title>JUnit 5 Integration — Lifecycle and Annotations</title><link>https://blog.devops-monk.com/tutorials/testcontainers/junit5-lifecycle-annotations/</link><pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/testcontainers/junit5-lifecycle-annotations/</guid><description>The biggest performance factor in a Testcontainers test suite is how many times you start and stop containers. Start a PostgreSQL container once per JVM and your tests add 2 seconds to total CI time. Start it once per test method and 50 tests cost 100 seconds. Understanding Testcontainers lifecycle management is how you write fast tests without sacrificing isolation.
This article covers all the container lifecycle options in JUnit 5 — from the simplest @Container annotation to the singleton pattern that shares one container across your entire test suite.</description></item><item><title>Kafka Integration Testing with Testcontainers</title><link>https://blog.devops-monk.com/tutorials/testcontainers/kafka-testing/</link><pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/testcontainers/kafka-testing/</guid><description>@EmbeddedKafka has long been the standard for Kafka testing in Spring Boot. It works, but it runs a different Kafka implementation than production — one that can accept messages and serialize records in ways that a real Kafka broker would not. KafkaContainer runs the same Confluent Kafka image your production environment uses, with the same broker configuration, same partition behavior, and same consumer group offset management. This article shows how to test Kafka producers, consumers, and dead-letter topics with a real broker.</description></item><item><title>Keycloak and OAuth2 Testing with Testcontainers</title><link>https://blog.devops-monk.com/tutorials/testcontainers/keycloak-oauth2-testing/</link><pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/testcontainers/keycloak-oauth2-testing/</guid><description>Testing Spring Security with @WithMockUser and SecurityMockMvcRequestPostProcessors.jwt() verifies that your authorization annotations are wired correctly, but it does not test JWT validation, token signing algorithms, issuer URL verification, or clock skew handling. These require a real OAuth2 provider. KeycloakContainer runs a real Keycloak instance in Docker, giving you a complete OIDC server for integration tests.
What You&amp;rsquo;ll Learn KeycloakContainer setup from the community module Importing a Keycloak realm for tests Retrieving JWT access tokens programmatically Testing protected endpoints with real JWTs Testing role-based access control (RBAC) Testing token expiry and refresh flows Dependencies KeycloakContainer is not in the Testcontainers core library.</description></item><item><title>LocalStack — Testing AWS Services (S3, SQS, SNS) with Testcontainers</title><link>https://blog.devops-monk.com/tutorials/testcontainers/localstack-aws-testing/</link><pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/testcontainers/localstack-aws-testing/</guid><description>Testing AWS integrations against real AWS services requires IAM credentials, costs money per test run, and creates real side effects (files in S3, messages in SQS). LocalStack runs a local AWS cloud emulator in Docker — the same API, the same response format, but running locally with no AWS account required. This article covers testing S3, SQS, and SNS integrations with LocalStackContainer.
What You&amp;rsquo;ll Learn LocalStackContainer setup and @ServiceConnection Creating S3 buckets and testing file upload/download Testing SQS message producers and @SqsListener consumers Testing SNS to SQS subscriptions Creating AWS resources before tests with the AWS SDK Testing DynamoDB operations Dependencies &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;io.</description></item><item><title>MongoDB and NoSQL Testing with Testcontainers</title><link>https://blog.devops-monk.com/tutorials/testcontainers/mongodb-nosql-testing/</link><pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/testcontainers/mongodb-nosql-testing/</guid><description>MongoDB&amp;rsquo;s document model introduces testing challenges that relational database tests do not have: schema flexibility (different documents can have different fields), aggregation pipelines, geospatial queries, and text search. Testing these against an embedded Flapdoodle MongoDB works for basic CRUD but breaks down for aggregation pipelines, custom index types, and MongoDB-version-specific behavior. This article covers MongoDB testing with a real MongoDB container.
What You&amp;rsquo;ll Learn MongoDBContainer setup and @ServiceConnection @DataMongoTest slice testing for repositories Testing aggregation pipelines Testing compound indexes and text indexes Querying documents with MongoTemplate Testing document validation rules Dependencies &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;org.</description></item><item><title>MySQL Testing with Testcontainers</title><link>https://blog.devops-monk.com/tutorials/testcontainers/mysql-testing/</link><pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/testcontainers/mysql-testing/</guid><description>MySQL is still the most widely deployed relational database in Java applications. Its SQL dialect, strict mode behavior, and character set handling differ from both H2 and PostgreSQL in ways that matter for real applications. This article covers MySQL testing with Testcontainers — from basic setup to MySQL-specific features and the STRICT_TRANS_TABLES mode that catches data truncation bugs H2 silently ignores.
What You&amp;rsquo;ll Learn MySQLContainer setup and configuration @ServiceConnection with MySQL MySQL strict mode and why it matters for tests Character set and collation configuration Testing MySQL-specific SQL functions MariaDB as an alternative Dependencies &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;org.</description></item><item><title>Parallel Test Execution with Testcontainers</title><link>https://blog.devops-monk.com/tutorials/testcontainers/parallel-test-execution/</link><pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/testcontainers/parallel-test-execution/</guid><description>A test suite with 50 integration test classes runs sequentially in 10 minutes. Configured for parallel execution across 4 threads, it completes in 3 minutes. Parallel test execution with Testcontainers requires careful data isolation — tests running simultaneously against the same database will step on each other&amp;rsquo;s data without it. This article covers JUnit 5 parallel configuration, container sharing strategies, and data isolation techniques.
What You&amp;rsquo;ll Learn JUnit 5 parallel execution configuration with junit-platform.</description></item><item><title>PostgreSQL Testing with Testcontainers</title><link>https://blog.devops-monk.com/tutorials/testcontainers/postgresql-testing/</link><pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/testcontainers/postgresql-testing/</guid><description>Testing database code against H2 gives you a false sense of security. PostgreSQL has different behavior for ON CONFLICT, RETURNING, JSON operators, full-text search, and dozens of other features. A JPA query that works in H2 dialect may fail against real PostgreSQL. This article shows how to test your Spring Data JPA repositories, custom queries, and database constraints against a real PostgreSQL instance using Testcontainers.
What You&amp;rsquo;ll Learn @ServiceConnection — the Spring Boot 3.</description></item><item><title>RabbitMQ Testing with Testcontainers</title><link>https://blog.devops-monk.com/tutorials/testcontainers/rabbitmq-testing/</link><pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/testcontainers/rabbitmq-testing/</guid><description>RabbitMQ&amp;rsquo;s exchange-queue model, routing keys, dead-letter exchanges, and message TTL are features that cannot be tested with mocks. The broker&amp;rsquo;s routing logic — topic exchange patterns, fanout behavior, message acknowledgment, and requeue behavior on rejection — all require a real broker. This article covers RabbitMQ integration testing with RabbitMQContainer.
What You&amp;rsquo;ll Learn RabbitMQContainer setup and @ServiceConnection Configuring exchanges, queues, and bindings for tests Testing @RabbitListener consumers with Awaitility Testing dead-letter exchange and dead-letter queue routing Testing message TTL and automatic expiry Testing acknowledgment modes Dependencies &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;org.</description></item><item><title>Redis Testing with Testcontainers</title><link>https://blog.devops-monk.com/tutorials/testcontainers/redis-testing/</link><pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/testcontainers/redis-testing/</guid><description>Redis cache testing with a real Redis instance catches behaviors that embedded or mocked alternatives cannot: TTL expiry timing, cache eviction under memory pressure, cluster failover behavior, and the exact serialization format that Redis stores data in. This article covers testing Spring Cache, Spring Session, and distributed locks against a real Redis container.
What You&amp;rsquo;ll Learn Redis GenericContainer setup and @ServiceConnection (Spring Boot 3.2+) Testing Spring Cache with @Cacheable, @CacheEvict, and @CachePut Verifying TTL expiry behavior Testing Spring Session with Redis Testing distributed locks with Redisson or Spring Integration Dependencies &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;org.</description></item><item><title>Setting Up Testcontainers in a Java Project</title><link>https://blog.devops-monk.com/tutorials/testcontainers/setup-testcontainers-java/</link><pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/testcontainers/setup-testcontainers-java/</guid><description>Setting up Testcontainers takes about five minutes. The dependencies are on Maven Central, the BOM manages all version alignment, and Spring Boot 3 has first-class support that eliminates most of the configuration boilerplate. By the end of this article, you will have a Spring Boot project with Testcontainers running, a PostgreSQL container backing your first integration test, and a clear understanding of how to add any additional container module.
What You&amp;rsquo;ll Learn How the Testcontainers BOM works and why you need it Which dependencies to add for Spring Boot, PostgreSQL, and JUnit 5 How to configure Maven and Gradle for Testcontainers tests How to set up Logback so you can see container startup logs How @ServiceConnection eliminates @DynamicPropertySource boilerplate in Spring Boot 3.</description></item><item><title>Singleton Containers and Shared Base Classes</title><link>https://blog.devops-monk.com/tutorials/testcontainers/singleton-containers/</link><pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/testcontainers/singleton-containers/</guid><description>With 20 integration test classes, each starting its own PostgreSQL container, you pay 20 container starts at 2 seconds each — 40 seconds of pure overhead before a single assertion runs. The singleton pattern starts one container per JVM, shares it across all test classes, and cuts that 40 seconds to 2. This is the most impactful performance optimization for large Testcontainers test suites.
What You&amp;rsquo;ll Learn The singleton pattern using a static initializer block Abstract base class design for sharing containers and @DynamicPropertySource Why you must not combine @Testcontainers/@Container with the singleton pattern Spring TestContext caching — how it works and how to maximize reuse The @ImportTestcontainers approach for Spring Boot 3.</description></item><item><title>Spring Boot Testing Slices — @DataJpaTest, @WebMvcTest, and @SpringBootTest</title><link>https://blog.devops-monk.com/tutorials/testcontainers/spring-boot-testing-slices/</link><pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/testcontainers/spring-boot-testing-slices/</guid><description>@SpringBootTest starts the full application context — all beans, all auto-configurations, all datasources, all message listeners. That is 10–30 seconds of startup time for every test class that needs it. Spring Boot&amp;rsquo;s test slices load a subset of the application context — only the beans relevant to what you are testing. Repository tests that once took 15 seconds with @SpringBootTest take 2 seconds with @DataJpaTest. This article covers all the major test slices and how to combine them with Testcontainers.</description></item><item><title>Testcontainers in CI/CD — GitHub Actions, Docker, and Production Patterns</title><link>https://blog.devops-monk.com/tutorials/testcontainers/testcontainers-cicd/</link><pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/testcontainers/testcontainers-cicd/</guid><description>Testcontainers tests that run perfectly on your developer laptop can fail in CI for environmental reasons — Docker daemon availability, image pull timeouts, port conflicts, and resource limits. This final article covers production-ready CI/CD configuration for Testcontainers in GitHub Actions, GitLab CI, and Jenkins, plus Spring Boot 3.1&amp;rsquo;s development-time container support.
What You&amp;rsquo;ll Learn GitHub Actions configuration for Testcontainers (zero extra setup on ubuntu-latest) GitLab CI configuration with Docker-in-Docker Jenkins configuration with Docker socket mounting TestApplication — running your app locally with containers instead of a real database .</description></item><item><title>Understanding Wait Strategies in Testcontainers</title><link>https://blog.devops-monk.com/tutorials/testcontainers/wait-strategies/</link><pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/testcontainers/wait-strategies/</guid><description>A container being started does not mean it is ready to accept connections. Docker marks the container as running the moment the entrypoint process starts. PostgreSQL needs another second or two to initialize storage, bind to port 5432, and start accepting connections. If your test code tries to connect before the database is ready, you get Connection refused.
The wrong solution is Thread.sleep(5000). That is a fixed delay: too short on a slow machine, too long on a fast one.</description></item><item><title>Unit Testing vs Integration Testing vs System Testing in Java</title><link>https://blog.devops-monk.com/tutorials/testcontainers/unit-integration-system-testing/</link><pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/testcontainers/unit-integration-system-testing/</guid><description>&amp;ldquo;We have 90% test coverage&amp;rdquo; means nothing if 90% of your tests are mocking away the parts that matter. A codebase full of unit tests that mock every dependency can have excellent coverage numbers while completely missing bugs that manifest when real components interact. Understanding the difference between unit tests, integration tests, and system tests — and when to use each — is the foundation of a test suite that actually finds bugs.</description></item><item><title>What Is Testcontainers and Why You Need It</title><link>https://blog.devops-monk.com/tutorials/testcontainers/what-is-testcontainers/</link><pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/testcontainers/what-is-testcontainers/</guid><description>Your CI pipeline goes green. You deploy to staging. The application crashes on startup because Flyway migrations fail against MySQL — but all your tests used H2. Your team has seen this before. Tests that pass on every machine, fail in production. Not because the business logic was wrong, but because the test infrastructure was fake.
This is the problem Testcontainers was built to solve.
This article covers what Testcontainers is, why traditional approaches to integration testing fall short, and how Testcontainers gives you test environments that match production — automatically, repeatably, with no shared test database to maintain.</description></item><item><title>WireMock — Testing External REST API Integrations</title><link>https://blog.devops-monk.com/tutorials/testcontainers/wiremock-testing/</link><pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/testcontainers/wiremock-testing/</guid><description>Most Spring Boot applications call external APIs — payment gateways, shipping providers, notification services, CRM systems. Testing these integrations is hard because external APIs have rate limits, authentication requirements, and a habit of being unavailable at exactly the wrong moment. WireMock runs a real HTTP server in a Docker container, accepts your requests, and returns whatever responses you configure. This article covers WireMock as a Testcontainer for testing Spring Boot REST client integrations.</description></item><item><title>AOT Compilation in Java 25 (JEP 514 &amp; 515): Faster Startup, Zero Warm-Up</title><link>https://blog.devops-monk.com/tutorials/java25/aot-compilation/</link><pubDate>Sun, 10 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java25/aot-compilation/</guid><description>The Java Startup Problem Java&amp;rsquo;s performance story has always had one weak spot: startup time.
When a JVM starts, it:
Loads and verifies class bytecode Interprets bytecode (slow) Profiles which methods are called most (warm-up) Compiles hot methods to native code via JIT (takes time and CPU) Eventually reaches peak throughput This process takes seconds for large applications. For a Spring Boot application, typical warm-up to peak throughput can take 10–30 seconds.</description></item><item><title>Compact Object Headers (JEP 519): 33% Less Heap Overhead</title><link>https://blog.devops-monk.com/tutorials/java25/compact-object-headers/</link><pubDate>Sun, 10 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java25/compact-object-headers/</guid><description>What Is an Object Header? Every single Java object — every String, every Integer, every record, every array — carries a header that the JVM uses for bookkeeping. Your code never sees this header; it lives alongside the object&amp;rsquo;s fields in memory.
Before Java 25, on a 64-bit JVM, the header occupied 96 to 128 bits (12–16 bytes):
┌─────────────────────────────────────────────────────────┐ │ Mark Word (64 bits) │ │ ─ identity hash code │ │ ─ lock state (biased lock / thin lock / fat lock) │ │ ─ GC age bits │ ├─────────────────────────────────────────────────────────┤ │ Class Pointer (32 bits compressed / 64 bits full) │ │ ─ pointer to the object&amp;#39;s class (Klass* in HotSpot) │ └─────────────────────────────────────────────────────────┘ With UseCompressedOops (default), the class pointer is compressed to 32 bits, giving a 96-bit (12-byte) header.</description></item><item><title>Compact Source Files &amp; Instance Main Methods (JEP 512): Java as a Scripting Language</title><link>https://blog.devops-monk.com/tutorials/java25/compact-source-files/</link><pubDate>Sun, 10 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java25/compact-source-files/</guid><description>The Ceremony Problem When a student writes their first Java program, they copy this boilerplate:
public class HelloWorld { public static void main(String[] args) { System.out.println(&amp;#34;Hello, World!&amp;#34;); } } Three layers of ceremony for one line of logic:
A public class whose name must match the filename A public static void main(String[] args) signature with specific keywords The actual code, buried inside two levels of braces This has been the #1 on-ramp friction point in Java for 30 years.</description></item><item><title>Flexible Constructor Bodies (JEP 513): Validate Before super()</title><link>https://blog.devops-monk.com/tutorials/java25/flexible-constructor-bodies/</link><pubDate>Sun, 10 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java25/flexible-constructor-bodies/</guid><description>The Old Rule That Caused So Much Pain From Java 1.0 through Java 24, a constructor that extended another class had one rigid rule:
super(...) or this(...) must be the first statement in the constructor body.
This was enforced by the compiler. Not because of a deep technical reason — but because the JVM specification had always required that the superclass be fully initialized before the subclass could do anything with the object.</description></item><item><title>Generational Shenandoah (JEP 516): Best GC for Low Latency</title><link>https://blog.devops-monk.com/tutorials/java25/generational-shenandoah/</link><pubDate>Sun, 10 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java25/generational-shenandoah/</guid><description>GC Background: Why Generations Matter The Generational Hypothesis is the foundation of most modern GC design:
Most objects die young.
In a typical Java application, the vast majority of objects are short-lived: request/response objects, DTOs, builder instances, stream pipeline intermediates. They are created, used briefly, and then immediately eligible for collection.
A GC that knows about this pattern can be far more efficient than one that treats all objects equally:</description></item><item><title>Java 25 Security: Key Derivation Function API &amp; PEM Encodings</title><link>https://blog.devops-monk.com/tutorials/java25/security-kdf-pem/</link><pubDate>Sun, 10 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java25/security-kdf-pem/</guid><description>Overview Java 25 ships two important security additions:
JEP 510 — Key Derivation Function (KDF) API — Final. A standard API for HKDF, PBKDF2, and other KDFs. JEP 470 — PEM Encodings of Cryptographic Objects — Preview. Read and write .pem files without third-party libraries. These fill two long-standing gaps: Java had the underlying crypto but no clean standard API for key derivation and PEM I/O.
Part 1: Key Derivation Function API (JEP 510) What Is Key Derivation?</description></item><item><title>Java 25: The LTS That Graduates Four Years of Previews</title><link>https://blog.devops-monk.com/tutorials/java25/java25-overview/</link><pubDate>Sun, 10 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java25/java25-overview/</guid><description>Why Java 25 Matters Java 25 was released on September 16, 2025 — exactly two years after Java 21, following the cadence Oracle committed to when it moved to a two-year LTS cycle.
But Java 25 is not just &amp;ldquo;Java 21 with more stuff&amp;rdquo;. It is the release where four years of preview work finally becomes production API. Features that Java developers have been testing since Java 21 — Scoped Values, Flexible Constructor Bodies, Module Import Declarations, Compact Source Files — all graduate to final status.</description></item><item><title>Module Import Declarations (JEP 511): One Import to Rule Them All</title><link>https://blog.devops-monk.com/tutorials/java25/module-import-declarations/</link><pubDate>Sun, 10 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java25/module-import-declarations/</guid><description>The Problem: Import Hell Every Java developer has experienced this. You open a file and before you see a single line of business logic, you wade through a wall of imports:
import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.</description></item><item><title>Primitive Types in Patterns (JEP 507): Pattern Match Every Type</title><link>https://blog.devops-monk.com/tutorials/java25/primitive-patterns/</link><pubDate>Sun, 10 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java25/primitive-patterns/</guid><description>Note: JEP 507 is a preview feature in Java 25 (3rd preview). Enable it with --enable-preview at compile and runtime. It is expected to finalize in Java 26 or 27.
The Asymmetry in Pattern Matching Java 21 gave us pattern matching for switch and instanceof. It is a great feature — but it had a glaring asymmetry: it only worked with reference types.
Object obj = 42; // Java 21: works fine if (obj instanceof Integer i) { System.</description></item><item><title>Scoped Values (JEP 506): The Final ThreadLocal Replacement</title><link>https://blog.devops-monk.com/tutorials/java25/scoped-values/</link><pubDate>Sun, 10 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java25/scoped-values/</guid><description>The ThreadLocal Problem ThreadLocal has been the standard way to pass contextual data through a call chain without polluting method signatures since Java 1.2. Every Java developer has seen it used for things like:
Web request context (user ID, correlation ID, tenant ID) Database transaction binding Security principal propagation Logging MDC (Mapped Diagnostic Context) Here is the classic pattern:
public class RequestContext { // ThreadLocal stores one value per thread private static final ThreadLocal&amp;lt;String&amp;gt; CURRENT_USER = new ThreadLocal&amp;lt;&amp;gt;(); public static void set(String userId) { CURRENT_USER.</description></item><item><title>Setting Up Java 25: Install, Tooling &amp; IDE Configuration</title><link>https://blog.devops-monk.com/tutorials/java25/java25-setup/</link><pubDate>Sun, 10 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java25/java25-setup/</guid><description>Installing Java 25 Option 1: SDKMAN! (Recommended) SDKMAN! is the easiest way to install and switch between Java versions on macOS and Linux.
# Install SDKMAN! if you don&amp;#39;t have it curl -s &amp;#34;https://get.sdkman.io&amp;#34; | bash source &amp;#34;$HOME/.sdkman/bin/sdkman-init.sh&amp;#34; # List available Java 25 distributions sdk list java | grep &amp;#34;25&amp;#34; # Install Eclipse Temurin 25 (open-source, most common) sdk install java 25-tem # Set as default globally sdk default java 25-tem # Verify java -version # openjdk version &amp;#34;25&amp;#34; 2025-09-16 Switch between versions per project:</description></item><item><title>Structured Concurrency (JEP 505): Preview 5 — What Changed?</title><link>https://blog.devops-monk.com/tutorials/java25/structured-concurrency/</link><pubDate>Sun, 10 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java25/structured-concurrency/</guid><description>Note: JEP 505 is a preview feature in Java 25 (5th preview). Enable with --enable-preview. The API has been stable for several rounds and is expected to finalize in Java 26.
The Problem with Unstructured Concurrency Classic Java concurrency with ExecutorService is unstructured: you submit tasks, and those tasks have no formal relationship with the code that submitted them. This causes three recurring problems:
Problem 1: Partial failure leaves orphaned tasks ExecutorService exec = Executors.</description></item><item><title>@MappedSuperclass: Sharing Fields Without Inheritance Tables</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/mapped-superclass/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/mapped-superclass/</guid><description>Introduction Almost every entity in a real application shares a few common fields: id, createdAt, updatedAt, and perhaps version. Writing these in every entity class is repetitive and error-prone. @MappedSuperclass lets you define them once in a base class that all entities extend — without creating a parent table or any inheritance mapping in the database.
What Is @MappedSuperclass? @MappedSuperclass marks a class whose field mappings are inherited by subclass entities.</description></item><item><title>@SendTo and @KafkaHandler: Chaining Consumers and Multi-Type Dispatch</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/sendto-kafkahandler/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/sendto-kafkahandler/</guid><description>@SendTo — Chaining Listeners @SendTo on a @KafkaListener method automatically sends the return value to another Kafka topic. This is how you build event pipelines without manually calling KafkaTemplate.send() in your listener.
flowchart LR T1["orders\n(OrderPlacedEvent)"] T2["orders-confirmed\n(OrderConfirmedEvent)"] T3["inventory-events\n(StockReservedEvent)"] T1 -->|"@KafkaListener\n@SendTo"| L1["confirmOrder()"] L1 --> T2 T2 -->|"@KafkaListener\n@SendTo"| L2["reserveStock()"] L2 --> T3 Basic @SendTo @KafkaListener(topics = &amp;#34;orders&amp;#34;, groupId = &amp;#34;confirmation-service&amp;#34;) @SendTo(&amp;#34;orders-confirmed&amp;#34;) public OrderConfirmedEvent onOrder(OrderPlacedEvent event) { // Return value is automatically sent to &amp;#34;orders-confirmed&amp;#34; return new OrderConfirmedEvent( event.</description></item><item><title>@Transactional in Depth: Propagation, Isolation, and Rollback</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/transactional-propagation-isolation/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/transactional-propagation-isolation/</guid><description>What @Transactional Does @Transactional tells Spring to wrap the annotated method in a database transaction. Spring opens the transaction before the method starts and commits (or rolls back) when it ends. The annotation works via an AOP proxy — understanding this proxy model is essential for avoiding the most common @Transactional bugs.
The AOP Proxy Model Spring creates a proxy around your bean. When code outside the bean calls a @Transactional method, the call goes through the proxy, which manages the transaction:</description></item><item><title>Actuator Security and Production Hardening</title><link>https://blog.devops-monk.com/tutorials/spring-security/actuator-security-production/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/actuator-security-production/</guid><description>The Actuator Security Problem Spring Boot Actuator exposes endpoints that reveal sensitive information about your application — environment variables, configuration properties, heap dumps, thread dumps, and the ability to shut down the application remotely.
An exposed /actuator/env endpoint can leak database passwords, API keys, and JWT signing secrets. An exposed /actuator/shutdown is a denial-of-service button. Actuator security is not optional in production.
Actuator Endpoints and Their Risk Level Endpoint Exposes Risk /actuator/health Application health Low — often public /actuator/info App metadata Low /actuator/metrics JVM/HTTP metrics Medium — business data /actuator/env All configuration properties (including secrets) Critical /actuator/configprops All @ConfigurationProperties values Critical /actuator/loggers Log levels (writable) High /actuator/heapdump Full JVM heap as a file Critical /actuator/threaddump Thread state Medium /actuator/mappings All URL mappings Medium — reveals API surface /actuator/shutdown Kills the JVM Critical /actuator/auditevents Security events High Step 1: Expose Only What You Need By default, only health is exposed over HTTP.</description></item><item><title>Advanced Streams: flatMap, Collectors, Grouping, and Partitioning</title><link>https://blog.devops-monk.com/tutorials/java8/streams-advanced/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java8/streams-advanced/</guid><description>flatMap in Depth The Collectors API covers perhaps 90% of real-world aggregation needs, but the moment you need something beyond grouping, partitioning, or joining, the custom Collector is there. Understanding flatMap and the full Collectors toolkit — including when to write your own — separates competent Java 8 developers from those who still reach for a for loop whenever a problem gets slightly unusual.
flatMap is the most powerful transformation in the Streams API.</description></item><item><title>Auditing: @CreatedDate, @LastModifiedBy, and Hibernate Envers</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/auditing/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/auditing/</guid><description>Why Auditing Matters Production systems must answer: &amp;ldquo;Who changed this? When? What did it look like before?&amp;rdquo; Without auditing infrastructure, you add created_at, updated_at, created_by, updated_by columns manually to every entity — dozens of lines of boilerplate repeated everywhere.
Spring Data JPA auditing fills these fields automatically. Hibernate Envers goes further: it captures every revision of every entity in separate audit tables.
Spring Data JPA Auditing Enable Auditing @Configuration @EnableJpaAuditing(auditorAwareRef = &amp;#34;auditorProvider&amp;#34;) public class JpaAuditingConfig { @Bean public AuditorAware&amp;lt;String&amp;gt; auditorProvider() { return () -&amp;gt; Optional.</description></item><item><title>AuthenticationManager, AuthenticationProvider, and UserDetailsService</title><link>https://blog.devops-monk.com/tutorials/spring-security/authentication-manager-provider/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/authentication-manager-provider/</guid><description>The Authentication Delegation Chain Spring Security separates requesting authentication from performing authentication. A filter receives a username and password — but it does not check them itself. It passes the request to AuthenticationManager, which delegates to one or more AuthenticationProvider instances:
flowchart LR F[Authentication Filter\nForm Login / Basic / JWT] -->|UsernamePasswordToken| AM[AuthenticationManager\nProviderManager] AM -->|try each provider| AP1[DaoAuthenticationProvider\nfor DB users] AM -->|try each provider| AP2[LdapAuthenticationProvider\nfor LDAP users] AM -->|try each provider| AP3[Custom Provider\nfor API key] AP1 -->|if supported| UDS[UserDetailsService\nloadUserByUsername] UDS --> DB[(Database)] AP1 -->|verify password| PE[PasswordEncoder] AP1 -->|success| Token[Authenticated Token] Token --> F This chain is the core of Spring Security&amp;rsquo;s extensibility — you can plug in any number of authentication mechanisms without touching the filter or the rest of the framework.</description></item><item><title>Avro Serialization with Confluent Schema Registry</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/avro-schema-registry/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/avro-schema-registry/</guid><description>Why Avro and Schema Registry? JSON has no schema enforcement — a producer can change a field name and silently break every consumer. Avro + Schema Registry solves this:
Avro gives you a compact binary format with a schema definition Schema Registry stores and versions schemas, enforces compatibility rules, and prevents breaking changes from reaching consumers flowchart LR subgraph Producer["Order Service"] E["OrderPlacedEvent"] -->|"KafkaAvroSerializer"| SR["Schema Registry\n(register/lookup schema)"] SR --> Bytes["[schema_id (4 bytes)] + [avro payload]"</description></item><item><title>Basic Entity Mapping: @Entity, @Table, @Id, @Column</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/basic-entity-mapping/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/basic-entity-mapping/</guid><description>Introduction Entity mapping is the process of telling Hibernate how your Java class corresponds to a database table, and how each field maps to a column. JPA provides a rich set of annotations for this — from the minimal @Entity and @Id to the detailed @Column with constraints.
This article covers every annotation and attribute you need to map entities precisely and confidently.
@Entity @Entity marks a class as a JPA entity — a Java object that maps to a database table.</description></item><item><title>Best Practices Reference: The Complete Spring Batch Checklist</title><link>https://blog.devops-monk.com/tutorials/spring-batch/spring-batch-best-practices/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/spring-batch-best-practices/</guid><description>Introduction This is the final article in the Spring Batch Tutorial series. It consolidates every hard-won lesson into actionable checklists — use it as a review before deploying a new batch job to production, or as a reference when debugging an existing one.
Part 1: Job Design Always design for restartability Every step that writes to a database uses idempotent SQL (ON DUPLICATE KEY UPDATE or WHERE NOT EXISTS) Every ItemReader has a unique .</description></item><item><title>Cascade Types and Orphan Removal: Managing Lifecycle Propagation</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/cascade-types-orphan-removal/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/cascade-types-orphan-removal/</guid><description>Introduction Cascade types control which persistence operations (save, merge, delete, refresh) propagate from a parent entity to its associated children. orphanRemoval controls what happens when a child is removed from the parent&amp;rsquo;s collection. Getting cascade wrong is one of the most common causes of unexpected deletes and data integrity issues in JPA applications.
The Six Cascade Types cascade = CascadeType.PERSIST // propagate persist (save new entity) cascade = CascadeType.MERGE // propagate merge (re-attach detached entity) cascade = CascadeType.</description></item><item><title>Collection Factory Methods (JEP 269): Immutable List, Set, and Map</title><link>https://blog.devops-monk.com/tutorials/java11/collection-factory-methods/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java11/collection-factory-methods/</guid><description>Why Collection Factory Methods? Before Java 9, creating a small immutable collection was tedious:
// Java 8 — three lines, two classes, mutable intermediate List&amp;lt;String&amp;gt; names = Collections.unmodifiableList( Arrays.asList(&amp;#34;Alice&amp;#34;, &amp;#34;Bob&amp;#34;, &amp;#34;Charlie&amp;#34;) ); // Java 8 — even worse for Map Map&amp;lt;String, Integer&amp;gt; scores = new HashMap&amp;lt;&amp;gt;(); scores.put(&amp;#34;Alice&amp;#34;, 90); scores.put(&amp;#34;Bob&amp;#34;, 85); Map&amp;lt;String, Integer&amp;gt; immutableScores = Collections.unmodifiableMap(scores); JEP 269 (Java 9) introduced static factory methods that create truly immutable collections with a single expression:</description></item><item><title>Collections and Map Enhancements: forEach, merge, compute, replaceAll</title><link>https://blog.devops-monk.com/tutorials/java8/collections-maps/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java8/collections-maps/</guid><description>Overview Java 8 didn&amp;rsquo;t just add Streams — it also enhanced the existing Iterable, Collection, List, and Map interfaces with default methods that cover the most common imperative patterns. Understanding these methods lets you write cleaner code even without streams.
The three methods worth memorising immediately are computeIfAbsent (the most useful new Map method), merge (for accumulation patterns), and removeIf (eliminating the iterator-based removal pattern). Together they eliminate the most common sources of verbose boilerplate in collection-heavy code.</description></item><item><title>CompletableFuture: Async Pipelines and Non-Blocking Composition</title><link>https://blog.devops-monk.com/tutorials/java8/completablefuture/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java8/completablefuture/</guid><description>The Problem with Future&amp;lt;T&amp;gt; Fetching a product page on an e-commerce site requires at least three data sources: product details, live pricing, and inventory availability. Done sequentially, a 200 ms call to each service adds up to 600 ms. Done in parallel with a properly composed CompletableFuture pipeline, the wall-clock time drops to roughly the slowest of the three — around 200 ms. This article shows exactly how to build that pipeline and everything else you need to write robust async code in Java 8.</description></item><item><title>Consumer @Bean Configuration: ConcurrentKafkaListenerContainerFactory</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/consumer-bean-configuration/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/consumer-bean-configuration/</guid><description>Why @Bean Configuration? application.properties covers the common cases. But real applications need multiple listener factories — one for orders with manual acknowledgment and concurrency 3, another for analytics events with batch listening and different deserializers. @Bean configuration gives you a factory per use case, full IDE support, and the ability to wire in custom components like error handlers and interceptors.
The Factory Relationship flowchart TD CF["ConsumerFactory\n(connection + deserialization config)"] LCF["ConcurrentKafkaListenerContainerFactory\n(container behaviour config)"</description></item><item><title>Consumer Groups, Offsets, and the __consumer_offsets Topic</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/consumer-groups-offsets/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/consumer-groups-offsets/</guid><description>What Is a Consumer Group? A consumer group is a set of consumer instances that jointly consume a topic. Kafka assigns each partition to exactly one consumer within the group at a time. This is what enables parallel processing: multiple consumers in the same group read different partitions simultaneously.
flowchart LR subgraph Topic["Topic: orders — 4 partitions"] P0["Partition 0"] P1["Partition 1"] P2["Partition 2"] P3["Partition 3"] end subgraph CG["Consumer Group: inventory-service"] C1["</description></item><item><title>Consumer Groups: Parallel Processing and Partition Assignment Strategies</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/consumer-groups/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/consumer-groups/</guid><description>Consumer Group Fundamentals A consumer group is how Kafka distributes work across multiple consumers. Each partition in a topic is assigned to exactly one consumer instance in the group at any given time.
flowchart TB subgraph Topic["Topic: orders — 6 partitions"] P0["P0"] P1["P1"] P2["P2"] P3["P3"] P4["P4"] P5["P5"] end subgraph CG1["Group: inventory-service (3 instances)"] I1["Instance 1\nP0, P1"] I2["Instance 2\nP2, P3"] I3["Instance 3\nP4, P5"] end subgraph CG2["Group: notification-service (1 instance)"] N1["Instance 1\nP0,P1,P2,P3,P4,P5"] end P0 &amp; P1 --> I1 P2 &amp; P3 --> I2 P4 &amp; P5 --> I3 P0 &amp; P1 &amp; P2 &amp; P3 &amp; P4 &amp; P5 --> N1 Both groups receive all events — they are independent.</description></item><item><title>Context-Specific Deserialization Filters (JEP 415): Securing Java Deserialization</title><link>https://blog.devops-monk.com/tutorials/java17/deserialization-filters/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java17/deserialization-filters/</guid><description>Finalized in Java 17 (JEP 415). Extends JEP 290 (Java 9), which introduced the basic deserialization filter API.
Why Deserialization Is Dangerous Java object deserialization (ObjectInputStream.readObject()) is one of the most exploited attack surfaces in Java. When a Java application deserializes untrusted bytes, the JVM instantiates arbitrary classes and calls their methods as a side effect — before your application code even sees the result.
Attackers craft gadget chains: sequences of serializable classes in common libraries (Apache Commons Collections, Spring Framework, etc.</description></item><item><title>CORS: Cross-Origin Requests and Preflight Configuration</title><link>https://blog.devops-monk.com/tutorials/spring-security/cors-configuration/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/cors-configuration/</guid><description>What Is CORS? The Same-Origin Policy (SOP) is a browser security rule: JavaScript on app.example.com cannot read responses from api.example.com — different subdomain, different origin. Without this, any website could use a visitor&amp;rsquo;s authenticated session to silently call other sites on their behalf.
CORS (Cross-Origin Resource Sharing) is the mechanism by which a server explicitly relaxes SOP for trusted origins. When a browser sees a cross-origin request, it checks whether the server has granted permission before allowing JavaScript to read the response.</description></item><item><title>CSRF Protection: How It Works and When to Disable It</title><link>https://blog.devops-monk.com/tutorials/spring-security/csrf-protection/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/csrf-protection/</guid><description>What Is a CSRF Attack? Cross-Site Request Forgery (CSRF) tricks an authenticated user&amp;rsquo;s browser into making an unintended request to your application.
The attack:
Alice is logged into bank.com — her browser holds a valid session cookie Alice visits evil.com evil.com contains &amp;lt;img src=&amp;quot;https://bank.com/transfer?to=attacker&amp;amp;amount=5000&amp;quot;&amp;gt; Alice&amp;rsquo;s browser fires the request, automatically attaching her bank.com session cookie bank.com receives an authenticated request that Alice never intended to make The attack works because browsers automatically send cookies with cross-origin requests.</description></item><item><title>Custom Queries with @Query: JPQL and Native SQL</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/custom-queries-jpql-native-sql/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/custom-queries-jpql-native-sql/</guid><description>Why @Query Exists Derived query methods (Article 17) are great for simple conditions, but they break down fast. A method like findByOrderStatusAndCustomerCityAndPriceBetweenOrderByCreatedAtDesc is unreadable and still cannot express a JOIN or aggregation.
@Query lets you write the exact JPQL or SQL you need, attached directly to a repository method — keeping your repository clean while giving you full query control.
JPQL vs Native SQL JPQL Native SQL Language Entity/field names Table/column names Database portable Yes No JOIN FETCH Yes No Window functions No Yes Full-text search No Yes Complex analytics Limited Full power Start with JPQL.</description></item><item><title>Custom Serializers and Deserializers</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/custom-serializers/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/custom-serializers/</guid><description>When to Write a Custom Serializer Spring Kafka ships JSON and Avro support. You need a custom serializer when:
Your team uses Protobuf or MessagePack and wants native support You need a compact binary format for high-throughput topics (pricing ticks, sensor readings) You&amp;rsquo;re integrating with a legacy system that publishes a fixed binary protocol You want deterministic serialization for event deduplication or content-addressed storage The Serializer and Deserializer Interfaces // org.</description></item><item><title>Custom Type Conversions: AttributeConverter and Enums</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/custom-type-conversions/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/custom-type-conversions/</guid><description>Introduction JPA handles most Java types automatically — String, Integer, LocalDate, BigDecimal. But what about a Java enum? A List&amp;lt;String&amp;gt; stored as JSON? A custom Money type? JPA&amp;rsquo;s AttributeConverter and @Enumerated let you control exactly how any Java type maps to a database column.
Mapping Enums with @Enumerated Java enums are common in domain models. JPA maps them with @Enumerated:
public enum OrderStatus { PENDING, CONFIRMED, SHIPPED, DELIVERED, CANCELLED } public enum ProductStatus { ACTIVE, INACTIVE, DISCONTINUED } @Column(length = 20, nullable = false) @Enumerated(EnumType.</description></item><item><title>Date and Time API (JSR-310): LocalDate, ZonedDateTime, Duration, Period</title><link>https://blog.devops-monk.com/tutorials/java8/date-time-api/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java8/date-time-api/</guid><description>Why java.util.Date Had to Go If you have ever tracked down a bug caused by a SimpleDateFormat shared across threads, or spent an afternoon understanding why a Calendar.get(Calendar.MONTH) returns 0 for January, you will appreciate why JSR-310 was one of the most eagerly anticipated Java 8 features. The new java.time API is not a patch on the old API — it is a complete redesign that makes an entire class of date and time bugs impossible by construction.</description></item><item><title>Dead Letter Topics: Routing Failed Messages with DeadLetterPublishingRecoverer</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/dead-letter-topics/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/dead-letter-topics/</guid><description>What Is a Dead Letter Topic? When a record fails processing and retries are exhausted, you have two options: skip it (losing the data) or park it somewhere for inspection and reprocessing. A dead-letter topic (DLT) is that parking lot — a Kafka topic that holds records that could not be processed, enriched with error metadata headers.
flowchart LR subgraph Main["orders topic"] R1["record: offset 42\n(bad data)"] end subgraph DLT["orders.DLT topic"] R2["</description></item><item><title>Default and Static Interface Methods: Backwards-Compatible API Evolution</title><link>https://blog.devops-monk.com/tutorials/java8/default-static-methods/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java8/default-static-methods/</guid><description>The Problem: Evolving Interfaces Without Breaking Implementations Default methods solved a problem that had blocked the Java Collections team from improving the core APIs for years: how do you add methods to an interface without breaking every existing implementation? The answer Java 8 gave — concrete implementations on the interface itself — has wider implications than just Collection.stream(). It is the mechanism behind the fluent Comparator API, the Predicate composition methods, and the design pattern of composable interface contracts.</description></item><item><title>Derived Query Methods: Finding Data Without Writing SQL</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/derived-query-methods/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/derived-query-methods/</guid><description>Introduction Spring Data JPA can generate JPQL queries directly from method names. You declare a method like findByStatusAndPriceGreaterThan, and Spring Data parses the name, derives the query, and generates the implementation. No SQL, no @Query, no boilerplate.
How Derived Query Parsing Works Spring Data JPA splits method names into a subject and a predicate:
findBy Email And Status ↑ ↑ ↑ ↑ subject property operator property The subject determines the operation type (find, count, exists, delete).</description></item><item><title>Dirty Checking, Flush Modes, and the First-Level Cache</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/dirty-checking-flush-modes/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/dirty-checking-flush-modes/</guid><description>The Persistence Context Revisited Article 3 introduced the persistence context — the in-memory cache of managed entities. This article goes deeper: how does the persistence context detect changes? When does it flush them to the database? And how can you tune this behaviour?
Dirty Checking When you modify a managed entity, you don&amp;rsquo;t call save() or update(). You simply change the field:
@Transactional public void giveDiscount(Long productId, BigDecimal discount) { Product product = productRepository.</description></item><item><title>Domain Object Security: Access Control Lists (ACLs)</title><link>https://blog.devops-monk.com/tutorials/spring-security/domain-object-acl/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/domain-object-acl/</guid><description>What ACLs Solve Role-based access control answers: &amp;ldquo;Can this user perform this action?&amp;rdquo; ACLs answer: &amp;ldquo;Can this user perform this action on this specific object?&amp;rdquo;
Consider a document management system:
Alice owns Document #42 — she can read and edit it Bob is a reviewer on Document #42 — he can read it but not edit Carol has no permission on Document #42 — she gets a 403 This cannot be expressed with roles alone.</description></item><item><title>Dynamic Listener Containers and Programmatic Topic Registration</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/dynamic-listeners/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/dynamic-listeners/</guid><description>Why Dynamic Listeners? @KafkaListener is declared at compile time. Some scenarios require listeners created at runtime:
Multi-tenant SaaS — each tenant onboards to their own topic; you can&amp;rsquo;t redeploy to add @KafkaListener for each new tenant Feature flags — enable or disable a listener without a deployment Plugin systems — modules register their own topic subscriptions when loaded Admin APIs — operators subscribe to new topics via a REST endpoint ConcurrentMessageListenerContainer The core building block is ConcurrentMessageListenerContainer — the same class @KafkaListener uses internally, but constructed and started manually:</description></item><item><title>Embedded Types and Value Objects: @Embeddable and @Embedded</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/embedded-types-value-objects/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/embedded-types-value-objects/</guid><description>Introduction Not every concept in your domain deserves its own table. An Address — street, city, state, postal code — is a value object: it has no identity of its own, it belongs to an entity. JPA&amp;rsquo;s @Embeddable lets you model this as a separate Java class while storing it in the owning entity&amp;rsquo;s table.
What Is an Embeddable? An @Embeddable class is a Java class whose fields are mapped to columns in the owning entity&amp;rsquo;s table — not a separate table.</description></item><item><title>Enhanced Pseudo-Random Number Generators (JEP 356): A Modern PRNG API</title><link>https://blog.devops-monk.com/tutorials/java17/enhanced-prng/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java17/enhanced-prng/</guid><description>Finalized in Java 17 (JEP 356).
The Problem with Java&amp;rsquo;s Old PRNG API Java&amp;rsquo;s random number generation before Java 17 had several problems:
1. java.util.Random has poor statistical properties. The default PRNG algorithm is a 48-bit linear congruential generator (LCG). It fails many statistical tests (spectral test, birthday test). Its 2^48 period is short — exhaustible in seconds on modern hardware.
2. No common interface. java.util.Random, java.util.concurrent.ThreadLocalRandom, java.util.SplittableRandom, and java.</description></item><item><title>Entity Graphs and Batch Loading: Precision Fetching</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/entity-graphs-batch-loading/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/entity-graphs-batch-loading/</guid><description>The Problem @EntityGraph Solves JOIN FETCH in @Query solves N+1 but creates inflexibility: the fetch plan is baked into the query. Two different use cases — an order detail page (needs items + customer) and an order list page (needs only customer) — require two different queries with different JOIN FETCHes.
@EntityGraph separates the fetch plan from the query. You define what to load at the call site, and Spring Data JPA generates the appropriate JOIN.</description></item><item><title>Entity Lifecycle States: Transient, Managed, Detached, Removed</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/entity-lifecycle-states/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/entity-lifecycle-states/</guid><description>Introduction Every JPA entity is always in one of four states. The state determines whether Hibernate is tracking the entity, whether changes are detected automatically, and what operations are valid. Understanding these states explains a large class of JPA bugs — especially the dreaded LazyInitializationException and detached entity errors.
The Four States new Customer() │ │ persist / save() ▼ TRANSIENT ──────────────────────► MANAGED (not tracked) (tracked by persistence context) │ │ evict / clear / close ▼ DETACHED (no longer tracked) │ │ merge() ▼ MANAGED (re-attached) MANAGED ─── remove / delete() ──► REMOVED (delete scheduled, still in context) 1.</description></item><item><title>Error Handling Basics: DefaultErrorHandler and CommonErrorHandler</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/error-handling-basics/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/error-handling-basics/</guid><description>What Happens When a Listener Throws? Without an error handler, an uncaught exception from @KafkaListener causes the container to log the error and retry the same record on the next poll — indefinitely. One bad record can block an entire partition forever.
DefaultErrorHandler fixes this: it retries a configurable number of times with backoff, then calls a ConsumerRecordRecoverer (e.g. send to a dead-letter topic) and moves on.
DefaultErrorHandler — The Modern API Spring Kafka 2.</description></item><item><title>Fetch Types: EAGER vs LAZY Loading</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/fetch-types-eager-vs-lazy/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/fetch-types-eager-vs-lazy/</guid><description>Introduction Every relationship in JPA has a fetch type: either EAGER (load immediately with the parent) or LAZY (load only when accessed). The defaults are counterintuitive, and getting fetch types wrong is one of the top causes of performance problems and LazyInitializationException in JPA applications.
Default Fetch Types Annotation Default fetch type Performance risk @ManyToOne EAGER Loads related entity on every query — can be expensive @OneToOne EAGER Same — loads profile/address on every customer load @OneToMany LAZY Correct default — collections are only loaded when needed @ManyToMany LAZY Correct default The defaults for @ManyToOne and @OneToOne are EAGER — a poor choice that the JPA spec made for historical reasons.</description></item><item><title>Files and IO Enhancements (Java 11)</title><link>https://blog.devops-monk.com/tutorials/java11/files-io-api/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java11/files-io-api/</guid><description>The Problem: File IO Boilerplate Reading a file&amp;rsquo;s content in Java 8 required several lines of boilerplate, even for simple tasks:
// Java 8 — read entire file as String String content; try (var reader = new BufferedReader(new FileReader(&amp;#34;config.txt&amp;#34;))) { content = reader.lines().collect(Collectors.joining(&amp;#34;\n&amp;#34;)); } // Java 8 — write a String to a file try (var writer = new BufferedWriter(new FileWriter(&amp;#34;output.txt&amp;#34;))) { writer.write(content); } Java 11 reduced this to one-liners.</description></item><item><title>Filtering Messages with RecordFilterStrategy</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/message-filtering/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/message-filtering/</guid><description>Why Filter at the Container Level? Multiple consumers can share a topic. The inventory service only cares about PLACED orders; the analytics service wants everything. Rather than putting if (event.getStatus() != PLACED) return; at the top of every listener, Spring Kafka lets you filter records before they reach your method — keeping business logic clean and making the filter reusable across listeners.
How RecordFilterStrategy Works flowchart LR Broker -->|"poll() → [r1, r2, r3, r4]"</description></item><item><title>Flight Recorder and JVM Monitoring (JEP 328)</title><link>https://blog.devops-monk.com/tutorials/java11/flight-recorder/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java11/flight-recorder/</guid><description>What Is Java Flight Recorder? Java Flight Recorder (JFR) is a low-overhead, always-on profiling and diagnostics framework built into the JVM. It was a commercial feature of Oracle JDK until JEP 328 (Java 11) open-sourced it as part of OpenJDK.
JFR collects data about JVM internals and application behaviour — method profiling, allocation, GC pauses, thread states, I/O, lock contention — with a typical overhead of 1–2% in production.
This makes it fundamentally different from traditional profilers (JProfiler, YourKit): those profilers cause 10–50% overhead, making them impractical for production.</description></item><item><title>Foreign Function &amp; Memory API (JEP 412): First Look at Project Panama</title><link>https://blog.devops-monk.com/tutorials/java17/foreign-function-memory/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java17/foreign-function-memory/</guid><description>Incubator Feature in Java 17 (JEP 412 — First Incubator). Package: jdk.incubator.foreign. Requires --add-modules jdk.incubator.foreign at compile and runtime. This API evolved significantly across Java 18 (JEP 419), 19 (JEP 424), 20 (JEP 434), and was finalized in Java 22 (JEP 454). See the Java 21 article series for the final API. This article covers the Java 17 incubator version and the Project Panama vision.
Why Replace JNI? Java Native Interface (JNI) has been the way to call native code from Java since Java 1.</description></item><item><title>Foreign Function &amp; Memory API (JEP 442): Calling Native Code Without JNI</title><link>https://blog.devops-monk.com/tutorials/java21/foreign-function-memory/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java21/foreign-function-memory/</guid><description>Preview Feature in Java 21 — Finalized in Java 22 (JEP 454). The API shown here is the Java 21 preview version; it is nearly identical to the final API.
Why Replace JNI? Java Native Interface (JNI) has been the standard way to call native code since Java 1.1. It works but is notoriously painful:
Requires writing C wrapper code for every native call Native method signatures must exactly match Java declarations (or you get silent crashes) Memory management is manual — leak native memory once and you have a slow memory leak JNI calls disable JIT optimizations around the call site Debugging native crashes through JNI is extremely difficult The Foreign Function &amp;amp; Memory (FFM) API replaces all of this: call native functions directly from Java, manage native memory safely with automatic lifetime management, and do it without writing a single line of C.</description></item><item><title>Form Login Authentication: From Request to Session</title><link>https://blog.devops-monk.com/tutorials/spring-security/form-login/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/form-login/</guid><description>Form Login: The Classic Web Authentication Flow Form login is the traditional authentication mechanism for server-rendered web applications. The user fills in a username and password form, the server verifies credentials, creates a session, and returns a session cookie. All subsequent requests carry that cookie.
sequenceDiagram participant Browser participant SSF as Spring Security Filters participant AM as AuthenticationManager participant SS as HTTP Session Store participant Controller Browser->>SSF: GET /dashboard (unauthenticated) SSF->>SS: Save original request (RequestCache) SSF-->>Browser: 302 Redirect to /login Browser->>SSF: GET /login SSF-->>Browser: 200 HTML login page Browser->>SSF: POST /login {username, password} SSF->>AM: authenticate(UsernamePasswordToken) AM-->>SSF: Authentication (success) SSF->>SS: Store SecurityContext in session SSF-->>Browser: 302 Redirect to /dashboard\nSet-Cookie: JSESSIONID=abc123 Browser->>SSF: GET /dashboard (JSESSIONID cookie) SSF->>SS: Load SecurityContext from session SSF->>Controller: Request with authenticated user Controller-->>Browser: 200 Dashboard HTML Minimal Configuration @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .</description></item><item><title>Functional Interfaces: Predicate, Function, Supplier, Consumer, and More</title><link>https://blog.devops-monk.com/tutorials/java8/functional-interfaces/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java8/functional-interfaces/</guid><description>What Is a Functional Interface? Functional interfaces are the type system bridge that makes lambda expressions work in Java 8. Without them, the compiler would have no way to know what type to assign to a lambda. Once you understand how the 43 built-in interfaces in java.util.function are organised — and when to write your own — you will find that the same patterns (compose, chain, validate, transform) appear everywhere in a Java 8 codebase.</description></item><item><title>Garbage Collection: G1GC, ZGC, Epsilon, and AppCDS</title><link>https://blog.devops-monk.com/tutorials/java11/garbage-collection/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java11/garbage-collection/</guid><description>GC Changes Across Java 9–11 Release Change JEP Java 9 G1GC becomes the default GC JEP 248 Java 9 Unified GC logging (-Xlog:gc*) JEP 271 Java 10 Parallel Full GC for G1 JEP 307 Java 10 Application Class-Data Sharing (AppCDS) JEP 310 Java 11 Epsilon: No-Op GC JEP 318 Java 11 ZGC: Scalable Low-Latency GC (experimental) JEP 333 G1GC as Default (JEP 248, Java 9) G1 (Garbage-First) replaced Parallel GC as the default on systems with ≥2 CPUs and ≥2 GB heap.</description></item><item><title>Generational ZGC (JEP 439): Sub-Millisecond Pauses at Any Scale</title><link>https://blog.devops-monk.com/tutorials/java21/generational-zgc/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java21/generational-zgc/</guid><description>Why GC Still Matters in 2024 Even with better APIs and faster hardware, garbage collection pauses remain one of the top causes of latency spikes in Java services. A 200ms GC pause in a payment processing service is a customer-visible failure. A 50ms pause in a trading system is a missed execution window.
Java 21 delivers Generational ZGC — the most capable GC Java has ever shipped — as a production-ready, non-preview feature.</description></item><item><title>Handling Deserialization Errors Gracefully</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/deserialization-errors/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/deserialization-errors/</guid><description>The Problem: Poison Pills at Deserialization Time A malformed byte sequence — truncated JSON, wrong Avro schema, corrupt payload — throws an exception during deserialization, before the listener method is called. Without special handling, this record blocks the partition indefinitely: the consumer fetches it, fails to deserialize, and fetches it again on the next poll.
sequenceDiagram participant Broker participant Container participant Deserializer loop forever without ErrorHandlingDeserializer Container->>Broker: poll() Broker-->>Container: [good-record, CORRUPT-RECORD, good-record] Container->>Deserializer: deserialize(CORRUPT-RECORD) Deserializer-->>Container: JsonProcessingException 💥 Note over Container: Partition blocked — same record on every poll end ErrorHandlingDeserializer solves this by catching the deserialization exception and wrapping it in a DeserializationException that the listener container can route to the error handler.</description></item><item><title>How Spring Security Works: The Big Picture</title><link>https://blog.devops-monk.com/tutorials/spring-security/how-spring-security-works/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/how-spring-security-works/</guid><description>Why Spring Security Is Hard to Learn Spring Security is not complex because the concepts are complex. Authentication and authorization are straightforward ideas. Spring Security is hard because it hides its machinery: a request comes in, something happens, an endpoint is secured or not — and without knowing the internal architecture, the behaviour feels magical and the configuration feels arbitrary.
This article removes the magic. By the end, you will have the mental model needed to understand every other concept in this series.</description></item><item><title>HTTP Authorization: Securing Endpoints with requestMatchers</title><link>https://blog.devops-monk.com/tutorials/spring-security/http-authorization/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/http-authorization/</guid><description>HTTP Authorization: The Last Line of Defense AuthorizationFilter is the last filter in the Spring Security chain. After all authentication filters have run (and either set an Authentication or left it as anonymous), AuthorizationFilter makes the final access decision: allow or deny.
The rules you write in authorizeHttpRequests() are evaluated in order, first match wins.
flowchart TD Request[HTTP Request] --> Auth[AuthorizationFilter] Auth --> Rules{Evaluate rules\nin order} Rules -->|Rule 1 matches| Decision1{Allowed?</description></item><item><title>HTTP Basic Authentication and Stateless APIs</title><link>https://blog.devops-monk.com/tutorials/spring-security/http-basic-authentication/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/http-basic-authentication/</guid><description>What Is HTTP Basic Authentication? HTTP Basic is the simplest authentication scheme defined in the HTTP specification (RFC 7617). The client encodes username:password in Base64 and sends it in the Authorization header with every request:
Authorization: Basic YWxpY2U6c2VjcmV0 ↑ Base64(&amp;#34;alice:secret&amp;#34;) Important: Base64 is encoding, not encryption. Anyone who intercepts the request can decode the credentials instantly. HTTP Basic must only be used over HTTPS.
sequenceDiagram participant Client as API Client participant SSF as Spring Security Filters participant AM as AuthenticationManager Client->>SSF: GET /api/data\n(no Authorization header) SSF-->>Client: 401 Unauthorized\nWWW-Authenticate: Basic realm="</description></item><item><title>HTTP Client API (JEP 321): HTTP/2, Async, and Authentication</title><link>https://blog.devops-monk.com/tutorials/java11/http-client-api/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java11/http-client-api/</guid><description>Why a New HTTP Client? HttpURLConnection — Java&amp;rsquo;s HTTP API since Java 1.1 — has deep design problems:
Mutable shared state makes it error-prone in multithreaded code No built-in HTTP/2 support No built-in async; non-blocking requires manual thread management Clunky API: setDoOutput(true), getOutputStream(), connect() in sequence No support for reactive streams JEP 321 (Java 11) standardised the HTTP Client API that was incubating since Java 9. The new API lives in java.</description></item><item><title>Idempotent Producers: Eliminating Duplicate Messages</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/idempotent-producer/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/idempotent-producer/</guid><description>The Duplicate Problem With acks=all and retries enabled, a produce request might be acknowledged by the broker, but the acknowledgment is lost in the network before reaching the producer. The producer, seeing no response, retries — sending the same record again. The broker writes it a second time. The consumer sees a duplicate.
sequenceDiagram participant Producer participant Leader as Broker (Leader) Producer->>Leader: ProduceRequest: OrderPlaced (orderId=1001) Leader->>Leader: Write record at offset 42 ✓ Leader--xProducer: ProduceResponse LOST (network failure) Note over Producer: No ack received — retrying Producer->>Leader: ProduceRequest: OrderPlaced (orderId=1001) [RETRY] Leader->>Leader: Write record at offset 43 ✓ (DUPLICATE!</description></item><item><title>Inheritance Strategies: SINGLE_TABLE, JOINED, TABLE_PER_CLASS</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/inheritance-strategies/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/inheritance-strategies/</guid><description>Introduction Object-oriented code uses inheritance to share behaviour. Relational databases have no concept of inheritance. JPA bridges this gap with three strategies for mapping a class hierarchy to tables. Understanding when each is appropriate prevents schema headaches and performance problems.
The Domain Example An e-commerce system has different types of discount:
Discount (abstract) ├── PercentageDiscount (e.g., 10% off) └── FixedAmountDiscount (e.g., $5 off) All discounts share: id, name, validFrom, validUntil. PercentageDiscount adds: percentage (e.</description></item><item><title>Introduction to JPA, Hibernate, and Spring Data JPA</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/introduction-jpa-hibernate-spring-data/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/introduction-jpa-hibernate-spring-data/</guid><description>Introduction Every Spring Boot application that touches a relational database eventually encounters three terms used almost interchangeably: JPA, Hibernate, and Spring Data JPA. They are related but distinct, and understanding the difference is essential before writing a single line of mapping code.
This article explains what each one is, how they fit together, and why this stack is the dominant approach to Java database access.
The Problem: Object-Relational Impedance Mismatch Java applications work with objects: classes, inheritance, collections, references.</description></item><item><title>Java 11 Overview: The Road from Java 8 Through Java 9, 10, to LTS</title><link>https://blog.devops-monk.com/tutorials/java11/java11-overview/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java11/java11-overview/</guid><description>Why Java 11 Matters Java 8 was released in March 2014. It dominated enterprise Java for nearly a decade, but it misses a decade&amp;rsquo;s worth of language improvements, API modernisation, JVM advances, and security hardening. Java 11 (September 2018) is the first Long-Term Support release after Java 8, and it packages three releases of evolution into a single supported baseline.
For most teams the question is not whether to upgrade, but how.</description></item><item><title>Java 11 Production Checklist and Performance Best Practices</title><link>https://blog.devops-monk.com/tutorials/java11/production-best-practices/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java11/production-best-practices/</guid><description>Production Readiness Checklist [ ] JDK distribution chosen and version pinned [ ] Heap and Metaspace sized correctly [ ] GC selected and tuned for your workload [ ] Container-aware JVM flags set [ ] AppCDS archive built for faster startup [ ] JFR always-on recording configured [ ] GC logging enabled with rotation [ ] Security-related algorithms locked down [ ] Thread and connection pool sizes verified [ ] JVM exit flags prevent silent crashes Baseline JVM Flags for Java 11 Start with these flags and tune from here:</description></item><item><title>Java 17 Production Checklist and Performance Best Practices</title><link>https://blog.devops-monk.com/tutorials/java17/production-best-practices/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java17/production-best-practices/</guid><description>Production Baseline JVM Flags Start every Java 17 production deployment with this baseline:
java \ # GC — choose one (see GC section) -XX:+UseG1GC \ -XX:MaxGCPauseMillis=200 \ \ # Heap sizing -Xms4g -Xmx4g \ \ # GC logging — essential for diagnosis -Xlog:gc*:file=/var/log/app/gc.log:time,uptime,level,tags:filecount=5,filesize=20m \ \ # OOM diagnostics -XX:+HeapDumpOnOutOfMemoryError \ -XX:HeapDumpPath=/var/log/app/heap-dump.hprof \ -XX:+ExitOnOutOfMemoryError \ \ # Metaspace -XX:MaxMetaspaceSize=512m \ \ # Code cache -XX:ReservedCodeCacheSize=512m \ \ # JFR — always-on profiling -XX:StartFlightRecording=duration=0,filename=/var/log/app/profile.</description></item><item><title>Java 17: The LTS That Delivers — What Changed from Java 11</title><link>https://blog.devops-monk.com/tutorials/java17/java17-overview/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java17/java17-overview/</guid><description>Java 17: The Landmark LTS Java 17 was released on September 14, 2021 as a Long-Term Support (LTS) release. It is the successor to Java 11 (released September 2018) as the recommended production baseline for enterprise and cloud Java deployments.
Between Java 11 and Java 17, six non-LTS releases (Java 12 through 16) delivered a continuous stream of language improvements on a six-month cadence. Java 17 is where five of the most important new features — Records, Sealed Classes, Pattern Matching for instanceof, Text Blocks, and Switch Expressions — all reached their final, production-ready status simultaneously.</description></item><item><title>Java 21 Production Checklist and Performance Best Practices</title><link>https://blog.devops-monk.com/tutorials/java21/production-best-practices/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java21/production-best-practices/</guid><description>The Production Mindset Migrating to Java 21 unlocks new capabilities, but production readiness requires deliberate configuration. The JVM defaults are conservative — designed to work reasonably across a wide range of workloads, not to be optimal for any specific one.
This article covers:
Which JVM flags to set for every production Java 21 deployment GC selection and tuning for different workload profiles Virtual thread configuration and monitoring Container-aware JVM settings Observability and profiling Startup and memory optimization JVM Flags: The Production Baseline Start every Java 21 production deployment with this baseline flag set:</description></item><item><title>Java 21: The LTS Release That Changes Everything</title><link>https://blog.devops-monk.com/tutorials/java21/java21-overview/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java21/java21-overview/</guid><description>Why Java 21 Is Different Every third Java release is an LTS — Long-Term Support. Java 21 is the fourth LTS after Java 8, 11, and 17. But unlike previous LTS releases, which were largely incremental, Java 21 delivers features that fundamentally change how you write concurrent code, how the JVM manages memory and GC, and how Java competes with dynamic languages for expressiveness.
Three years of Project Loom work lands as Virtual Threads — production-ready, requiring zero framework changes for most Spring Boot or Jakarta EE applications.</description></item><item><title>Java 8 Best Practices and Patterns for Production Code</title><link>https://blog.devops-monk.com/tutorials/java8/java8-best-practices/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java8/java8-best-practices/</guid><description>Introduction Java 8 introduced a fundamentally different programming model. The features — lambdas, streams, Optional, CompletableFuture — interact with each other in ways that produce clean, readable code when used correctly and confusing, brittle code when used incorrectly. This article consolidates the most important production-ready guidance from across the series into a single reference, along with the migration checklist for moving a Java 7 codebase to Java 8.
Streams Best Practices Do: Use streams for transformations and aggregations // Good: filter → transform → collect List&amp;lt;String&amp;gt; premiumNames = customers.</description></item><item><title>Java 8 Overview: The Release That Changed Everything</title><link>https://blog.devops-monk.com/tutorials/java8/java8-overview/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java8/java8-overview/</guid><description>Before We Start — Feel the Difference You&amp;rsquo;re about to learn Java 8. Before diving into history and theory, let&amp;rsquo;s just feel what changed. Here&amp;rsquo;s the same task written in Java 7 and Java 8:
Task: Find all names that start with &amp;ldquo;A&amp;rdquo;, uppercase them, sort them, and collect into a list.
// Java 7 — 10 lines, two passes, one temporary variable, zero joy List&amp;lt;String&amp;gt; result = new ArrayList&amp;lt;&amp;gt;(); for (String name : names) { if (name.</description></item><item><title>JDK Encapsulation and Removed APIs (JEP 403, 306, 407, 411): What It Means for Your Code</title><link>https://blog.devops-monk.com/tutorials/java17/jdk-encapsulation-removed-apis/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java17/jdk-encapsulation-removed-apis/</guid><description>Overview Java 17 includes several platform changes that can break existing code during migration. This article covers four JEPs that affect compatibility:
JEP Change Impact 403 Strongly Encapsulate JDK Internals HIGH — breaks libraries using internal APIs 407 Remove RMI Activation LOW — niche feature, deprecated since Java 15 411 Deprecate Security Manager MEDIUM — some applications use SecurityManager 306 Restore Always-Strict Floating-Point LOW — affects numeric edge cases JEP 403: Strongly Encapsulate JDK Internals What Changed In Java 8 and earlier, code could access any JDK internal API via reflection — setAccessible(true) bypassed access controls.</description></item><item><title>JSON Serialization: JsonSerializer, JsonDeserializer, and Type Mapping</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/json-serialization/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/json-serialization/</guid><description>The Serialization Problem Kafka stores bytes. KafkaTemplate&amp;lt;String, OrderPlacedEvent&amp;gt; needs to turn your Java object into bytes for the producer, and @KafkaListener needs to turn those bytes back into the right Java class on the consumer. Spring Kafka ships JsonSerializer and JsonDeserializer built on Jackson to handle this — but they have several sharp edges that break in real multi-service deployments.
How Spring Kafka JSON Serialization Works flowchart LR subgraph Producer["Order Service"</description></item><item><title>JVM Improvements: Metaspace, PermGen Removal, and Performance</title><link>https://blog.devops-monk.com/tutorials/java8/jvm-improvements/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java8/jvm-improvements/</guid><description>PermGen Removal: The End of a Classic Error OutOfMemoryError: PermGen space was the Java error that launched a thousand Stack Overflow questions. Application servers would run fine for hours and then fall over during a hot redeploy. The fix — adding more -XX:MaxPermSize — was a band-aid. Java 8 removed the underlying problem entirely.
Before Java 8, the JVM heap was divided into several regions. One of them — Permanent Generation (PermGen) — held class metadata, interned strings, and bytecode.</description></item><item><title>JWT Authentication: Stateless Token-Based Security</title><link>https://blog.devops-monk.com/tutorials/spring-security/jwt-authentication/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/jwt-authentication/</guid><description>What Is JWT? JWT (JSON Web Token, RFC 7519) is a compact, URL-safe token format for representing claims between two parties. It is the standard for stateless REST API authentication — the server issues a signed token at login, and the client presents it on every subsequent request. No session, no cookie, no server-side state.
JWT Structure A JWT has three Base64URL-encoded parts separated by dots:
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhbGljZSIsInJvbGVzIjpbIlJPTEVfVVNFUiJdfQ.signature Header Payload Signature block-beta columns 3 A["</description></item><item><title>Kafka Architecture: Brokers, Topics, Partitions, and Replicas</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/kafka-architecture/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/kafka-architecture/</guid><description>The Cluster: Brokers and the Controller A Kafka cluster is a group of servers, each called a broker. Brokers store data and serve producer/consumer requests. One broker in the cluster acts as the controller — it manages partition leadership, handles broker joins and departures, and coordinates rebalancing.
In KRaft mode (Kafka 3.3+, the default from Kafka 4.0), the controller is built into Kafka itself — no ZooKeeper needed.
flowchart TB subgraph Cluster["</description></item><item><title>Kafka Consumer in Spring Boot: @KafkaListener Basics</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/kafka-consumer-basics/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/kafka-consumer-basics/</guid><description>How @KafkaListener Works @KafkaListener is a Spring Kafka annotation that registers a method as a Kafka consumer. Under the hood, Spring Kafka creates a ConcurrentMessageListenerContainer — a managed thread pool that continuously polls the broker and dispatches records to your method.
flowchart LR Broker["Kafka Broker"] subgraph Container["ConcurrentMessageListenerContainer"] T1["Poll Thread 1\n(Partition 0)"] T2["Poll Thread 2\n(Partition 1)"] T3["Poll Thread 3\n(Partition 2)"] end Method["@KafkaListener\nvoid onOrderPlaced(...)"] Broker -->|"fetch records"| T1 Broker -->|"fetch records"| T2 Broker -->|"</description></item><item><title>Kafka Producer in Spring Boot: KafkaTemplate Basics</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/kafka-producer-basics/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/kafka-producer-basics/</guid><description>How a Spring Kafka Producer Works KafkaTemplate is the central Spring Kafka class for sending messages. It wraps the native Kafka KafkaProducer, manages serialization, and provides a Spring-friendly API for sending records.
flowchart LR App["Your Service\n(OrderService)"] KT["KafkaTemplate\n(Spring Kafka)"] Buffer["Producer Buffer\n(RecordAccumulator)"] Sender["Sender Thread\n(NetworkClient)"] Broker["Kafka Broker\n(Leader Partition)"] App -->|"send(topic, key, value)"| KT KT -->|serialize + route| Buffer Buffer -->|batch when full\nor linger.ms elapsed| Sender Sender -->|ProduceRequest| Broker Broker -->|ProduceResponse| Sender Sender -->|callback| App The send is asynchronous by default — KafkaTemplate.</description></item><item><title>Kafka Streams with Spring Boot: Stateless and Stateful Processing</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/kafka-streams/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/kafka-streams/</guid><description>Kafka Streams vs @KafkaListener @KafkaListener is a consumer — it reads records and processes them one by one or in batches. Kafka Streams is a stream processing library — it builds a topology of transformations that runs continuously, with built-in state stores, windowed aggregations, and join operations.
Aspect @KafkaListener Kafka Streams Processing model Consume and process Topology of operators Stateful processing Manual (external DB) Built-in state stores (RocksDB) Windowed aggregations Manual Native (time, session, hopping) Joins Manual KStream-KTable, KStream-KStream Fault tolerance Committed offsets Changelog topics + offsets Use when Imperative event handling Stream transformations and aggregations Maven Dependency &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;org.</description></item><item><title>Kafka Transactions and Exactly-Once Semantics</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/transactions-eos/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/transactions-eos/</guid><description>Why Transactions? At-least-once delivery means a record can be processed and produced more than once after a crash. For most applications, idempotent consumers handle this. But when you need a hard guarantee — either the produce happens and the offset commits, or neither does — you need Kafka transactions.
Common scenarios:
Consume → transform → produce (read from one topic, write to another) where partial completion is unacceptable Exactly-once aggregations in financial or billing systems Atomic multi-topic produce where records to multiple topics must all land or none land How Kafka Transactions Work sequenceDiagram participant Producer participant Broker participant Consumer Producer->>Broker: initTransactions() [registers transactional.</description></item><item><title>KafkaAdmin and AdminClient: Managing Topics Programmatically</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/kafka-admin/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/kafka-admin/</guid><description>Why Manage Topics Programmatically? CLI commands work for one-time setup. Production services need:
Startup validation — verify required topics exist before the application starts Auto-provisioning — create topics at deployment time with correct config Dynamic tenant onboarding — create per-tenant topics at runtime Config drift detection — compare actual topic config against expected values Spring Kafka provides KafkaAdmin for declarative topic management and AdminClient for imperative operations.
KafkaAdmin — Declarative Topic Creation Declare NewTopic beans — Spring Kafka creates them at startup if they don&amp;rsquo;t exist:</description></item><item><title>Key Encapsulation Mechanism API (JEP 452): Post-Quantum Cryptography in Java</title><link>https://blog.devops-monk.com/tutorials/java21/kem-api/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java21/kem-api/</guid><description>Why Post-Quantum Cryptography Now? Classical public-key cryptography (RSA, ECDH) relies on mathematical problems that are hard for classical computers — factoring large integers or solving the discrete logarithm problem. A sufficiently powerful quantum computer running Shor&amp;rsquo;s algorithm could solve these problems efficiently, breaking all existing RSA and ECC-based security.
Quantum computers capable of breaking 2048-bit RSA don&amp;rsquo;t exist yet. But &amp;ldquo;harvest now, decrypt later&amp;rdquo; attacks are real: adversaries intercept and store encrypted traffic today, planning to decrypt it once quantum computers mature.</description></item><item><title>Lambda Expressions (JEP 126): Syntax, Closures, and Target Typing</title><link>https://blog.devops-monk.com/tutorials/java8/lambdas/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java8/lambdas/</guid><description>The Problem Lambdas Solve Every Java 7 developer has written the same five lines of boilerplate to sort a list or run a background task. Lambda expressions eliminate that ceremony entirely — and once you understand target typing, closures, and composition, you will find yourself reaching for them in every layer of a codebase: validation pipelines, event systems, retry logic, and beyond.
Before Java 8, passing behaviour as a value required an anonymous inner class:</description></item><item><title>LDAP Authentication: Enterprise Directory Integration</title><link>https://blog.devops-monk.com/tutorials/spring-security/ldap-authentication/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/ldap-authentication/</guid><description>LDAP in the Enterprise LDAP (Lightweight Directory Access Protocol) is the standard protocol for directory services. Microsoft Active Directory, OpenLDAP, and many other enterprise identity providers use LDAP. If your application must authenticate users from a corporate directory, LDAP integration is the path.
sequenceDiagram participant Client participant SpringSecurity as Spring Security participant LDAP as LDAP / Active Directory Client->>SpringSecurity: POST /login {username, password} SpringSecurity->>LDAP: Bind as service account (manager DN) LDAP-->>SpringSecurity: Bind success SpringSecurity->>LDAP: Search for user:\n(uid=alice,ou=users,dc=example,dc=com) LDAP-->>SpringSecurity: User DN found SpringSecurity->>LDAP: Bind as user (verify password) LDAP-->>SpringSecurity: Bind success = password correct SpringSecurity->>LDAP: Search groups for user LDAP-->>SpringSecurity: Groups: [cn=developers, cn=devops] SpringSecurity->>SpringSecurity: Map groups to roles SpringSecurity-->>Client: Authentication success (ROLE_DEVELOPER, ROLE_DEVOPS) Dependencies &amp;lt;!</description></item><item><title>Many-to-Many Relationships: @ManyToMany and Join Tables</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/many-to-many-relationships/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/many-to-many-relationships/</guid><description>Introduction A many-to-many relationship means records in table A can relate to many records in table B, and vice versa. Products have many tags; tags apply to many products. In SQL this requires a join table. In JPA, @ManyToMany and @JoinTable handle this automatically — but when you need extra data on the join (like a creation date or a relevance score), you need a different approach.
Simple @ManyToMany: Product and Tag products (*) ──── product_tags ──── (*) tags The product_tags join table has two columns: product_id and tag_id.</description></item><item><title>Message Headers: Metadata, Routing, and Custom Header Propagation</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/message-headers/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/message-headers/</guid><description>What Are Kafka Record Headers? Every Kafka record carries a list of Header objects — key-value pairs of String key and byte[] value. They sit outside the message payload and are ideal for:
Trace propagation — carry X-Trace-Id / X-Span-Id across service boundaries Correlation IDs — link a response to a request in async flows Routing metadata — signal which region, tenant, or feature flag applies Schema type hints — __TypeId__ (set automatically by JsonSerializer) Event versioning — indicate schema version without modifying the payload flowchart LR subgraph Record["</description></item><item><title>Method References: Four Kinds and When to Use Each</title><link>https://blog.devops-monk.com/tutorials/java8/method-references/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java8/method-references/</guid><description>What Are Method References? Method references are a signal to the reader: &amp;ldquo;this lambda does exactly one thing — it calls this existing method.&amp;rdquo; When the entire body of a lambda is a single method call, replacing it with a method reference removes noise without losing information. Knowing which of the four kinds to apply in each context is what separates fluent Java 8 code from code that merely uses the syntax.</description></item><item><title>Method Security: @PreAuthorize, @PostAuthorize, @Secured</title><link>https://blog.devops-monk.com/tutorials/spring-security/method-security/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/method-security/</guid><description>Why Method Security Exists URL-based authorization in authorizeHttpRequests() protects HTTP endpoints, but it has blind spots:
The same service method may be called from multiple controllers, scheduled tasks, or message listeners — none of which pass through the HTTP filter chain Fine-grained rules based on method arguments or return values cannot be expressed as URL patterns Authorization logic spread across a large security config is hard to read alongside the code it protects Method security moves the authorization decision to the method itself.</description></item><item><title>Migrating to Java 11: From Java 8 — Step by Step</title><link>https://blog.devops-monk.com/tutorials/java11/migration-guide/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java11/migration-guide/</guid><description>Migration Overview Migrating from Java 8 to Java 11 is the most common Java upgrade scenario. The migration has two distinct phases:
Make it compile and run — fix incompatibilities introduced by Java 9–11 Modernise the code — adopt var, List.of(), String.isBlank(), and other new APIs Phase 1 is mandatory and blocks the upgrade. Phase 2 is optional and can happen incrementally.
This guide focuses entirely on Phase 1.
The 10-Step Migration Checklist [ ] 1.</description></item><item><title>Migrating to Java 17: From Java 8 and Java 11 — Step by Step</title><link>https://blog.devops-monk.com/tutorials/java17/migration-guide/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java17/migration-guide/</guid><description>Why Migrate to Java 17? Java 17 is the current recommended enterprise LTS. Key reasons to migrate now:
Spring Boot 3.x requires Java 17 — the entire Spring ecosystem is moving here Java 11 extended support ends around 2026 depending on your vendor Java 8 mainstream support ended in 2019; extended support ended 2030 for Oracle JDK but active vulnerability exposure is increasing Language features: Records, Sealed Classes, Text Blocks, Pattern Matching, Switch Expressions — all available in Java 17 Security: Strong JDK encapsulation closes years of internal API exposure used in exploits Recommended Migration Path Java 8 → Java 11 → Java 17 Do not jump directly from Java 8 to Java 17 in one step if your codebase is large or has many third-party dependencies.</description></item><item><title>Migrating to Java 21: From Java 8, 11, and 17 — Step by Step</title><link>https://blog.devops-monk.com/tutorials/java21/migration-guide/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java21/migration-guide/</guid><description>Why Migrate Now? Java 21 is the current Long-Term Support (LTS) release, and it is the most feature-rich LTS since Java 8. LTS releases receive security patches and bug fixes for years. Java 11, the previous widely-used LTS, reached its extended support window end depending on your vendor. Java 8 mainstream support ended in 2019.
More concretely, Java 21 brings:
Virtual Threads — drop-in replacement for platform threads, enabling massive concurrency without reactive rewrites Pattern Matching for switch and records — eliminating entire categories of verbose, error-prone instanceof/cast chains Sequenced Collections — a unified API for ordered collection types Generational ZGC — sub-millisecond GC pauses at any heap size These are not incremental improvements.</description></item><item><title>Module System (JPMS, JEP 261): Project Jigsaw Deep Dive</title><link>https://blog.devops-monk.com/tutorials/java11/module-system/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java11/module-system/</guid><description>The Problem JPMS Solves Before Java 9, the JDK had no real notion of encapsulation at the library level. public meant accessible to everyone — including internal JDK classes like sun.misc.Unsafe and com.sun.internal.*. Large codebases suffered from:
No reliable encapsulation: Any public class in any JAR was reachable from any other JAR. Classpath hell: Duplicate or conflicting classes from different JARs led to unpredictable behaviour. Monolithic JDK: The entire 60+ module JDK had to ship with every application.</description></item><item><title>Monitoring: Consumer Lag, Micrometer Metrics, and Actuator Integration</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/monitoring/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/monitoring/</guid><description>What to Monitor in Kafka Production Kafka applications need visibility into:
Consumer lag — how many records are unprocessed per partition Throughput — records produced and consumed per second Error rates — listener exceptions, DLT records, retry counts Producer latency — time from send() to broker acknowledgment Rebalance frequency — high rebalance rate signals consumer instability Dependencies &amp;lt;!-- Micrometer Prometheus registry --&amp;gt; &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;io.micrometer&amp;lt;/groupId&amp;gt; &amp;lt;artifactId&amp;gt;micrometer-registry-prometheus&amp;lt;/artifactId&amp;gt; &amp;lt;/dependency&amp;gt; &amp;lt;!-- Spring Boot Actuator --&amp;gt; &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;org.</description></item><item><title>Multi-Factor Authentication (TOTP and WebAuthn)</title><link>https://blog.devops-monk.com/tutorials/spring-security/multi-factor-authentication/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/multi-factor-authentication/</guid><description>Why MFA Matters A password alone is a single point of failure. Phishing, credential stuffing, and password reuse mean that knowing a password is not proof of identity. Multi-factor authentication requires something the user knows (password) and something they have (phone, hardware key) — compromising one factor is no longer sufficient.
TOTP: Time-Based One-Time Passwords TOTP (RFC 6238) generates a 6-digit code that changes every 30 seconds, derived from a shared secret and the current time.</description></item><item><title>Multi-Threaded Steps and Async Processing for Performance</title><link>https://blog.devops-monk.com/tutorials/spring-batch/multi-threaded-steps/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/multi-threaded-steps/</guid><description>Introduction A single-threaded Spring Batch step processes one chunk at a time — read N items, process N items, write N items, repeat. For large data sets this is a bottleneck. Spring Batch offers two in-JVM scaling options:
Approach How it works Use when Multi-threaded step Multiple threads each process independent chunks Reader is thread-safe (JdbcPagingItemReader) AsyncItemProcessor Processing runs concurrently; writes remain sequential I/O-bound processors (REST calls, slow enrichment) This article covers both, plus the thread-safety requirements you must meet.</description></item><item><title>New APIs: Base64, StringJoiner, Spliterator, Files.lines, StampedLock, Nashorn</title><link>https://blog.devops-monk.com/tutorials/java8/new-apis/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java8/new-apis/</guid><description>Base64 Before Java 8, Base64 encoding required a third-party library (Apache Commons Codec, Guava) or the internal sun.misc.BASE64Encoder class — an API that was never officially supported and was deliberately restricted from Java 9 onwards. Java 8 standardised encoding in java.util.Base64, which covers standard, URL-safe, and MIME variants and can wrap streams for large payloads. Java 8 added java.util.Base64 to the standard library.
Three Encoders // Standard Base64 (RFC 4648) Base64.</description></item><item><title>New String Methods (Java 11): isBlank, lines, strip, repeat</title><link>https://blog.devops-monk.com/tutorials/java11/string-methods/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java11/string-methods/</guid><description>New String Methods in Java 11 Java 11 added six new instance methods to java.lang.String. None require any imports — they are part of the standard String class.
Method Returns Description isBlank() boolean True if string is empty or contains only whitespace lines() Stream&amp;lt;String&amp;gt; Stream of lines split by line terminators strip() String Removes leading and trailing Unicode whitespace stripLeading() String Removes leading Unicode whitespace only stripTrailing() String Removes trailing Unicode whitespace only repeat(int n) String Returns the string repeated n times isBlank() Returns true if the string is empty or contains only whitespace characters as defined by Character.</description></item><item><title>Non-Blocking Retries: @RetryableTopic, BackOff, and the Retry Topic Chain</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/non-blocking-retries/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/non-blocking-retries/</guid><description>The Blocking Retry Problem DefaultErrorHandler retries by seeking back to the failed offset. While retrying, no other records from that partition are consumed — the partition is blocked. For a topic with high throughput, one slow retry can cause significant consumer lag.
flowchart TD subgraph Blocking["Blocking Retry (DefaultErrorHandler)"] B1["poll() → [r50, r51, r52, r53]"] B2["process r50 ✓"] B3["process r51 ✗ — retry"] B4["wait 10s... retry r51 ✗"] B5["wait 20s... retry r51 ✗"</description></item><item><title>OAuth2 Fundamentals: Grant Types and Flows</title><link>https://blog.devops-monk.com/tutorials/spring-security/oauth2-fundamentals/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/oauth2-fundamentals/</guid><description>OAuth2 in One Sentence OAuth2 lets a user grant a third-party application limited access to their account on another service — without giving the third party their password.
Classic example: &amp;ldquo;Sign in with Google.&amp;rdquo; Your app never sees the user&amp;rsquo;s Google password. Google verifies the user&amp;rsquo;s identity and gives your app a token with limited permissions.
The Four OAuth2 Roles flowchart TD RO["**Resource Owner**\nThe user who owns the data\n(e.g. Alice, the Google account owner)"</description></item><item><title>OAuth2 Login: Sign In with Google, GitHub, and Custom Providers</title><link>https://blog.devops-monk.com/tutorials/spring-security/oauth2-login/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/oauth2-login/</guid><description>What OAuth2 Login Does OAuth2LoginConfigurer implements the Authorization Code flow for user authentication. When a user clicks &amp;ldquo;Sign in with Google&amp;rdquo;:
Spring redirects to Google&amp;rsquo;s /authorize endpoint User authenticates on Google and grants consent Google redirects back to your app with a code Spring exchanges the code for tokens (back channel) Spring fetches the user profile from /userinfo Spring creates an OAuth2User and stores it in the SecurityContext Dependencies &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;org.</description></item><item><title>OAuth2 Resource Server: Protecting APIs with Bearer Tokens</title><link>https://blog.devops-monk.com/tutorials/spring-security/oauth2-resource-server/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/oauth2-resource-server/</guid><description>What Is a Resource Server? In OAuth2, a Resource Server is an API that accepts access tokens and returns protected resources. It doesn&amp;rsquo;t issue tokens — that&amp;rsquo;s the Authorization Server&amp;rsquo;s job. The Resource Server just validates tokens and enforces access control.
flowchart LR Client[API Client\nor SPA/Mobile] -->|"1. POST /token\n{client_id, secret}"| AS[Authorization Server\nGoogle / Okta / Custom] AS -->|"2. access_token"| Client Client -->|"3. GET /api/products\nAuthorization: Bearer {token}"| RS[Your Spring Boot API\nResource Server] RS -->|"</description></item><item><title>Offset Management: Auto-Commit vs Manual Acknowledgment</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/offset-management/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/offset-management/</guid><description>Why Offset Management Matters The committed offset determines what happens when a consumer restarts. If the offset is committed too early, a crash before processing completes means events are lost. If it is committed too late, a crash after processing but before committing means events are re-processed.
flowchart TD subgraph TooEarly["Commit too early → Data Loss"] E1["Commit offset 43"] --> E2["Process record 42"] --> E3["Crash!"] E4["Restart: resume from 43"] --> E5["</description></item><item><title>One-to-Many and Many-to-One: The Most Common Relationship</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/one-to-many-many-to-one/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/one-to-many-many-to-one/</guid><description>Introduction The one-to-many relationship is the most common in any domain model. An Order has many OrderItems. A Category has many Products. A Customer has many Orders. Understanding @OneToMany and @ManyToOne well — especially bidirectional mapping, collection types, and cascade configuration — is foundational to any JPA application.
The Domain Example orders (1) ──────────────── (*) order_items An order has many order items. Each order item belongs to exactly one order.</description></item><item><title>One-to-One Relationships: @OneToOne in Depth</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/one-to-one-relationships/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/one-to-one-relationships/</guid><description>Introduction A one-to-one relationship means one record in table A corresponds to exactly one record in table B. In JPA this is modelled with @OneToOne. Understanding where the foreign key lives, which side &amp;ldquo;owns&amp;rdquo; the relationship, and how cascade operations work is essential before moving to the more complex @OneToMany and @ManyToMany.
The Domain Example In the e-commerce system, a Customer has one CustomerProfile containing their preferences and biography. A profile belongs to exactly one customer.</description></item><item><title>Optimistic and Pessimistic Locking: Handling Concurrent Updates</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/optimistic-pessimistic-locking/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/optimistic-pessimistic-locking/</guid><description>The Lost Update Problem Two users edit the same product simultaneously:
Time User A User B T1 Load product (price=999) Load product (price=999) T2 Change price to 899 T3 Change price to 799 T4 Save → UPDATE price=899 T5 Save → UPDATE price=799 User A&amp;rsquo;s change is silently overwritten. This is the lost update problem. Both JPA locking strategies prevent it — in different ways.
Optimistic Locking with @Version Optimistic locking assumes conflicts are rare.</description></item><item><title>Optional: Eliminating NullPointerException the Right Way</title><link>https://blog.devops-monk.com/tutorials/java8/optional/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java8/optional/</guid><description>The Problem Optional Solves NullPointerException is the most common runtime exception in Java, and it is almost always avoidable. Optional&amp;lt;T&amp;gt; is not a magic fix — used incorrectly it becomes a more verbose null check. Used correctly, it encodes the possibility of absence directly in the type system, so callers can never &amp;ldquo;forget&amp;rdquo; to handle the empty case. This article covers both how to use Optional and — critically — how not to.</description></item><item><title>Pagination and Sorting with Pageable, Page, and Slice</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/pagination-sorting/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/pagination-sorting/</guid><description>Why Pagination Matters Returning all rows from a database table is one of the most common production incidents. A query that works in development with 100 rows silently becomes a 10-second, 2 GB memory spike when production has 2 million rows. Pagination limits how many rows travel from the database to the application at a time.
Spring Data JPA makes pagination a first-class feature — pass a Pageable to any repository method and get a Page&amp;lt;T&amp;gt; back.</description></item><item><title>Parallel Streams: ForkJoinPool, Spliterators, and When NOT to Parallelize</title><link>https://blog.devops-monk.com/tutorials/java8/parallel-streams/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java8/parallel-streams/</guid><description>How Parallel Streams Work Parallel streams are one of Java 8&amp;rsquo;s most misused features. It is tempting to add .parallel() to any slow stream pipeline, but the performance characteristics are counterintuitive: parallel can make things slower for small data, and adding blocking I/O inside a parallel stream can stall the entire JVM. This article explains the mechanics, the cases where parallel genuinely helps, and the patterns to avoid.
A parallel stream splits its source into sub-sequences, processes each sub-sequence on a separate thread, and merges the results.</description></item><item><title>Partitioning: Splitting Work Across Parallel Workers</title><link>https://blog.devops-monk.com/tutorials/spring-batch/partitioning/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/partitioning/</guid><description>Introduction Multi-threaded steps (Article 20) run multiple chunks concurrently from a single reader. Partitioning is different — it splits the data into independent slices before processing starts, then runs each slice as its own StepExecution with its own reader, processor, writer, and metadata.
This gives you:
True independence between partitions — one partition&amp;rsquo;s failure doesn&amp;rsquo;t affect others A separate StepExecution row per partition in BATCH_STEP_EXECUTION — full visibility into per-partition progress The ability to distribute partitions across multiple JVMs (remote partitioning, covered in Article 22) Partitioning Architecture ManagerStep (PartitionStep) │ ├── Partitioner → creates N ExecutionContexts (one per partition) ├── PartitionHandler → distributes partitions to workers │ └── Worker Steps (run per partition) ├── ItemReader → reads only its slice of data ├── ItemProcessor └── ItemWriter The manager step runs once: it calls Partitioner.</description></item><item><title>Password Encoding: BCrypt, Argon2, and DelegatingPasswordEncoder</title><link>https://blog.devops-monk.com/tutorials/spring-security/password-encoding/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/password-encoding/</guid><description>Why Passwords Must Be Hashed Storing plaintext passwords is a critical security failure. When a database is breached, attackers immediately have every user&amp;rsquo;s password — and because people reuse passwords, those credentials work on other sites too.
Password hashing is not encryption. Encryption is reversible. Hashing is one-way: you can verify a password by hashing it and comparing to the stored hash, but you cannot recover the original password from the hash.</description></item><item><title>Password Management: Registration, Reset, and Migration</title><link>https://blog.devops-monk.com/tutorials/spring-security/password-management/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/password-management/</guid><description>Secure Registration Registration is where passwords enter your system. Get it wrong here and no amount of downstream security saves you.
The Registration Flow User submits form → Validate password strength → Check username/email uniqueness → Encode password with PasswordEncoder → Persist user (disabled) → Send verification email → User clicks link → enable account Registration Endpoint @RestController @RequestMapping(&amp;#34;/auth&amp;#34;) public class RegistrationController { private final UserService userService; private final PasswordEncoder passwordEncoder; @PostMapping(&amp;#34;/register&amp;#34;) public ResponseEntity&amp;lt;Void&amp;gt; register(@Valid @RequestBody RegistrationRequest request) { userService.</description></item><item><title>Pattern Matching for instanceof (JEP 394): Smarter Type Checks</title><link>https://blog.devops-monk.com/tutorials/java17/pattern-matching-instanceof/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java17/pattern-matching-instanceof/</guid><description>Finalized in Java 16 (JEP 394). Available in all Java 16+ releases, including Java 17. Previous previews: Java 14 (JEP 305) and Java 15 (JEP 375).
The Problem: The instanceof-Cast Dance Every Java developer has written this pattern:
// Java 11 — check, then cast if (obj instanceof String) { String s = (String) obj; // cast is logically redundant System.out.println(s.toUpperCase()); } The instanceof check already verified the type. The cast on the next line is conceptually redundant — the compiler could infer that obj is a String in the if body.</description></item><item><title>Pattern Matching for switch (JEP 406): Type Dispatch in switch (Preview)</title><link>https://blog.devops-monk.com/tutorials/java17/pattern-matching-switch-preview/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java17/pattern-matching-switch-preview/</guid><description>Preview Feature in Java 17 (JEP 406 — First Preview). Requires --enable-preview. This feature evolved through Java 18 (JEP 420), Java 19 (JEP 427), Java 20 (JEP 433), and was finalized in Java 21 (JEP 441). The Java 21 final version has minor syntax differences from this Java 17 preview — documented below.
Enabling Preview Features Pattern matching for switch requires --enable-preview in Java 17. Add to your build tool:</description></item><item><title>Pattern Matching for switch (JEP 441): Type Dispatch Without the Boilerplate</title><link>https://blog.devops-monk.com/tutorials/java21/pattern-matching-switch/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java21/pattern-matching-switch/</guid><description>The Problem: Cascading instanceof Chains Any Java codebase handling multiple subtypes has code like this:
// Java 16 and earlier — the bad old way static double calculateArea(Shape shape) { if (shape instanceof Circle) { Circle c = (Circle) shape; return Math.PI * c.radius() * c.radius(); } else if (shape instanceof Rectangle) { Rectangle r = (Rectangle) shape; return r.width() * r.height(); } else if (shape instanceof Triangle) { Triangle t = (Triangle) shape; return 0.</description></item><item><title>Pausing, Resuming, and Stopping Listener Containers</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/listener-lifecycle/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/listener-lifecycle/</guid><description>Why Control Container Lifecycle? A running listener consumes from Kafka continuously. In production you need to:
Pause consumption when a downstream service is overloaded (back-pressure) Resume once the downstream recovers Stop a container entirely during maintenance or feature flag toggles Restart after a configuration change without redeploying Spring Kafka exposes all of this through KafkaListenerEndpointRegistry and the container&amp;rsquo;s own lifecycle API.
Container States stateDiagram-v2 [*] --> Running : start() Running --> Paused : pause() Paused --> Running : resume() Running --> Stopped : stop() Stopped --> Running : start() Paused --> Stopped : stop() Running — polling Kafka, dispatching to listener Paused — broker connection maintained, consumer heartbeat sent, no new records fetched Stopped — consumer thread terminated, partitions released back to group Paused is preferable to stopped for temporary throttling: it avoids a rebalance and keeps the consumer&amp;rsquo;s partition assignment intact.</description></item><item><title>Performance Tuning: Chunk Size, Connection Pools, and Memory Management</title><link>https://blog.devops-monk.com/tutorials/spring-batch/performance-tuning/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/performance-tuning/</guid><description>Introduction A poorly tuned batch job can be 10–100x slower than a well-tuned one. The biggest gains come from a handful of settings — chunk size, MySQL JDBC rewrite, connection pool alignment, and avoiding unnecessary object creation. This article covers each systematically.
Chunk Size — The Most Impactful Setting Chunk size determines how many items are processed per transaction. Too small = too many round trips to the database. Too large = long transactions, high memory pressure, slower rollback on failure.</description></item><item><title>Primary Keys and Generated Values: IDENTITY, SEQUENCE, UUID</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/primary-keys-generated-values/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/primary-keys-generated-values/</guid><description>Introduction Primary key choice affects performance, scalability, and application design. This article covers every strategy JPA supports — from the simple AUTO_INCREMENT to UUID and composite keys — with the trade-offs of each.
IDENTITY Strategy (MySQL AUTO_INCREMENT) GenerationType.IDENTITY delegates key generation to the database column&amp;rsquo;s auto-increment feature. This is the standard for MySQL.
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; Generated DDL (when ddl-auto=create):
id BIGINT NOT NULL AUTO_INCREMENT How IDENTITY works with Hibernate IDENTITY has one important behaviour: Hibernate cannot batch INSERT statements when using IDENTITY.</description></item><item><title>Producer @Bean Configuration: Beyond application.properties</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/producer-bean-configuration/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/producer-bean-configuration/</guid><description>Why @Bean Configuration? application.properties is convenient for a single producer, but insufficient when you need:
Multiple producers with different serializers (e.g. one for JSON events, one for Avro) Different settings per environment built at runtime (not just property substitution) Producers sending to different clusters (e.g. primary + DR cluster) Programmatic validation of configuration at startup flowchart TB subgraph PropertiesApproach["application.properties Approach"] P1["Single producer config\nspring.kafka.producer.*\n✓ Simple\n✗ One producer only\n✗ No runtime logic"] end subgraph BeanApproach["</description></item><item><title>Producer Acknowledgments: acks, min.insync.replicas, and Data Durability</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/producer-acknowledgments/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/producer-acknowledgments/</guid><description>What Are Acknowledgments? When a producer sends a record to a Kafka broker, it can wait for confirmation that the write was received and replicated before considering the send &amp;ldquo;complete.&amp;rdquo; The acks setting controls how much confirmation the producer requires.
flowchart LR Producer["Producer"] Leader["Partition Leader\n(Broker 1)"] F1["Follower\n(Broker 2)"] F2["Follower\n(Broker 3)"] Producer -->|"ProduceRequest"| Leader Leader -->|"replicate"| F1 Leader -->|"replicate"| F2 Leader -->|"ProduceResponse ✓"| Producer style Producer fill:#3b82f6,color:#fff style Leader fill:#10b981,color:#fff The acknowledgment is the broker&amp;rsquo;s confirmation to the producer.</description></item><item><title>Producer Retries: Backoff, Timeouts, and Retry Strategies</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/producer-retries/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/producer-retries/</guid><description>Why Producers Need Retries Network errors, leader elections, and broker restarts are normal events in a distributed system. Without retries, a transient broker hiccup causes permanent data loss from the producer&amp;rsquo;s perspective. With retries, the producer automatically re-sends failed records until either the broker accepts them or a timeout deadline is reached.
sequenceDiagram participant Producer participant Leader as Leader (Broker 1) participant NewLeader as New Leader (Broker 2) Producer->>Leader: ProduceRequest (offset 42) Note over Leader: Broker 1 crashes mid-write Leader--xProducer: No response (timeout) Note over Producer: retry.</description></item><item><title>Projections and DTOs: Fetching Only What You Need</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/projections-dtos/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/projections-dtos/</guid><description>The Over-Fetching Problem By default, findAll() loads every column for every entity. A Product entity with 20 fields loads all 20 columns — even when a dropdown menu only needs id and name. This wastes bandwidth, memory, and query time.
Projections let you define what shape the result should take, and Spring Data JPA generates a query that fetches only those columns.
Interface Projections The simplest projection: declare an interface with getter methods matching entity field names.</description></item><item><title>Reactive Security with Spring WebFlux</title><link>https://blog.devops-monk.com/tutorials/spring-security/reactive-security-webflux/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/reactive-security-webflux/</guid><description>Reactive vs. Servlet Security Spring Security&amp;rsquo;s standard configuration targets Servlet-based applications (Spring MVC). Reactive applications built with Spring WebFlux run on a non-blocking event loop — there is no thread-per-request model, so ThreadLocal-based SecurityContextHolder does not work.
Spring Security provides a parallel reactive stack:
Servlet Reactive SecurityFilterChain SecurityWebFilterChain HttpSecurity ServerHttpSecurity SecurityContextHolder ReactiveSecurityContextHolder UserDetailsService ReactiveUserDetailsService AuthenticationManager ReactiveAuthenticationManager @EnableWebSecurity @EnableWebFluxSecurity @EnableMethodSecurity @EnableReactiveMethodSecurity Dependencies &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt; &amp;lt;artifactId&amp;gt;spring-boot-starter-webflux&amp;lt;/artifactId&amp;gt; &amp;lt;/dependency&amp;gt; &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt; &amp;lt;artifactId&amp;gt;spring-boot-starter-security&amp;lt;/artifactId&amp;gt; &amp;lt;/dependency&amp;gt; Spring Security auto-configures reactive security when WebFlux is on the classpath.</description></item><item><title>Record Patterns (JEP 440): Destructuring Records with Power and Precision</title><link>https://blog.devops-monk.com/tutorials/java21/record-patterns/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java21/record-patterns/</guid><description>Records Recap Records (Java 16, JEP 395) are transparent carriers of immutable data:
record Point(int x, int y) {} record ColoredPoint(Point point, String color) {} record Line(Point start, Point end) {} The compiler generates a constructor, accessor methods (x(), y()), equals, hashCode, and toString. Before Java 21, accessing record components required calling accessor methods:
Object obj = new ColoredPoint(new Point(3, 4), &amp;#34;red&amp;#34;); if (obj instanceof ColoredPoint cp) { int x = cp.</description></item><item><title>Records (JEP 395): Immutable Data Classes Without the Boilerplate</title><link>https://blog.devops-monk.com/tutorials/java17/records/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java17/records/</guid><description>Finalized in Java 16 (JEP 395). Available in all Java 16+ releases, including Java 17. Previous previews: Java 14 (JEP 359) and Java 15 (JEP 384).
The Problem: Data Classes in Java Writing a simple immutable data class in Java 11 requires significant boilerplate:
public final class Point { private final int x; private final int y; public Point(int x, int y) { this.x = x; this.y = y; } public int x() { return x; } public int y() { return y; } @Override public boolean equals(Object o) { if (this == o) return true; if (!</description></item><item><title>Refresh Tokens and Token Rotation</title><link>https://blog.devops-monk.com/tutorials/spring-security/refresh-tokens/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/refresh-tokens/</guid><description>Why Refresh Tokens? Short-lived access tokens (15–60 minutes) limit the damage if a token is stolen — it expires quickly. But forcing users to log in every hour is terrible UX.
Refresh tokens solve this: a long-lived token (7–30 days) stored securely lets the client silently obtain a new access token when the old one expires. The user stays logged in indefinitely without re-entering credentials.
sequenceDiagram participant Client participant AuthServer as Auth Endpoint participant API as Protected API Client->>AuthServer: POST /api/auth/login AuthServer-->>Client: {accessToken: exp 15min, refreshToken: exp 7d} Note over Client,API: Normal API usage (15 min) Client->>API: GET /api/data\nAuthorization: Bearer {accessToken} API-->>Client: 200 OK Note over Client,API: Access token expires Client->>API: GET /api/data\nAuthorization: Bearer {expiredToken} API-->>Client: 401 Unauthorized Note over Client,API: Silent token refresh Client->>AuthServer: POST /api/auth/refresh\n{refreshToken} AuthServer-->>Client: {newAccessToken, newRefreshToken} Client->>API: GET /api/data\nAuthorization: Bearer {newAccessToken} API-->>Client: 200 OK Token Rotation: Every Refresh Issues a New Refresh Token Token rotation is the critical security mechanism: every time a refresh token is used, the server issues a new refresh token and invalidates the old one.</description></item><item><title>Remote Partitioning and Remote Chunking with Kafka</title><link>https://blog.devops-monk.com/tutorials/spring-batch/remote-partitioning-kafka/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/remote-partitioning-kafka/</guid><description>Introduction Local partitioning (Article 21) runs all workers on one JVM. When a single machine is the bottleneck — CPU, memory, or network bandwidth — you need workers on separate machines. Spring Batch Integration provides two patterns for this:
Pattern What distributes Coordinator controls Workers do Remote Partitioning Partition descriptors (small messages) Data splitting, aggregation Full read-process-write per partition Remote Chunking Actual items (larger messages) Reading Processing + writing only Remote partitioning is the more common choice — workers read directly from the database/file, so only small partition metadata crosses the network.</description></item><item><title>Removed and Deprecated APIs: Java EE, JavaFX, Nashorn (JEP 320, 335, 336)</title><link>https://blog.devops-monk.com/tutorials/java11/removed-deprecated-apis/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java11/removed-deprecated-apis/</guid><description>Removal Timeline API Deprecated In Removed In JEP Java EE modules (JAXB, JAX-WS, etc.) Java 9 Java 11 JEP 320 JavaFX Bundled with Java 9 Unbundled in Java 11 — Nashorn JavaScript Engine Java 11 Java 15 JEP 335 Pack200 tools and API Java 11 Java 14 JEP 336 javah tool Java 9 Java 10 JEP 313 sun.misc.BASE64Encoder/Decoder Java 8 Java 11 Encapsulation Thread.destroy(), Thread.stop(Throwable) Old Java 11 — Applet API Java 9 Java 17 JEP 289 Java EE Modules Removed (JEP 320) The six Java EE modules that shipped with Java SE 6–10 were removed from Java 11.</description></item><item><title>Repository Interfaces: CrudRepository, JpaRepository, and How They Work</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/repository-interfaces/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/repository-interfaces/</guid><description>Introduction Spring Data JPA&amp;rsquo;s repository abstraction eliminates the DAO boilerplate that every Java application used to require. You declare an interface, extend one of the repository base interfaces, and Spring generates a complete implementation at startup — find, save, delete, count, and more — with zero code written.
The Repository Hierarchy Repository&amp;lt;T, ID&amp;gt; ← Marker interface — no methods │ └── CrudRepository&amp;lt;T, ID&amp;gt; ← Basic CRUD: save, find, delete, count │ └── PagingAndSortingRepository&amp;lt;T, ID&amp;gt; ← + findAll(Pageable), findAll(Sort) │ └── JpaRepository&amp;lt;T, ID&amp;gt; ← + flush, saveAndFlush, deleteInBatch Each interface adds more operations.</description></item><item><title>Request-Reply Pattern with ReplyingKafkaTemplate</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/request-reply/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/request-reply/</guid><description>When Kafka Needs to Be Synchronous Kafka is designed for asynchronous event streaming. But some flows genuinely need a response: a payment validation service that must confirm before the order proceeds, or a pricing engine that must return the current price before checkout completes. ReplyingKafkaTemplate gives you a blocking send-and-receive call over Kafka without leaving the Kafka ecosystem.
How Request-Reply Works sequenceDiagram participant Requester as "Order Service\n(ReplyingKafkaTemplate)" participant Broker participant Replier as "</description></item><item><title>Retryable vs Non-Retryable Exceptions: Custom Exception Classification</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/retryable-exceptions/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/retryable-exceptions/</guid><description>Transient vs Permanent Failures Not every exception is worth retrying. Retrying a NullPointerException or a schema validation error wastes time and delays other records. Retrying a database timeout or a downstream HTTP 503 is exactly right — the error is temporary and will likely resolve.
flowchart TD Ex["Exception in listener"] Q{"Transient?\n(DB timeout, HTTP 503,\nnetwork blip)"} Q -->|Yes| Retry["Retry with BackOff"] Q -->|No| Skip["Call recoverer immediately\n(no retries wasted)"] Retry -->|"still failing after\nmax retries"</description></item><item><title>Role-Based Access Control: Roles, Authorities, and Hierarchies</title><link>https://blog.devops-monk.com/tutorials/spring-security/role-based-access-control/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/role-based-access-control/</guid><description>Roles vs. Authorities: The Distinction That Matters Spring Security uses one interface for both roles and authorities: GrantedAuthority. Both are just strings. The difference is convention.
Authority: a fine-grained permission string — user:read, report:export, order:cancel Role: a coarse-grained label that groups authorities — ROLE_ADMIN, ROLE_MANAGER, ROLE_USER The ROLE_ prefix is the only mechanical difference. When you call hasRole(&amp;quot;ADMIN&amp;quot;), Spring Security prepends ROLE_ automatically and checks for ROLE_ADMIN. When you call hasAuthority(&amp;quot;ROLE_ADMIN&amp;quot;) you must include the prefix yourself.</description></item><item><title>Scheduling Batch Jobs: @Scheduled, Quartz, and Clustered Scheduling</title><link>https://blog.devops-monk.com/tutorials/spring-batch/scheduling-batch-jobs/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/scheduling-batch-jobs/</guid><description>Introduction A batch job that only runs manually is barely useful. Production jobs run on a schedule — nightly, hourly, after a file arrives. Spring Boot provides three scheduling options:
Option Persistence Clustered Use when @Scheduled No No Simple cron on a single node Quartz Scheduler Yes (DB) Yes HA scheduling, persistent triggers External scheduler (Kubernetes CronJob, Airflow) Varies Yes Complex pipelines, dependency management @Scheduled — Simple Cron Trigger The simplest approach: annotate a method with @Scheduled and run the job from it.</description></item><item><title>Scoped Values (JEP 446): Thread-Safe Context Without ThreadLocal</title><link>https://blog.devops-monk.com/tutorials/java21/scoped-values/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java21/scoped-values/</guid><description>Preview Feature — Requires --enable-preview at compile and runtime.
The ThreadLocal Problem at Scale ThreadLocal has been the standard way to pass context (request ID, user session, database transaction) implicitly through a call stack without threading it through every method signature. It works well with a few hundred platform threads. With virtual threads, it breaks down.
Problem 1 — Memory overhead with inheritance
When you create a child thread with InheritableThreadLocal, Java copies the thread-local map from parent to child:</description></item><item><title>Sealed Classes (JEP 409): Controlled, Exhaustive Class Hierarchies</title><link>https://blog.devops-monk.com/tutorials/java17/sealed-classes/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java17/sealed-classes/</guid><description>Finalized in Java 17 (JEP 409). This is the headline language feature of the Java 17 LTS release. Previous previews: Java 15 (JEP 360) and Java 16 (JEP 397).
The Problem: Open Hierarchies Are Hard to Reason About In Java, any class can be extended by default. This openness is flexible but has costs.
Consider a Shape interface used in a drawing application:
// Java 11 — anyone can implement Shape public interface Shape { double area(); } At runtime, a Shape instance could be a Circle, a Rectangle, a Triangle, or anything else in the classpath.</description></item><item><title>Second-Level Cache and Query Cache with Hibernate</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/second-level-cache/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/second-level-cache/</guid><description>Cache Layers in Hibernate Hibernate has two cache levels:
Level Scope Lifetime Shared? First-level cache One Session (one transaction) Transaction No — per session Second-level cache SessionFactory Application lifetime Yes — across sessions The first-level cache (Article 24) prevents repeated reads within one transaction. The second-level cache prevents repeated reads across transactions — once an entity is loaded, it stays in the shared cache until evicted.
When to Use the Second-Level Cache Good candidates:</description></item><item><title>Security Headers: CSP, HSTS, Clickjacking Protection</title><link>https://blog.devops-monk.com/tutorials/spring-security/security-headers/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/security-headers/</guid><description>Why Security Headers Matter Security headers tell browsers how to behave when handling your content. They stop entire classes of attacks — XSS, clickjacking, protocol downgrade, information leakage — with a few lines of configuration. They cost nothing at runtime and are one of the highest-value-per-effort security improvements available.
Spring Security&amp;rsquo;s Default Headers Spring Security adds a set of secure headers by default. You do not need any explicit configuration to get them:</description></item><item><title>Security: TLS 1.3, ChaCha20-Poly1305, and Curve25519 (JEP 329, 332, 324)</title><link>https://blog.devops-monk.com/tutorials/java11/security-tls13/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java11/security-tls13/</guid><description>Security Improvements Across Java 9–11 JEP Release Feature JEP 287 Java 9 SHA-3 hash algorithms JEP 273 Java 9 DRBG-based SecureRandom JEP 288 Java 9 Disable SHA-1 certificates JEP 324 Java 11 Key Agreement with Curve25519 and Curve448 JEP 329 Java 11 ChaCha20 and Poly1305 cryptographic algorithms JEP 332 Java 11 Transport Layer Security (TLS) 1.3 JEP 181 Java 11 Nest-Based Access Control TLS 1.3 (JEP 332) TLS 1.3 is the current version of the Transport Layer Security protocol, finalised in RFC 8446 (August 2018).</description></item><item><title>SecurityContext and Authentication Object</title><link>https://blog.devops-monk.com/tutorials/spring-security/security-context-authentication/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/security-context-authentication/</guid><description>The SecurityContext Is the Source of Truth Every security decision in Spring Security ultimately comes down to one question: &amp;ldquo;What Authentication object is stored in the SecurityContext?&amp;rdquo;
Does the user have ROLE_ADMIN? → check Authentication.getAuthorities() What is the user&amp;rsquo;s ID? → cast Authentication.getPrincipal() to UserDetails Is the user logged in at all? → check Authentication.isAuthenticated() Understanding the SecurityContext and Authentication is not optional — it underlies everything.
The Object Hierarchy classDiagram class SecurityContextHolder { -strategy: SecurityContextHolderStrategy +getContext() SecurityContext +setContext(SecurityContext context) +clearContext() +getContextHolderStrategy() SecurityContextHolderStrategy } class SecurityContext { &lt;> +getAuthentication() Authentication +setAuthentication(Authentication authentication) } class Authentication { &lt;> +getPrincipal() Object +getCredentials() Object +getAuthorities() Collection~GrantedAuthority~ +getDetails() Object +isAuthenticated() boolean +getName() String } class UsernamePasswordAuthenticationToken { +UsernamePasswordAuthenticationToken(principal, credentials) +UsernamePasswordAuthenticationToken(principal, credentials, authorities) } class GrantedAuthority { &lt;> +getAuthority() String } class SimpleGrantedAuthority { -role: String +getAuthority() String } SecurityContextHolder --> SecurityContext SecurityContext --> Authentication Authentication &lt;|.</description></item><item><title>SecurityFilterChain Bean: The Modern Configuration API</title><link>https://blog.devops-monk.com/tutorials/spring-security/security-filter-chain-config/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/security-filter-chain-config/</guid><description>The Modern Configuration Model Spring Security 6.x dropped WebSecurityConfigurerAdapter. The new model uses a SecurityFilterChain @Bean directly. This is not just a syntax change — it&amp;rsquo;s a fundamentally cleaner design:
Old approach New approach Extend WebSecurityConfigurerAdapter @Bean SecurityFilterChain method Override configure(HttpSecurity) Accept HttpSecurity parameter Chain with .and() Lambda DSL — each concern is a separate block One class per application Multiple beans, one per URL namespace Implicit global AuthenticationManager Explicit AuthenticationManager bean // OLD — don&amp;#39;t do this @Configuration public class OldSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.</description></item><item><title>Seeking to Specific Offsets: Replay, Recovery, and Time-Based Seeking</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/seeking-offsets/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/seeking-offsets/</guid><description>Why Seek Instead of Reset? Offset management (auto-commit vs manual acknowledgment) controls when offsets advance during normal processing. Seeking is different: it lets you reposition the consumer to any offset — past or future — programmatically, without touching the committed offset in __consumer_offsets.
Common scenarios:
Replay from the beginning — reprocess all historical events after a bug fix Resume from a known-good offset — skip a poison pill that&amp;rsquo;s blocking the consumer Time-based replay — reprocess everything since yesterday 09:00 Startup positioning — always start from the end, ignoring backlog on first launch How Kafka Seeking Works flowchart LR subgraph Broker["</description></item><item><title>Sending Messages with Keys, Headers, and Custom Partitioning</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/producer-keys-headers-partitioning/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/producer-keys-headers-partitioning/</guid><description>Why Partitioning Strategy Matters How you route messages to partitions determines:
Ordering: only messages in the same partition are ordered relative to each other Parallelism: how evenly work is distributed across consumers Hot spots: if one key generates 90% of traffic, one partition (and one consumer) gets 90% of the load flowchart TD subgraph Routing["Message Routing Decision"] Msg["Message"] HasKey{Has key?} HasPartition{Explicit partition?} KeyHash["hash(key) % numPartitions\n→ deterministic, same partition always"] RoundRobin["Sticky partitioning\n(batch to same partition,\nthen round-robin)"</description></item><item><title>Sequenced Collections (JEP 431): A Unified API for Ordered Collections</title><link>https://blog.devops-monk.com/tutorials/java21/sequenced-collections/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java21/sequenced-collections/</guid><description>The 30-Year Gap in the Collections API Java&amp;rsquo;s Collections Framework has a fundamental inconsistency: there is no uniform way to access the first or last element of an ordered collection. Before Java 21:
// List — index-based var list = List.of(&amp;#34;a&amp;#34;, &amp;#34;b&amp;#34;, &amp;#34;c&amp;#34;); String first = list.get(0); // first element String last = list.get(list.size() - 1); // last element — verbose, error-prone // Deque — special methods Deque&amp;lt;String&amp;gt; deque = new ArrayDeque&amp;lt;&amp;gt;(List.</description></item><item><title>Session Management: Fixation, Concurrency, and Redis Sessions</title><link>https://blog.devops-monk.com/tutorials/spring-security/session-management/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/session-management/</guid><description>How Spring Security Uses Sessions For form login and traditional web applications, Spring Security stores the Authentication object in the HTTP session. On every request, SecurityContextPersistenceFilter (Spring Security 5) or SecurityContextHolderFilter (Spring Security 6) loads the SecurityContext from the session and puts it in the SecurityContextHolder.
For stateless APIs using JWT or OAuth2 Bearer tokens, no session is created — the token is verified on every request.
Session Creation Policy Control when Spring Security creates sessions:</description></item><item><title>Setting Up Java 11: JDK Options, Maven/Gradle, and IDE Configuration</title><link>https://blog.devops-monk.com/tutorials/java11/java11-setup/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java11/java11-setup/</guid><description>Choosing a JDK Distribution Java 11 is available from multiple OpenJDK distributions. All are TCK-certified and binary-compatible; the differences are support timelines and add-ons.
Distribution Vendor Java 11 Support Until Notes Eclipse Temurin Adoptium Oct 2027 Community standard, most downloaded Amazon Corretto Amazon Aug 2024 (free), extended via AWS Best choice for AWS workloads Azul Zulu Azul Systems 2026+ Wide platform coverage Microsoft Build of OpenJDK Microsoft 2024 Best for Azure Oracle JDK 11 Oracle Oct 2024 (free LTS) Commercial support until 2026 GraalVM CE 21 Oracle/GraalVM Community Native image + polyglot; JDK 21-based For most teams: Eclipse Temurin 11 — widest platform support, longest free support window, backed by a vendor-neutral foundation.</description></item><item><title>Setting Up Java 17: JDK Options, Tooling, and IDE Configuration</title><link>https://blog.devops-monk.com/tutorials/java17/java17-setup/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java17/java17-setup/</guid><description>JDK Distribution Options Multiple vendors ship Java 17 JDK builds. All pass the TCK (Technology Compatibility Kit) — they are functionally equivalent for development.
Distribution Provider Notes Eclipse Temurin Adoptium / Eclipse Recommended default; fully open-source Amazon Corretto AWS Free; optimized for AWS Lambda and EC2 Microsoft Build of OpenJDK Microsoft Windows and Azure optimized Oracle JDK 17 Oracle Free for development; commercial license for production Azul Zulu Azul Commercial support available; free binaries GraalVM CE 22.</description></item><item><title>Setting Up Java 21: JDK Options, Tooling, and IDE Configuration</title><link>https://blog.devops-monk.com/tutorials/java21/java21-setup/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java21/java21-setup/</guid><description>Choosing a JDK Distribution Java 21 is open source (OpenJDK). Multiple vendors distribute binaries, all built from the same source with identical features. Choose based on your support requirements:
Distribution Vendor Free LTS Support Notes Eclipse Temurin Adoptium Yes 2028+ Recommended for most developers OpenJDK Oracle/Community Yes 6 months only Reference implementation, no LTS Oracle JDK Oracle Yes (NFTC) 2031 Identical to OpenJDK since Java 17 Amazon Corretto AWS Yes 2030+ Good for AWS deployments Microsoft Build of OpenJDK Microsoft Yes Long-term Good for Azure deployments Azul Zulu Azul Yes/Paid Long-term Includes Zing JVM variant GraalVM Oracle Yes Long-term Adds native-image, polyglot Recommendation: Eclipse Temurin for local development; match your cloud provider&amp;rsquo;s JDK for production (Corretto on AWS, Microsoft OpenJDK on Azure).</description></item><item><title>Setting Up Java 8: JDK Options, Maven/Gradle, and IDE Configuration</title><link>https://blog.devops-monk.com/tutorials/java8/java8-setup/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java8/java8-setup/</guid><description>Choosing a Java 8 Distribution Getting your environment right before writing a single line of code saves hours of debugging compiler errors and version conflicts later. This article covers every distribution option, all three major build tools, and both popular IDEs — including the specific settings that trip up most developers when first configuring Java 8.
Oracle stopped providing free Java 8 updates for commercial use in January 2019. Multiple vendors now ship free, production-quality Java 8 builds:</description></item><item><title>Setting Up Spring Boot with Spring Data JPA and MySQL</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/setup-spring-boot-spring-data-jpa/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/setup-spring-boot-spring-data-jpa/</guid><description>Introduction This article builds the project foundation used throughout the entire series. By the end, you will have a running Spring Boot 3.3 application connected to MySQL 8.x with Hibernate 6, a proper connection pool, schema management via Flyway, and SQL logging configured so you can see exactly what Hibernate sends to the database.
Project Setup Maven pom.xml &amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt; &amp;lt;project xmlns=&amp;#34;http://maven.apache.org/POM/4.0.0&amp;#34; xmlns:xsi=&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34; xsi:schemaLocation=&amp;#34;http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd&amp;#34;&amp;gt; &amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt; &amp;lt;parent&amp;gt; &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt; &amp;lt;artifactId&amp;gt;spring-boot-starter-parent&amp;lt;/artifactId&amp;gt; &amp;lt;version&amp;gt;3.</description></item><item><title>Specifications and the Criteria API: Dynamic Queries</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/specifications-criteria-api/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/specifications-criteria-api/</guid><description>Why Specifications Derived query methods and @Query annotations work for fixed queries. But what happens when the user can filter by any combination of 10 fields — some optional, some not?
// Wrong approach — combinatorial explosion List&amp;lt;Product&amp;gt; findByName(String name); List&amp;lt;Product&amp;gt; findByNameAndPrice(String name, BigDecimal price); List&amp;lt;Product&amp;gt; findByNameAndPriceAndCategory(...); // ... you&amp;#39;d need 2^10 = 1024 methods The Specification pattern solves this. Each filter condition is a reusable Specification&amp;lt;T&amp;gt; object. You compose them with and(), or(), and not() at runtime, and Spring Data JPA generates the correct query.</description></item><item><title>Spring Authorization Server: Build Your Own OAuth2 Server</title><link>https://blog.devops-monk.com/tutorials/spring-security/spring-authorization-server/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/spring-authorization-server/</guid><description>What Is Spring Authorization Server? Spring Authorization Server (SAS) is an official Spring project that implements OAuth 2.1 and OpenID Connect 1.0 as a Spring Boot application. It provides a complete authorization server you can host yourself — issuing tokens for your own clients and APIs.
Use it when:
You want SSO across your own microservices You cannot use a hosted provider (Okta, Auth0) due to compliance or cost You need complete control over token format and claims You&amp;rsquo;re building a platform where other apps authenticate against your identity service Architecture flowchart TD SPA[SPA / Mobile App\nOAuth2 Client] -->|Authorization Code + PKCE| SAS Service[Backend Service\nClient Credentials| SAS] SAS[Spring Authorization Server\nIssues tokens] RS1[Your REST API\nResource Server] -->|Validates JWT| SAS RS2[Another API\nResource Server] -->|Validates JWT| SAS SAS -->|Signs tokens with| PK[Private Key\nJWK Set at /.</description></item><item><title>Spring Kafka Production Checklist and Best Practices</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/production-best-practices/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/production-best-practices/</guid><description>Before You Ship This is the checklist distilled from everything in this series. Work through it before your first production deployment. Each item links to the article where it&amp;rsquo;s covered in depth.
Producer Checklist Durability # Never lose data on leader failure spring.kafka.producer.acks=all # At least 2 brokers must acknowledge every write spring.kafka.producer.properties.min.insync.replicas=2 # Enables exactly-once message delivery (required for transactions) spring.kafka.producer.properties.enable.idempotence=true Do: Set acks=all and min.insync.replicas=2 for any topic that carries business data.</description></item><item><title>Spring Security Best Practices and Production Checklist</title><link>https://blog.devops-monk.com/tutorials/spring-security/spring-security-best-practices/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/spring-security-best-practices/</guid><description>Using This Reference This is the final article in the Spring Security series. It is a consolidated reference — not a tutorial. Come back to this checklist before every launch and when reviewing a new codebase. Each item links back to the relevant article in the series.
1. Keep Spring Security Updated Security vulnerabilities in Spring Security itself are rare but severe when they occur. A dependency that is one minor version behind can expose known CVEs.</description></item><item><title>Stream &amp; Optional API Enhancements (Java 9–11)</title><link>https://blog.devops-monk.com/tutorials/java11/stream-optional-enhancements/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java11/stream-optional-enhancements/</guid><description>Stream API — New Methods (Java 9) Java 9 added four new instance methods to Stream&amp;lt;T&amp;gt;. All are terminal-ish or intermediate operations that make common patterns expressible without workarounds.
takeWhile(Predicate) Returns elements from the beginning of the stream as long as the predicate holds, then stops. The first element that fails the predicate (and all subsequent elements) are discarded.
Stream.of(1, 2, 3, 4, 5, 1, 2) .takeWhile(n -&amp;gt; n &amp;lt; 4) .</description></item><item><title>Streams API: Lazy Pipelines and the Functional Data Model</title><link>https://blog.devops-monk.com/tutorials/java8/streams-introduction/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java8/streams-introduction/</guid><description>The Problem Streams Solve Java loops are not wrong — they are often the right tool. But when a computation is a multi-step pipeline of filter → transform → aggregate, a loop hides the structure of the computation in a tangle of temporary variables and mutated collections. The Streams API makes the structure explicit, lets the JVM optimise it, and composes naturally with lambdas and method references.
Consider filtering a list of orders to find the names of active premium customers who spent over $500, sorted alphabetically:</description></item><item><title>Structured Concurrency (JEP 453): Safe, Readable Concurrent Code</title><link>https://blog.devops-monk.com/tutorials/java21/structured-concurrency/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java21/structured-concurrency/</guid><description>Preview Feature — Requires --enable-preview at compile and runtime. The API stabilized significantly from Java 21 through 24 and will be finalized in a future release.
The Unstructured Concurrency Problem When you submit tasks to an ExecutorService, the tasks are logically related but structurally unconnected — the executor doesn&amp;rsquo;t know they belong together:
// Unstructured concurrency — looks simple, hides serious problems ExecutorService executor = Executors.newCachedThreadPool(); Future&amp;lt;Order&amp;gt; orderFuture = executor.</description></item><item><title>Switch Expressions (JEP 361): Switch as an Expression, Not Just a Statement</title><link>https://blog.devops-monk.com/tutorials/java17/switch-expressions/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java17/switch-expressions/</guid><description>Finalized in Java 14 (JEP 361). Available in all Java 14+ releases, including Java 17. Previous previews: Java 12 (JEP 325) and Java 13 (JEP 354).
The Problem with Switch Statements Java&amp;rsquo;s traditional switch statement has two well-known pain points:
Pain 1: Fall-Through // Java 11 — easy to introduce fall-through bugs switch (day) { case MONDAY: result = &amp;#34;Start of work week&amp;#34;; break; // easy to forget this case TUESDAY: result = &amp;#34;Weekday&amp;#34;; // no break — falls through to WEDNESDAY case case WEDNESDAY: result = &amp;#34;Hump day&amp;#34;; break; default: result = &amp;#34;Other&amp;#34;; } A missing break causes the next case to execute unexpectedly.</description></item><item><title>Testing Kafka Applications: EmbeddedKafka and Testcontainers</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/testing-kafka/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/testing-kafka/</guid><description>Two Testing Strategies Strategy Speed Fidelity Use when @EmbeddedKafka Fast (~2s startup) In-process broker, not 100% identical Unit/integration tests — CI fast path KafkaContainer (Testcontainers) Slower (~10s startup) Real Kafka broker in Docker Acceptance tests, DLT/transaction validation Use both: @EmbeddedKafka for the bulk of tests, KafkaContainer for the smoke suite that validates real-broker behaviour.
Test Dependencies &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;org.springframework.kafka&amp;lt;/groupId&amp;gt; &amp;lt;artifactId&amp;gt;spring-kafka-test&amp;lt;/artifactId&amp;gt; &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt; &amp;lt;/dependency&amp;gt; &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;org.testcontainers&amp;lt;/groupId&amp;gt; &amp;lt;artifactId&amp;gt;kafka&amp;lt;/artifactId&amp;gt; &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt; &amp;lt;/dependency&amp;gt; &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;org.awaitility&amp;lt;/groupId&amp;gt; &amp;lt;artifactId&amp;gt;awaitility&amp;lt;/artifactId&amp;gt; &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt; &amp;lt;/dependency&amp;gt; @EmbeddedKafka — Fast Integration Tests Testing a Producer @SpringBootTest @EmbeddedKafka( partitions = 1, topics = {&amp;#34;orders&amp;#34;}, brokerProperties = {&amp;#34;log.</description></item><item><title>Testing Spring Data JPA: @DataJpaTest, Testcontainers, and Best Practices</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/testing-spring-data-jpa/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/testing-spring-data-jpa/</guid><description>Why Test JPA Code? Unit tests with mocked repositories verify your service logic but nothing about the database. They won&amp;rsquo;t catch:
Wrong column mapping Constraint violations N+1 queries introduced by a new lazy association Transactions that silently don&amp;rsquo;t roll back Queries that fail on the real database but pass on H2 Testing against a real database — or at minimum a database-compatible in-memory store — is necessary. Spring Boot&amp;rsquo;s @DataJpaTest and Testcontainers make this practical.</description></item><item><title>Testing Spring Security: @WithMockUser, MockMvc, and SecurityMockMvc</title><link>https://blog.devops-monk.com/tutorials/spring-security/testing-spring-security/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/testing-spring-security/</guid><description>Why Security Tests Are Non-Negotiable A security configuration that is never tested is a security configuration that is probably wrong. URL patterns have subtle ordering rules. Method security annotations are silently ignored if @EnableMethodSecurity is missing. CSRF tokens must be present in the right form. Testing is the only way to know your authorization rules are actually enforced.
Test Dependencies &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;org.springframework.security&amp;lt;/groupId&amp;gt; &amp;lt;artifactId&amp;gt;spring-security-test&amp;lt;/artifactId&amp;gt; &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt; &amp;lt;/dependency&amp;gt; &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt; &amp;lt;artifactId&amp;gt;spring-boot-starter-test&amp;lt;/artifactId&amp;gt; &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt; &amp;lt;/dependency&amp;gt; @WithMockUser The simplest way to simulate an authenticated user.</description></item><item><title>Text Blocks (JEP 378): Multiline Strings Without the Escape Hell</title><link>https://blog.devops-monk.com/tutorials/java17/text-blocks/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java17/text-blocks/</guid><description>Finalized in Java 15 (JEP 378). Available in all Java 15+ releases, including Java 17. Previous previews: Java 13 (JEP 355) and Java 14 (JEP 368).
The Problem: Embedded Strings in Java Writing multi-line strings in Java has always been painful:
// JSON — a wall of escape sequences and concatenation String json = &amp;#34;{\n&amp;#34; + &amp;#34; \&amp;#34;name\&amp;#34;: \&amp;#34;Alice\&amp;#34;,\n&amp;#34; + &amp;#34; \&amp;#34;role\&amp;#34;: \&amp;#34;engineer\&amp;#34;,\n&amp;#34; + &amp;#34; \&amp;#34;active\&amp;#34;: true\n&amp;#34; + &amp;#34;}&amp;#34;; // SQL — unreadable indentation and newlines String sql = &amp;#34;SELECT u.</description></item><item><title>The N+1 Problem: Detection, Root Cause, and All Solutions</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/n-plus-one-problem-solutions/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/n-plus-one-problem-solutions/</guid><description>What Is the N+1 Problem? The N+1 problem occurs when loading a list of N entities triggers N additional queries to load their associations — one query per entity.
Example: load 50 orders, then access each order&amp;rsquo;s customer:
SELECT * FROM orders; -- 1 query SELECT * FROM customers WHERE id = 1; -- query for order 1&amp;#39;s customer SELECT * FROM customers WHERE id = 2; -- query for order 2&amp;#39;s customer SELECT * FROM customers WHERE id = 3; -- query for order 3&amp;#39;s customer .</description></item><item><title>The Persistence Context: How JPA Tracks Your Entities</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/persistence-context-entity-lifecycle/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/persistence-context-entity-lifecycle/</guid><description>Introduction The persistence context is the single most important concept in JPA. Everything else — lazy loading, dirty checking, transaction scope, detached entity exceptions — only makes sense once you understand what the persistence context is and how it works.
Many JPA bugs come from developers not understanding this concept. Understand it well and the rest of JPA becomes predictable.
What Is the Persistence Context? The persistence context is an in-memory map of entities managed by the current EntityManager.</description></item><item><title>The Security Filter Chain: Every Filter Explained</title><link>https://blog.devops-monk.com/tutorials/spring-security/security-filter-chain/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/security-filter-chain/</guid><description>Every Filter Has a Job The Spring Security filter chain is not a monolith — it&amp;rsquo;s 15-20 individual filters, each responsible for exactly one concern. Understanding each filter&amp;rsquo;s job means you can:
Know exactly where in the chain a request fails Add your own filter in the right position Remove filters you don&amp;rsquo;t need for performance Debug authentication and authorization problems The Complete Filter Order Spring Security defines a strict ordering for its filters.</description></item><item><title>Tooling: JShell, jlink, and Single-File Programs (JEP 222, 282, 330)</title><link>https://blog.devops-monk.com/tutorials/java11/tooling-jshell-jlink/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java11/tooling-jshell-jlink/</guid><description>Overview Java 9 and 11 added three tools that change how you write, test, and deploy Java code:
Tool JEP Release Purpose JShell JEP 222 Java 9 Interactive REPL for Java code jlink JEP 282 Java 9 Build minimal custom JRE images Single-file programs JEP 330 Java 11 Run .java files directly JShell — Interactive Java REPL JShell is Java&amp;rsquo;s Read-Eval-Print Loop: a command-line tool for evaluating Java expressions, statements, methods, and classes interactively, without creating a project.</description></item><item><title>Transaction Boundaries and Common Pitfalls</title><link>https://blog.devops-monk.com/tutorials/spring-data-jpa/transaction-boundaries-pitfalls/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-data-jpa/transaction-boundaries-pitfalls/</guid><description>Transaction Boundaries A transaction boundary is the point where a transaction starts and where it ends. In Spring, boundaries are defined by @Transactional on service methods.
HTTP Request └── Controller (no transaction) └── @Transactional Service method ← transaction OPENS here ├── Repository call 1 ├── Repository call 2 └── method returns ← transaction COMMITS here Everything inside the @Transactional method runs in the same database transaction. The persistence context (first-level cache) lives for the duration of that transaction.</description></item><item><title>Unnamed Classes and Instance Main Methods (JEP 445): Java for Scripts and Beginners</title><link>https://blog.devops-monk.com/tutorials/java21/unnamed-classes/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java21/unnamed-classes/</guid><description>Preview Feature — Requires --enable-preview at compile and runtime.
The Boilerplate Problem Teaching Java to a beginner means explaining class declarations, access modifiers, static, and String[] before they can print &amp;ldquo;Hello, World!&amp;rdquo;:
// Traditional Java — 4 concepts before printing one line public class HelloWorld { public static void main(String[] args) { System.out.println(&amp;#34;Hello, World!&amp;#34;); } } Every word carries meaning, but that meaning is irrelevant until the student understands OOP, the JVM class model, and program entry points.</description></item><item><title>Unnamed Patterns and Variables (JEP 443): Writing Intent-Clear Code</title><link>https://blog.devops-monk.com/tutorials/java21/unnamed-patterns/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java21/unnamed-patterns/</guid><description>Preview Feature — Requires --enable-preview at compile and runtime. The _ identifier was reserved in Java 9; this JEP gives it formal semantics.
The Problem: Forced Naming of Unused Things When pattern matching, you often care about only some components of a matched structure. But the syntax forces you to name everything:
record Point(int x, int y) {} record ColoredPoint(Point point, String color) {} record Box(ColoredPoint cp, int weight) {} // You only care about the color — but must name everything else if (obj instanceof Box(ColoredPoint(Point(int x, int y), String color), int weight)) { System.</description></item><item><title>var Keyword (JEP 286, 323): Local Variable Type Inference</title><link>https://blog.devops-monk.com/tutorials/java11/var-keyword/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java11/var-keyword/</guid><description>What var Does var is a reserved type name (not a keyword) introduced in Java 10 (JEP 286). It instructs the compiler to infer the type of a local variable from its initialiser. The inferred type is fixed at compile time — var does not make Java dynamically typed.
// Before var ArrayList&amp;lt;String&amp;gt; names = new ArrayList&amp;lt;String&amp;gt;(); Map&amp;lt;String, List&amp;lt;Integer&amp;gt;&amp;gt; scores = new HashMap&amp;lt;String, List&amp;lt;Integer&amp;gt;&amp;gt;(); // With var var names = new ArrayList&amp;lt;String&amp;gt;(); var scores = new HashMap&amp;lt;String, List&amp;lt;Integer&amp;gt;&amp;gt;(); After compilation, both forms produce identical bytecode.</description></item><item><title>Vector API (JEP 448): SIMD Computation in Java</title><link>https://blog.devops-monk.com/tutorials/java21/vector-api/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java21/vector-api/</guid><description>Preview Feature in Java 21 — The Vector API has been in preview since Java 16 (JEP 338). JEP 448 is the sixth preview iteration in Java 21. The API is stable and production-usable with --enable-preview; finalization is pending Project Valhalla value types.
What Is SIMD and Why Does It Matter? Modern CPUs can perform the same arithmetic operation on multiple data values in a single instruction. This is called SIMD — Single Instruction, Multiple Data.</description></item><item><title>Virtual Threads (JEP 444): A Million Threads Without the Pain</title><link>https://blog.devops-monk.com/tutorials/java21/virtual-threads/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/java21/virtual-threads/</guid><description>The Platform Thread Problem Every Java thread since Java 1.0 maps 1:1 to an OS thread. OS threads are heavy:
Stack memory: 512 KB – 2 MB per thread (configurable with -Xss, default 512 KB on Linux) Context switch cost: ~1–10 μs per switch (kernel mode transition + cache invalidation) Hard limit: A 64-bit machine with 8 GB RAM can support roughly 8,000–16,000 OS threads before running out of stack memory This limit shapes how Java servers are built.</description></item><item><title>What Is Apache Kafka: Event Streaming From First Principles</title><link>https://blog.devops-monk.com/tutorials/spring-kafka/what-is-apache-kafka/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-kafka/what-is-apache-kafka/</guid><description>The Problem Kafka Solves Imagine an e-commerce platform. A customer places an order. What needs to happen next?
Inventory must be reserved Payment must be charged A confirmation email must be sent The warehouse must be notified to pick and pack Analytics must record the sale Fraud detection must evaluate the transaction One request. Six downstream systems. In a traditional REST architecture, the Order Service calls each of those six services directly — synchronously, one after another.</description></item><item><title>X.509 Certificate Authentication</title><link>https://blog.devops-monk.com/tutorials/spring-security/x509-certificate-authentication/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-security/x509-certificate-authentication/</guid><description>What Is X.509 Authentication? X.509 authentication (also called mutual TLS or mTLS) uses digital certificates instead of passwords. The client presents a certificate during the TLS handshake. The server verifies the certificate against a trusted Certificate Authority (CA) and extracts the user identity from the certificate&amp;rsquo;s Common Name (CN) or Subject Alternative Name (SAN).
sequenceDiagram participant Client as Client (with certificate) participant Server as Spring Boot Server Client->>Server: TLS ClientHello Server->>Client: TLS ServerHello + Server Certificate Server->>Client: CertificateRequest (ask for client cert) Client->>Server: Client Certificate + CertificateVerify Server->>Server: Verify against trusted CA Server->>Server: Extract CN from certificate: "</description></item><item><title>Advanced Processing: CompositeItemProcessor, External APIs, and Async Processing</title><link>https://blog.devops-monk.com/tutorials/spring-batch/advanced-processors/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/advanced-processors/</guid><description>Introduction Real batch jobs often need more than one transformation step per item. You might validate first, then normalize, then enrich, then convert to the output type. CompositeItemProcessor chains multiple single-responsibility processors together. For I/O-bound enrichment steps, AsyncItemProcessor runs processing concurrently on a thread pool — giving you parallelism without rewriting your step as multi-threaded.
CompositeItemProcessor CompositeItemProcessor chains a list of processors. The output of each processor becomes the input to the next.</description></item><item><title>Advanced Writers: JpaItemWriter, CompositeItemWriter, and Custom Writers</title><link>https://blog.devops-monk.com/tutorials/spring-batch/advanced-writers/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/advanced-writers/</guid><description>Introduction The previous article covered the two most common writers: FlatFileItemWriter for files and JdbcBatchItemWriter for database inserts. This article covers advanced scenarios:
Persisting JPA entities with JpaItemWriter Writing to multiple destinations simultaneously with CompositeItemWriter Routing items to different writers based on content with ClassifierCompositeItemWriter Building custom writers for REST APIs, message queues, and cloud storage Combining a writer with a post-step cleanup tasklet JpaItemWriter JpaItemWriter calls EntityManager.merge() on each item.</description></item><item><title>Chunk-Oriented Processing: The Core Spring Batch Pattern</title><link>https://blog.devops-monk.com/tutorials/spring-batch/chunk-oriented-processing/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/chunk-oriented-processing/</guid><description>The read-process-write loop is at the heart of almost every Spring Batch job. Understanding exactly how it works — where transactions begin and end, what gets rolled back on failure, and how Spring Batch knows where to restart — makes everything else in the framework click into place.
This article goes deep on chunk-oriented processing: the execution model, the three interfaces, the counters that track progress, and how to size chunks correctly.</description></item><item><title>Configuring Jobs and Steps: Flows, Decisions, and Conditional Execution</title><link>https://blog.devops-monk.com/tutorials/spring-batch/job-step-configuration/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/job-step-configuration/</guid><description>Introduction So far every example has had a single step. Real batch jobs usually have multiple steps — validate input, import data, generate a report, send a notification, clean up temp files. Spring Batch&amp;rsquo;s JobBuilder DSL lets you compose these steps into flows with conditional branching, parallel execution, and decision logic.
This article covers:
Linear multi-step jobs Conditional step transitions using ExitStatus JobExecutionDecider for runtime branching Parallel flows with split Nested jobs with FlowStep The Flow abstraction for reusable step sequences Linear Multi-Step Job The simplest multi-step job runs steps in sequence.</description></item><item><title>How Spring Boot Auto-Configuration Actually Works (Behind the Magic)</title><link>https://blog.devops-monk.com/2026/05/spring-boot-auto-configuration-deep-dive/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/2026/05/spring-boot-auto-configuration-deep-dive/</guid><description>&amp;ldquo;Spring Boot is magic&amp;rdquo; is something you hear a lot. Add spring-boot-starter-data-jpa and suddenly you have a working DataSource, a JpaTransactionManager, and a LocalContainerEntityManagerFactoryBean — without writing a single @Bean method. Understanding how this actually works turns the magic into a tool you can control, debug, and extend.
The Entry Point: @EnableAutoConfiguration @SpringBootApplication is a shorthand for three annotations:
@Configuration @EnableAutoConfiguration // this is the one that matters here @ComponentScan public class MyApplication { public static void main(String[] args) { SpringApplication.</description></item><item><title>Introduction to Spring Batch: What, Why, and Architecture</title><link>https://blog.devops-monk.com/tutorials/spring-batch/introduction-to-spring-batch/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/introduction-to-spring-batch/</guid><description>Every application has a class of work that doesn&amp;rsquo;t fit the request-response model: process 2 million orders overnight, generate 500,000 monthly statements, migrate 10 years of legacy data before Monday morning. This work needs to be reliable, restartable after failures, and fast enough to finish in the available window. That&amp;rsquo;s what Spring Batch is built for.
This article covers what Spring Batch is, when to use it, and how its architecture works.</description></item><item><title>Introduction to Spring Boot — What It Is and Why It Exists</title><link>https://blog.devops-monk.com/tutorials/spring-boot/introduction-to-spring-boot/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/introduction-to-spring-boot/</guid><description>You want to build a Java web application or REST API. You&amp;rsquo;ve heard everyone uses Spring Boot. But what is it exactly, and why does it exist?
This article answers that — clearly — before writing a single line of code.
What Is Spring Boot? Spring Boot is an opinionated framework for building Spring-based Java applications with minimal configuration.
Break that down:
Spring-based — it sits on top of the Spring Framework, which has been the dominant Java application framework since 2003 Minimal configuration — you describe what you want (a web app, a database connection), and Spring Boot figures out how to set it up Opinionated — it makes sensible default choices so you don&amp;rsquo;t have to make every decision yourself The one-line version: Spring Boot lets you go from a blank project to a running application in minutes, with production-ready defaults built in.</description></item><item><title>JobParameters, ExecutionContext, and Job Restartability</title><link>https://blog.devops-monk.com/tutorials/spring-batch/job-parameters-execution-context/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/job-parameters-execution-context/</guid><description>Introduction Two mechanisms let you pass information into and through a Spring Batch job:
JobParameters — input values provided at launch time (a date, a file path, a run ID). They are immutable and persisted to BATCH_JOB_EXECUTION_PARAMS. ExecutionContext — a key-value map that steps can read and write during execution. It is persisted after each chunk commit, enabling restartability. Understanding both is essential for building jobs that can be safely re-run, restarted after failure, and parameterised for different data sets.</description></item><item><title>JobRepository and Batch Metadata: How Spring Batch Tracks Everything</title><link>https://blog.devops-monk.com/tutorials/spring-batch/job-repository-batch-metadata/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/job-repository-batch-metadata/</guid><description>Introduction Every time Spring Batch runs a job it records the run&amp;rsquo;s history in a set of relational tables. This metadata is not optional — it is what makes Spring Batch reliable. Without it there would be no restart capability, no duplicate-run prevention, and no audit trail. Understanding the metadata layer is essential for debugging failures, building monitoring dashboards, and designing restartable jobs.
In this article you will learn:
The role of JobRepository and JobExplorer in the Spring Batch architecture The six metadata tables — their schema, purpose, and relationships How Spring Batch uses these tables to enable restartability How to query batch history directly in MySQL How to access metadata programmatically with JobExplorer Key changes in Spring Batch 5 (removed MapJobRepositoryFactoryBean, new testing approach) All examples use Spring Boot 3.</description></item><item><title>Listeners: Hooking into Job, Step, and Chunk Lifecycle Events</title><link>https://blog.devops-monk.com/tutorials/spring-batch/listeners/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/listeners/</guid><description>Introduction Spring Batch emits lifecycle events at every stage of execution — before and after a job runs, before and after each step, before and after each chunk, and before and after each individual read/process/write call. Listeners let you hook into these events without modifying your core batch logic.
Common uses:
Log start/end times and item counts Send success/failure notifications (Slack, email, PagerDuty) Publish metrics to Prometheus or CloudWatch Log every skipped item to a dead-letter table Reset resources before a step begins Listener Hierarchy Job ├── JobExecutionListener beforeJob / afterJob │ └── Step ├── StepExecutionListener beforeStep / afterStep ├── ChunkListener beforeChunk / afterChunk / afterChunkError ├── ItemReadListener beforeRead / afterRead / onReadError ├── ItemProcessListener beforeProcess / afterProcess / onProcessError └── ItemWriteListener beforeWrite / afterWrite / onWriteError └── SkipListener onSkipInRead / onSkipInWrite / onSkipInProcess JobExecutionListener @Component public class ImportJobListener implements JobExecutionListener { private static final Logger log = LoggerFactory.</description></item><item><title>Null Safety with JSpecify</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-jspecify-null-safety/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-jspecify-null-safety/</guid><description>NullPointerException is the most common runtime error in Java. JSpecify is the standardized solution — annotate your APIs and get compile-time null safety. Spring Framework 7 adopts JSpecify throughout, and your code can too.
The Problem // Is this safe? You don&amp;#39;t know without reading the implementation. public Order findById(UUID id) { return repository.findById(id).orElse(null); // returns null! } // The caller has no warning: Order order = service.findById(id); order.cancel(); // ← NullPointerException if not found Without null annotations, every method call is potentially null — you write defensive code everywhere, or you miss a case and get a NPE in production.</description></item><item><title>Reading Flat Files: CSV, Fixed-Width, and Delimited with FlatFileItemReader</title><link>https://blog.devops-monk.com/tutorials/spring-batch/reading-flat-files/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/reading-flat-files/</guid><description>Introduction Flat files — CSV exports, fixed-width mainframe feeds, pipe-delimited data dumps — are the most common input source for batch jobs. Spring Batch&amp;rsquo;s FlatFileItemReader handles all of them. It is restartable out of the box: it persists its line-number position in the ExecutionContext so that a restarted job resumes exactly where it crashed.
In this article you will build a complete order-import job that reads a CSV file and inserts rows into a MySQL orders table.</description></item><item><title>Reading from External Sources: REST APIs, S3, and Custom ItemReaders</title><link>https://blog.devops-monk.com/tutorials/spring-batch/reading-external-sources/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/reading-external-sources/</guid><description>Introduction Not all batch input comes from files or databases. You may need to pull orders from an e-commerce API, sync products from a supplier feed, or process CSV exports stored in Amazon S3. Spring Batch provides MultiResourceItemReader for S3 files and a clean ItemReader interface for anything else.
In this article you will build:
A custom ItemReader that pages through a REST API An S3 reader that downloads files on demand A composite reader that merges multiple sources into one step The ItemReader Contract The entire ItemReader interface is one method:</description></item><item><title>Reading from MySQL: JdbcCursorItemReader and JdbcPagingItemReader</title><link>https://blog.devops-monk.com/tutorials/spring-batch/reading-from-mysql/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/reading-from-mysql/</guid><description>Introduction Most real batch jobs read from a database, not a file. Spring Batch provides two JDBC readers for this:
Reader Strategy Thread-safe Use when JdbcCursorItemReader Open a server-side cursor, stream rows No Single-threaded step, huge result sets JdbcPagingItemReader Execute LIMIT / OFFSET queries in a loop Yes Multi-threaded steps, sorted data Both handle restartability automatically and both work with any DataSource — including MySQL.
JdbcCursorItemReader How it works The reader opens a single JDBC ResultSet on Step.</description></item><item><title>Reading with JPA: JpaPagingItemReader and Entity-Based Reading</title><link>https://blog.devops-monk.com/tutorials/spring-batch/reading-with-jpa/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/reading-with-jpa/</guid><description>Introduction When your application already uses JPA/Hibernate, JpaPagingItemReader lets you read data using JPQL queries and mapped entities instead of raw JDBC. You get the full object graph, type-safe queries, and familiar entity lifecycle — but you also inherit JPA&amp;rsquo;s pitfalls: the N+1 problem, session-per-read overhead, and first-level cache growth.
This article covers:
When to choose JpaPagingItemReader over JdbcPagingItemReader Setting up the reader with JPQL and named queries Fetching associations to avoid N+1 Clearing the persistence context to prevent memory leaks A complete order-processing example with MySQL When to Use JpaPagingItemReader Use it when:</description></item><item><title>Retry Logic: Handling Transient Failures Gracefully</title><link>https://blog.devops-monk.com/tutorials/spring-batch/retry-logic/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/retry-logic/</guid><description>Introduction Batch jobs interact with databases, REST APIs, and file systems — all of which fail transiently. A MySQL deadlock resolves itself in milliseconds. A network timeout to an external service clears up in seconds. Retrying these transient failures automatically is far better than failing the entire job and requiring a manual restart.
Spring Batch has built-in retry support at the step level, integrated with its transaction management. This article covers everything you need to configure robust retry behaviour.</description></item><item><title>Skip Logic, Dead Letter Patterns, and Job Restart Strategies</title><link>https://blog.devops-monk.com/tutorials/spring-batch/skip-logic-restartability/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/skip-logic-restartability/</guid><description>Introduction Retry handles transient failures. Skip handles permanent ones — bad data rows, constraint violations, malformed records that will never succeed no matter how many times you retry. Skip logic lets your job continue processing good records while recording bad ones for human review.
This article covers:
Configuring skip for specific exception types Custom SkipPolicy for fine-grained control Dead-letter table pattern for tracking skipped items Stopping a job intentionally vs failing it Handling abandoned executions Designing jobs that restart safely after any failure Basic Skip Configuration return new StepBuilder(&amp;#34;importOrdersStep&amp;#34;, jobRepository) .</description></item><item><title>Spring Boot + Spring Batch Setup: Your First Complete Batch Project</title><link>https://blog.devops-monk.com/tutorials/spring-batch/spring-boot-spring-batch-setup/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/spring-boot-spring-batch-setup/</guid><description>Article 2 showed how chunk processing works conceptually. This article sets up the real project you&amp;rsquo;ll build on for the rest of the series — complete Maven configuration, MySQL metadata tables, multiple ways to trigger jobs, and correct use of JobParameters. Everything in this article is production-ready from the start.
Project Dependencies Add spring-boot-starter-batch to your Maven project. That one starter brings in everything you need.
&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt; &amp;lt;project xmlns=&amp;#34;http://maven.</description></item><item><title>Spring Boot 2.x → 3.x → 4.x Migration: The Definitive Checklist</title><link>https://blog.devops-monk.com/2026/05/spring-boot-migration-guide/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/2026/05/spring-boot-migration-guide/</guid><description>Many teams are still running Spring Boot 2.7.x. Spring Boot 2.x reached end of life in November 2023, which means no more security patches. The jump to 4.0 is two generations, and the breaking changes are real — but they are also well-documented and mostly automatable.
This guide walks through the migration in stages: 2.x → 3.0 first, then 3.x incremental updates, then 4.0. Each section lists what breaks and how to fix it.</description></item><item><title>Spring Boot 4.0: Everything That Changed (Complete Guide)</title><link>https://blog.devops-monk.com/2026/05/spring-boot-4-complete-guide/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/2026/05/spring-boot-4-complete-guide/</guid><description>Spring Boot 4.0 was released on November 20, 2025. It is built on Spring Framework 7 and represents the most significant shift in the Spring ecosystem since the Jakarta EE migration in Spring Boot 3. The headline change is full modularisation — the single spring-boot-autoconfigure JAR has been split into 70+ granular modules. But that is just the start.
This guide covers every change that matters, what breaks on upgrade, and what is genuinely new and useful.</description></item><item><title>Spring Boot Project Structure Explained</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-project-structure/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-project-structure/</guid><description>A Spring Boot project looks simple on the surface but has a specific structure with specific conventions. Understanding it upfront saves hours of confusion later.
The Standard Layout order-service/ ├── pom.xml ├── mvnw # Maven Wrapper script (Unix) ├── mvnw.cmd # Maven Wrapper script (Windows) ├── .mvn/ │ └── wrapper/ │ └── maven-wrapper.properties └── src/ ├── main/ │ ├── java/ │ │ └── com/devopsmonk/orderservice/ │ │ └── OrderServiceApplication.java │ └── resources/ │ ├── application.</description></item><item><title>Spring Boot vs Quarkus in 2026: An Honest Benchmarked Comparison</title><link>https://blog.devops-monk.com/2026/05/spring-boot-vs-quarkus/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/2026/05/spring-boot-vs-quarkus/</guid><description>Every year, someone asks: &amp;ldquo;Should we use Spring Boot or Quarkus?&amp;rdquo; In 2026, both frameworks are mature, both run natively, and both work well on Kubernetes. The differences come down to developer experience, ecosystem size, and specific performance characteristics.
This is an honest comparison with real numbers, not marketing claims.
The Frameworks at a Glance Spring Boot 4 (November 2025): Built on Spring Framework 7. The de-facto standard for enterprise Java.</description></item><item><title>Tasklets: Running Non-Chunk Operations in Batch Jobs</title><link>https://blog.devops-monk.com/tutorials/spring-batch/tasklets/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/tasklets/</guid><description>Introduction Not every step in a batch job reads-processes-writes a stream of items. Sometimes you need to:
Delete yesterday&amp;rsquo;s temp files before reading today&amp;rsquo;s data Truncate a staging table before loading new data Call a stored procedure to aggregate results Send an email or Slack notification after processing Execute a DDL statement to add an index These operations do not have items — they are single units of work. Spring Batch handles them with the Tasklet interface.</description></item><item><title>Testing Spring Batch Jobs: Unit Tests, Integration Tests, and @SpringBatchTest</title><link>https://blog.devops-monk.com/tutorials/spring-batch/testing-spring-batch/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/testing-spring-batch/</guid><description>Introduction Spring Batch jobs are code — they need tests. Without tests you will catch errors in production. With tests you catch them in CI.
Spring Batch has a dedicated testing module that provides @SpringBatchTest, JobLauncherTestUtils, and JobRepositoryTestUtils. These tools let you:
Run a complete job in a test and assert on its BatchStatus Launch individual steps and inspect StepExecution counters Test readers, processors, and writers in complete isolation This article covers all three levels: unit, integration, and database integration with Testcontainers.</description></item><item><title>Top 50 Spring Boot Interview Questions for 2026 (With Detailed Answers)</title><link>https://blog.devops-monk.com/2026/05/spring-boot-interview-questions/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/2026/05/spring-boot-interview-questions/</guid><description>These are the questions that actually come up in Spring Boot interviews — at startups, scale-ups, and large enterprises. Each answer explains the concept clearly, with the level of depth an interviewer expects from a mid-level or senior developer.
Questions are grouped by topic. For junior roles, focus on sections 1–3. For senior roles, everything here is fair game.
Section 1: Core Fundamentals Q1. What is the difference between Spring and Spring Boot?</description></item><item><title>Transforming Data with ItemProcessor: Validation, Filtering, and Enrichment</title><link>https://blog.devops-monk.com/tutorials/spring-batch/item-processor/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/item-processor/</guid><description>Introduction ItemProcessor&amp;lt;I, O&amp;gt; sits between the reader and the writer. It receives one item at a time and returns a transformed item — or null to filter the item out entirely. Processors are optional: if your job reads and writes the same type with no transformation needed, you can omit them.
The processor interface is one method:
public interface ItemProcessor&amp;lt;I, O&amp;gt; { O process(I item) throws Exception; } Return a processed item to pass it to the writer.</description></item><item><title>What's New in Spring Boot 4.0</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-4-whats-new/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-4-whats-new/</guid><description>Spring Boot 4.0 (November 2025) is a major release built on Spring Framework 7 and Java 17+. It&amp;rsquo;s the most significant Spring release since Boot 3&amp;rsquo;s Jakarta EE migration. This article covers every change that affects a practicing developer.
Minimum Requirements Spring Boot 3.x Spring Boot 4.0 Java 17 17 (baseline), 21 recommended Spring Framework 6.x 7.x Jakarta EE 10 11 Tomcat 10.x 11.x Hibernate 6.x 7.x Gradle (if used) 7.</description></item><item><title>Writing to Files and Databases: FlatFileItemWriter and JdbcBatchItemWriter</title><link>https://blog.devops-monk.com/tutorials/spring-batch/writing-files-databases/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-batch/writing-files-databases/</guid><description>Introduction After reading and processing data, your batch job needs to write results somewhere. Spring Batch provides two essential writers:
FlatFileItemWriter — writes items to CSV or any delimited/formatted flat file JdbcBatchItemWriter — writes items to a database using JDBC batch inserts/updates Both are transactional and restartable. This article covers both in depth, plus transaction semantics you must understand to avoid duplicates and partial writes.
How Writers Work in Spring Batch Writers receive a Chunk&amp;lt;O&amp;gt; — a list of all items processed in the current transaction.</description></item><item><title>Your First Spring Boot Application</title><link>https://blog.devops-monk.com/tutorials/spring-boot/first-spring-boot-application/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/first-spring-boot-application/</guid><description>In this article you&amp;rsquo;ll create a Spring Boot application, add a REST endpoint, and run it — all in under 10 minutes. Then you&amp;rsquo;ll understand exactly what each piece does.
What you&amp;rsquo;ll build: A Spring Boot app that responds to GET /hello with &amp;quot;Hello, Spring Boot!&amp;quot;.
Prerequisites JDK 21 or higher installed (java -version to verify) Maven 3.9+ installed (mvn -version to verify) An IDE — IntelliJ IDEA (recommended), VS Code with Java extension, or Eclipse Step 1: Generate the Project Go to start.</description></item></channel></rss>