<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Spring-Boot on Devops Monk</title><link>https://blog.devops-monk.com/tags/spring-boot/</link><description>Recent content in Spring-Boot 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/spring-boot/index.xml" rel="self" type="application/rss+xml"/><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>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>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>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>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>@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>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>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>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>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>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>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>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>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>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>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>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>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>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 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>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>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>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>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>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>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>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>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>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>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 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>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>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>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>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>API Documentation with OpenAPI and Springdoc</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-openapi-springdoc/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-openapi-springdoc/</guid><description>Good API documentation is non-negotiable. Springdoc reads your Spring MVC code and auto-generates interactive OpenAPI 3.1 documentation — no separate doc files to maintain.
Setup &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;org.springdoc&amp;lt;/groupId&amp;gt; &amp;lt;artifactId&amp;gt;springdoc-openapi-starter-webmvc-ui&amp;lt;/artifactId&amp;gt; &amp;lt;version&amp;gt;2.5.0&amp;lt;/version&amp;gt; &amp;lt;/dependency&amp;gt; Start the app and visit:
Swagger UI: http://localhost:8080/swagger-ui.html OpenAPI JSON: http://localhost:8080/v3/api-docs OpenAPI YAML: http://localhost:8080/v3/api-docs.yaml Springdoc scans your @RestController classes and generates the spec automatically. Zero configuration needed for basic docs.
Configuring the API Info @Configuration public class OpenApiConfig { @Bean public OpenAPI openAPI() { return new OpenAPI() .</description></item><item><title>API Gateway with Spring Cloud Gateway</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-cloud-gateway/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-cloud-gateway/</guid><description>The API gateway is the single entry point for all client traffic. It handles routing to downstream services, authentication, rate limiting, and request/response transformation — so individual services don&amp;rsquo;t have to.
Setup &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;org.springframework.cloud&amp;lt;/groupId&amp;gt; &amp;lt;artifactId&amp;gt;spring-cloud-starter-gateway&amp;lt;/artifactId&amp;gt; &amp;lt;/dependency&amp;gt; &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;org.springframework.cloud&amp;lt;/groupId&amp;gt; &amp;lt;artifactId&amp;gt;spring-cloud-starter-netflix-eureka-client&amp;lt;/artifactId&amp;gt; &amp;lt;/dependency&amp;gt; Spring Cloud Gateway is reactive (WebFlux-based) — don&amp;rsquo;t include spring-boot-starter-web.
Route Configuration YAML Configuration spring: application: name: api-gateway cloud: gateway: routes: - id: order-service uri: lb://order-service # lb:// = load-balanced via Eureka predicates: - Path=/api/orders/** filters: - StripPrefix=0 # don&amp;#39;t strip path prefix - AddRequestHeader=X-Gateway-Source, api-gateway - id: customer-service uri: lb://customer-service predicates: - Path=/api/customers/** filters: - StripPrefix=0 - id: payment-service uri: lb://payment-service predicates: - Path=/api/payments/** filters: - StripPrefix=0 # Versioned routing - id: order-service-v2 uri: lb://order-service-v2 predicates: - Path=/api/v2/orders/** - Header=X-API-Version, 2 default-filters: - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin Programmatic Route Configuration @Configuration public class GatewayRouteConfig { @Bean public RouteLocator routeLocator(RouteLocatorBuilder builder) { return builder.</description></item><item><title>API Versioning in Spring Boot 4</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-api-versioning/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-api-versioning/</guid><description>APIs evolve. Adding fields is safe — removing or changing fields breaks clients. Versioning gives you a path to evolve the API without breaking existing integrations.
When You Need Versioning You need a new API version when:
Removing a field from a response Changing a field&amp;rsquo;s type or semantics Changing URL structure significantly Breaking backward-incompatible business logic changes You don&amp;rsquo;t need a new version for:
Adding new optional fields (non-breaking) Adding new endpoints Bug fixes that don&amp;rsquo;t change the contract Strategy 1: URL Path Versioning The most common and explicit approach:</description></item><item><title>Application Configuration: Properties, YAML, and Profiles</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-configuration-profiles/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-configuration-profiles/</guid><description>Every real application needs different configuration for different environments — a local database for dev, a connection pool for staging, a secret manager for prod. This article covers everything Spring Boot gives you to handle this cleanly.
application.properties vs application.yml Spring Boot reads configuration from src/main/resources/application.properties (or .yml) by default. Both formats express the same thing:
application.properties:
server.port=8080 spring.datasource.url=jdbc:postgresql://localhost:5432/orders spring.datasource.username=app spring.datasource.password=secret spring.jpa.show-sql=false application.yml:
server: port: 8080 spring: datasource: url: jdbc:postgresql://localhost:5432/orders username: app password: secret jpa: show-sql: false YAML is generally preferred for nested properties — it&amp;rsquo;s less repetitive.</description></item><item><title>Async Processing with @Async and Virtual Threads</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-async-virtual-threads/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-async-virtual-threads/</guid><description>Not every operation needs to complete before the response returns. Sending an email, generating a report, publishing an event — these can run in the background. Async processing keeps request latency low while the work continues.
@Async — Fire and Forget @SpringBootApplication @EnableAsync public class OrderServiceApplication { } @Service @Slf4j public class NotificationService { @Async // runs in a separate thread public void sendOrderConfirmation(Order order) { log.info(&amp;#34;Sending confirmation for order {}&amp;#34;, order.</description></item><item><title>Bean Validation: @Valid, Custom Validators, and Error Messages</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-bean-validation/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-bean-validation/</guid><description>Every request that enters your API needs validation. Without it, invalid data propagates through your application and produces confusing errors deep in the stack. This article covers how to validate data at the API boundary using Jakarta Bean Validation.
Setup &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-validation&amp;lt;/artifactId&amp;gt; &amp;lt;/dependency&amp;gt; This includes Hibernate Validator — the reference implementation of Jakarta Bean Validation 3.0.
Built-in Constraints Annotate fields in your DTO with constraint annotations:
public record CreateOrderRequest( @NotNull(message = &amp;#34;Customer ID is required&amp;#34;) UUID customerId, @NotEmpty(message = &amp;#34;Order must contain at least one item&amp;#34;) @Size(max = 50, message = &amp;#34;Order cannot have more than {max} items&amp;#34;) List&amp;lt;@Valid OrderItemRequest&amp;gt; items, @Valid ShippingAddressRequest shippingAddress, @Size(max = 20, message = &amp;#34;Promo code cannot exceed {max} characters&amp;#34;) String promoCode ) {} public record OrderItemRequest( @NotNull UUID productId, @Positive(message = &amp;#34;Quantity must be positive&amp;#34;) @Max(value = 999, message = &amp;#34;Cannot order more than {value} units of a product&amp;#34;) int quantity, @NotNull @Positive BigDecimal unitPrice ) {} public record ShippingAddressRequest( @NotBlank(message = &amp;#34;Address line 1 is required&amp;#34;) @Size(max = 100) String line1, @Size(max = 100) String line2, @NotBlank @Size(max = 50) String city, @NotBlank @Pattern(regexp = &amp;#34;[A-Z]{2}&amp;#34;, message = &amp;#34;Country must be a 2-letter ISO code&amp;#34;) String country, @NotBlank @Pattern(regexp = &amp;#34;\\w{3,10}&amp;#34;, message = &amp;#34;Invalid postal code format&amp;#34;) String postalCode ) {} Common Constraint Annotations Annotation Validates @NotNull Value is not null @NotEmpty String/collection not null and not empty @NotBlank String not null, not empty, not just whitespace @Size(min, max) String length or collection size @Min(value) Number ≥ value @Max(value) Number ≤ value @Positive Number &amp;gt; 0 @PositiveOrZero Number ≥ 0 @Negative Number &amp;lt; 0 @Pattern(regexp) String matches regex @Email Valid email format @Past Date is in the past @Future Date is in the future @DecimalMin(value) Decimal ≥ value (as string) @AssertTrue Boolean is true @AssertFalse Boolean is false Triggering Validation with @Valid Add @Valid to the controller parameter to trigger validation:</description></item><item><title>Building a Modular Monolith with Spring Modulith</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-spring-modulith/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-spring-modulith/</guid><description>Microservices solve organizational and scalability problems — but they add operational complexity. Most applications don&amp;rsquo;t need that complexity. A modular monolith gives you clean boundaries and loose coupling without the distributed systems overhead. Spring Modulith enforces those boundaries.
The Problem with Unstructured Monoliths Without explicit boundaries, every part of the codebase can talk to every other part:
// OrderService calling PaymentRepository directly — skipping the Payment module @Service public class OrderService { @Autowired PaymentRepository paymentRepository; // ← wrong @Autowired NotificationService notificationService; // ← wrong @Autowired AnalyticsService analyticsService; // ← wrong } This creates hidden coupling.</description></item><item><title>Building Your First REST API with Spring Boot</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-rest-api/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-rest-api/</guid><description>Time to build something real. In this article you&amp;rsquo;ll create a fully functional REST API for the order-service — create, read, update, and delete orders over HTTP.
Project Setup Start with these dependencies at start.spring.io:
&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-web&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-validation&amp;lt;/artifactId&amp;gt; &amp;lt;/dependency&amp;gt; spring-boot-starter-web includes:
Embedded Tomcat (no WAR deployment needed) Spring MVC (the web framework) Jackson (JSON serialization) The Request Processing Pipeline Before writing code, understand how Spring MVC handles a request:</description></item><item><title>Caching with Caffeine and Redis</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-caching/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-caching/</guid><description>Caching sits between your application and the database. A cache hit returns data in microseconds; a database query takes milliseconds. For frequently-read, infrequently-changed data, caching is the highest-leverage performance improvement.
Spring Cache Abstraction Spring&amp;rsquo;s cache abstraction lets you add caching with annotations — the backing store (Caffeine, Redis, Hazelcast) is swappable:
@Service @RequiredArgsConstructor public class ProductService { private final ProductRepository repository; @Cacheable(&amp;#34;products&amp;#34;) // cache the result public Product findById(UUID id) { return repository.</description></item><item><title>Centralized Configuration with Spring Cloud Config Server</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-cloud-config-server/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-cloud-config-server/</guid><description>Managing configuration for 10 services across 3 environments means 30 separate config files. Spring Cloud Config Server centralizes all of them — one place to change a database URL, one place to rotate secrets, and services pick up changes without redeployment.
Config Server Setup &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;org.springframework.cloud&amp;lt;/groupId&amp;gt; &amp;lt;artifactId&amp;gt;spring-cloud-config-server&amp;lt;/artifactId&amp;gt; &amp;lt;/dependency&amp;gt; @SpringBootApplication @EnableConfigServer public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } } # application.yml — config-server server: port: 8888 spring: application: name: config-server cloud: config: server: git: uri: https://github.</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>CRUD Operations with JpaRepository</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-data-jpa-crud/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-data-jpa-crud/</guid><description>You have entities and repositories set up. Now let&amp;rsquo;s work through every data operation in depth — create, read, update, delete — and the JPA mechanics behind each.
Create: save() @Service @RequiredArgsConstructor @Transactional public class OrderService { private final OrderRepository orderRepository; public Order createOrder(CreateOrderRequest request) { Order order = new Order(); order.setCustomerId(request.customerId()); order.setOrderNumber(generateOrderNumber()); order.setStatus(OrderStatus.PENDING); request.items().forEach(itemReq -&amp;gt; { OrderItem item = new OrderItem(); item.setProductId(itemReq.productId()); item.setQuantity(itemReq.quantity()); item.setUnitPrice(itemReq.unitPrice()); order.addItem(item); // manages bidirectional relationship }); return orderRepository.</description></item><item><title>Database Migrations with Flyway</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-flyway-migrations/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-flyway-migrations/</guid><description>Never use spring.jpa.hibernate.ddl-auto=update in production. It&amp;rsquo;s unpredictable, irreversible, and can corrupt data. Flyway gives you version-controlled, audited, reproducible schema changes.
Why Flyway? Every database change runs as a versioned SQL script. Flyway tracks which scripts have run in a flyway_schema_history table. When the app starts:
Flyway reads all migration files Checks which have already run (by checking the history table) Runs any new ones, in order If the current state doesn&amp;rsquo;t match the expected state → fails fast Benefits:</description></item><item><title>Dockerizing Spring Boot Applications</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-docker/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-docker/</guid><description>Packaging your Spring Boot application as a Docker container is the standard way to deploy it — to Kubernetes, cloud platforms, or any container runtime. This article covers building production-quality images.
The Naive Dockerfile (Don&amp;rsquo;t Use This) FROM eclipse-temurin:21-jdk COPY target/order-service.jar app.jar ENTRYPOINT [&amp;#34;java&amp;#34;, &amp;#34;-jar&amp;#34;, &amp;#34;app.jar&amp;#34;] Problems:
600MB+ image (JDK, not JRE) No layer caching — every code change rebuilds the whole JAR layer Runs as root (security risk) No health check Layered JARs (Better Cache Utilization) Spring Boot 3 creates layered JARs by default.</description></item><item><title>DTOs and Response Shaping</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-dto-response/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-dto-response/</guid><description>Every beginner makes the same mistake: returning JPA entities directly from REST controllers. This article explains why that&amp;rsquo;s dangerous, and how to design clean DTOs that make your API stable, secure, and maintainable.
Why Not Return Entities Directly? Consider this:
@GetMapping(&amp;#34;/{id}&amp;#34;) public Order getOrder(@PathVariable UUID id) { return orderRepository.findById(id).orElseThrow(); // Entity returned directly } Problems with this:
1. Serialization of lazy-loaded relationships crashes
@Entity public class Order { @OneToMany(fetch = FetchType.</description></item><item><title>Entity Relationships: @OneToMany, @ManyToOne, @ManyToMany</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-data-jpa-relationships/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-data-jpa-relationships/</guid><description>Relationships are the trickiest part of JPA. A wrong cascade type or a missing mappedBy causes subtle bugs that appear in production. This article covers every relationship type with real examples and the pitfalls to avoid.
Relationship Fundamentals JPA relationships can be:
Direction: Unidirectional (one side knows about the other) or Bidirectional (both sides know each other) Cardinality: @OneToOne, @OneToMany, @ManyToOne, @ManyToMany Fetch: LAZY (load on access) or EAGER (load immediately) Ownership: The side with the foreign key column is the owner Default fetch types:</description></item><item><title>Event-Driven Spring Boot: Transactional Outbox Pattern with Kafka</title><link>https://blog.devops-monk.com/2026/05/spring-boot-kafka-transactional-outbox/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/2026/05/spring-boot-kafka-transactional-outbox/</guid><description>Publishing an event to Kafka after saving to the database looks simple. It has a subtle, dangerous flaw: if the Kafka publish fails after the DB commit, or the app crashes between the two, your event is lost and your data is inconsistent.
The Transactional Outbox Pattern solves this by writing the event to the database in the same transaction as the business data, then publishing to Kafka separately. This guide covers the pattern, the implementation, and idempotent consumers.</description></item><item><title>Externalized Configuration with @ConfigurationProperties</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-configuration-properties/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-configuration-properties/</guid><description>@ConfigurationProperties binds external configuration to a typed Java class — replacing scattered @Value annotations with a single, validated, testable object.
Why @ConfigurationProperties Over @Value // @Value — scattered, no type safety, no validation @Service public class PaymentService { @Value(&amp;#34;${payment.gateway.url}&amp;#34;) private String gatewayUrl; @Value(&amp;#34;${payment.gateway.timeout:5000}&amp;#34;) private int timeoutMs; @Value(&amp;#34;${payment.gateway.api-key}&amp;#34;) private String apiKey; @Value(&amp;#34;${payment.gateway.max-retries:3}&amp;#34;) private int maxRetries; } // @ConfigurationProperties — one place, typed, validated @Service public class PaymentService { private final PaymentProperties properties; // all config in one place, injected as a single object } @ConfigurationProperties gives you:</description></item><item><title>Full Observability: Prometheus + Grafana + Tempo + Loki</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-observability/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-observability/</guid><description>Observability means being able to answer &amp;ldquo;what&amp;rsquo;s wrong and why&amp;rdquo; from the outside — without modifying the code. The three pillars: metrics (what happened), logs (what the code did), and traces (how a request flowed). This article wires them all together.
The Stack Spring Boot App ├── Metrics → Micrometer → Prometheus scrape → Grafana dashboards ├── Traces → Micrometer Tracing → OTLP → Tempo → Grafana trace view └── Logs → Logback → Loki4j → Loki → Grafana log explorer All three converge in Grafana — click a metric spike to see the correlated logs and traces for that exact time window.</description></item><item><title>Global Exception Handling with @ControllerAdvice and ProblemDetail</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-exception-handling/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-exception-handling/</guid><description>Without global exception handling, Spring returns raw stack traces or inconsistent error shapes. This article shows how to centralize all error handling in one place and return structured, RFC 7807-compliant responses.
The Problem Without Global Handling Default Spring Boot error responses are inconsistent:
// Validation failure (MethodArgumentNotValidException) { &amp;#34;timestamp&amp;#34;: &amp;#34;2026-05-03T10:00:00.000+00:00&amp;#34;, &amp;#34;status&amp;#34;: 400, &amp;#34;error&amp;#34;: &amp;#34;Bad Request&amp;#34;, &amp;#34;path&amp;#34;: &amp;#34;/api/orders&amp;#34; } // Details of which fields failed? Not included. // Custom exception not handled // → 500 Internal Server Error with a stack trace in the body (in dev mode) What clients actually need:</description></item><item><title>GraalVM Native Images with Spring Boot 4: From 8 Seconds to 37ms Startup</title><link>https://blog.devops-monk.com/2026/05/spring-boot-graalvm-native-images/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/2026/05/spring-boot-graalvm-native-images/</guid><description>Spring Boot applications running as GraalVM native images start in milliseconds, use a fraction of the memory, and fit in tiny containers. The tradeoff is a longer build time. In 2026, with Spring Boot 4 and GraalVM 24, native images are production-ready for most Spring applications.
This guide covers everything: what Spring AOT does, how to build your first native image, how to fix the common issues, and how to add native builds to CI.</description></item><item><title>GraalVM Native Images: Millisecond Startup</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-graalvm-native/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-graalvm-native/</guid><description>A regular Spring Boot application takes 2–10 seconds to start. A GraalVM native image of the same application starts in under 100 milliseconds. For serverless functions, batch jobs, and CLI tools, this is the difference between viable and unusable.
What Is a Native Image? GraalVM&amp;rsquo;s native image compiler performs ahead-of-time (AOT) compilation. Instead of shipping a JAR that the JVM interprets at runtime, you ship a standalone executable that:
Contains only the code your application actually uses Has no JVM startup overhead Uses much less memory (no JIT compiler, no class metadata) Starts in milliseconds The tradeoff: compile time increases from seconds to minutes.</description></item><item><title>Graceful Shutdown and Production Readiness</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-production-readiness/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-production-readiness/</guid><description>An application that starts and serves traffic is not production-ready. Production readiness means it shuts down cleanly, handles spikes, recovers from transient failures, and gives you visibility into what it&amp;rsquo;s doing. This article covers the operational layer.
Graceful Shutdown When Kubernetes terminates a pod, it sends SIGTERM. Without graceful shutdown, in-flight requests are killed mid-execution — users see 500 errors or dropped writes.
Enable graceful shutdown:
server: shutdown: graceful # wait for in-flight requests to complete spring: lifecycle: timeout-per-shutdown-phase: 30s # max wait before forcing shutdown With this configured, Spring Boot:</description></item><item><title>Handling Requests: Path Variables, Query Params, and Request Bodies</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-request-handling/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-request-handling/</guid><description>A REST API receives data in many places: URL path, query string, body, headers, cookies. This article covers every way to extract that data in Spring MVC.
@PathVariable — Extract from URL Use @PathVariable when the data is part of the URL path:
// URL: GET /api/orders/550e8400-e29b-41d4-a716-446655440000 @GetMapping(&amp;#34;/{id}&amp;#34;) public OrderResponse getOrder(@PathVariable UUID id) { return orderService.findById(id) .map(OrderResponse::from) .orElseThrow(() -&amp;gt; new OrderNotFoundException(id)); } Spring automatically converts the string segment to the parameter type (UUID, Long, int, etc.</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>How Spring Boot Auto-Configuration Works — The Magic Explained</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-auto-configuration/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-auto-configuration/</guid><description>Spring Boot &amp;ldquo;just works&amp;rdquo; — you add a dependency and things appear in your application context. This article explains exactly how that happens, so you can debug it when it doesn&amp;rsquo;t, and extend it when you need to.
What Auto-Configuration Actually Does When you add spring-boot-starter-data-jpa to your project, you don&amp;rsquo;t write a single line of config — yet Spring creates a DataSource, EntityManagerFactory, and JpaTransactionManager automatically. That&amp;rsquo;s auto-configuration.
Auto-configuration is a set of @Configuration classes that Spring Boot ships.</description></item><item><title>Integration Testing with @SpringBootTest and Testcontainers</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-integration-testing/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-integration-testing/</guid><description>Integration tests verify that all layers work together — HTTP → controller → service → repository → database. This article shows how to write them efficiently with Testcontainers and manage test isolation.
@SpringBootTest — The Full Context @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class OrderIntegrationTest { // Loads the FULL Spring ApplicationContext // Everything: controllers, services, repositories, security // Starts on a random port (avoids port conflicts when running tests in parallel) } WebEnvironment options Option What it starts Use for RANDOM_PORT Embedded server on random port Full HTTP round-trip tests DEFINED_PORT Embedded server on configured port When you need a fixed port MOCK (default) No real server, MockMvc available Fast tests without real HTTP NONE No server at all Service/repo tests only Testcontainers — Real Database &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;org.</description></item><item><title>Inter-Service Communication with OpenFeign and RestClient</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-cloud-feign-restclient/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-cloud-feign-restclient/</guid><description>Services call each other over HTTP. You can use raw RestClient with a URL, or you can use OpenFeign — a declarative HTTP client that turns an interface into a fully functional HTTP client. This article covers both.
OpenFeign: Declarative HTTP Client &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;org.springframework.cloud&amp;lt;/groupId&amp;gt; &amp;lt;artifactId&amp;gt;spring-cloud-starter-openfeign&amp;lt;/artifactId&amp;gt; &amp;lt;/dependency&amp;gt; @SpringBootApplication @EnableFeignClients public class OrderServiceApplication { } Defining a Feign Client @FeignClient( name = &amp;#34;inventory-service&amp;#34;, // service name — resolved via Eureka path = &amp;#34;/api/inventory&amp;#34; ) public interface InventoryClient { @GetMapping(&amp;#34;/products/{productId}/availability&amp;#34;) InventoryResponse checkAvailability( @PathVariable UUID productId, @RequestParam int quantity ); @PostMapping(&amp;#34;/reservations&amp;#34;) ReservationResponse reserve(@RequestBody ReservationRequest request); @DeleteMapping(&amp;#34;/reservations/{reservationId}&amp;#34;) void cancelReservation(@PathVariable UUID reservationId); @GetMapping(&amp;#34;/products&amp;#34;) Page&amp;lt;ProductResponse&amp;gt; findProducts( @RequestParam String category, @SpringQueryMap Pageable pageable // Pageable as query params ); } Using it — just inject and call:</description></item><item><title>Introduction to Messaging with Apache Kafka</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-kafka-introduction/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-kafka-introduction/</guid><description>REST APIs are synchronous — the caller waits for a response. Sometimes you don&amp;rsquo;t want that. An order creation shouldn&amp;rsquo;t wait for the inventory system, the notification system, and the analytics system to all respond before confirming to the user. Kafka decouples these concerns.
What Kafka Is Kafka is a distributed event streaming platform. It stores events (messages) in an ordered, immutable log. Producers write to Kafka; consumers read from it.</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>Introduction to Spring Data JPA</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-data-jpa-introduction/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-data-jpa-introduction/</guid><description>Most Spring Boot applications need to read and write data to a relational database. Spring Data JPA makes this dramatically simpler by generating the boilerplate data access code for you. This article explains what it is, how it fits together, and how to get started.
The Stack: JPA, Hibernate, and Spring Data JPA These three things work together — understanding the layering matters:
Your Code (Repository interfaces, @Entity classes) │ ▼ Spring Data JPA (generates repository implementations, adds convenience methods) │ ▼ JPA (Jakarta Persistence API) (standard specification: @Entity, @Id, EntityManager, JPQL) │ ▼ Hibernate (JPA implementation) (translates JPA operations to SQL, manages sessions) │ ▼ JDBC (Java Database Connectivity) (sends SQL to the database) │ ▼ PostgreSQL / MySQL / H2 / etc.</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>JPA Entity Mapping: @Entity, @Id, @Column, and More</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-data-jpa-entity-mapping/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-data-jpa-entity-mapping/</guid><description>JPA entity mapping defines how Java objects translate to database tables. Get it right and your schema is clean, performant, and expressive. This article covers every mapping annotation you&amp;rsquo;ll need.
@Entity and @Table @Entity @Table( name = &amp;#34;orders&amp;#34;, // table name (default: class name) schema = &amp;#34;commerce&amp;#34;, // database schema indexes = { @Index(name = &amp;#34;idx_orders_customer_id&amp;#34;, columnList = &amp;#34;customer_id&amp;#34;), @Index(name = &amp;#34;idx_orders_status_created&amp;#34;, columnList = &amp;#34;status, created_at&amp;#34;) }, uniqueConstraints = { @UniqueConstraint(name = &amp;#34;uq_order_number&amp;#34;, columnNames = &amp;#34;order_number&amp;#34;) } ) public class Order { // .</description></item><item><title>JPA Performance: Solving N+1, Lazy Loading, and Query Optimization</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-jpa-performance-tuning/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-jpa-performance-tuning/</guid><description>JPA makes data access easy — until it silently runs hundreds of queries to load what you think is a single query. This article covers how to find and fix the most common JPA performance problems.
Enable Query Logging First You can&amp;rsquo;t fix what you can&amp;rsquo;t see. Enable SQL logging before optimizing:
logging: level: org.hibernate.SQL: DEBUG org.hibernate.orm.jdbc.bind: TRACE # log bind parameters (Spring Boot 3+) spring: jpa: properties: hibernate: format_sql: true generate_statistics: true # log query count, cache hits, etc.</description></item><item><title>JPQL, @Query, and Native Queries in Spring Data JPA</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-data-jpa-queries/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-data-jpa-queries/</guid><description>Derived query methods cover simple cases. Complex filtering, aggregation, and reporting queries need JPQL or native SQL. This article covers both with practical examples.
JPQL vs SQL vs Native SQL JPQL Native SQL Writes Entity-oriented (FROM Order o) Table-oriented (FROM orders o) Portability DB-agnostic DB-specific Features Basic SQL + JPA joins Full DB-specific SQL (CTEs, window functions, JSONB) Type-safety Partial None Use for Most queries Reporting, DB-specific features @Query with JPQL public interface OrderRepository extends JpaRepository&amp;lt;Order, UUID&amp;gt; { // Basic JPQL — uses entity/field names, not table/column names @Query(&amp;#34;SELECT o FROM Order o WHERE o.</description></item><item><title>JWT Authentication: Build a Complete Login System</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-security-jwt/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-security-jwt/</guid><description>JWT (JSON Web Token) is the standard for stateless REST API authentication. This article builds a complete JWT authentication system — login, token generation, request validation, and token refresh.
What is a JWT? A JWT has three base64url-encoded parts separated by dots:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 ← Header (algorithm + type) .eyJzdWIiOiJ1c2VyMTIzIiwicm9sZXMiOlsiVVNFUiJdLCJpYXQiOjE3MTQ3MjY0MDAsImV4cCI6MTcxNDczMDAwMH0 ← Payload .SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c ← Signature The payload contains claims:
{ &amp;#34;sub&amp;#34;: &amp;#34;user123&amp;#34;, // subject (user identifier) &amp;#34;roles&amp;#34;: [&amp;#34;USER&amp;#34;], &amp;#34;iat&amp;#34;: 1714726400, // issued at &amp;#34;exp&amp;#34;: 1714730000 // expires at } The signature is a HMAC of the header+payload — tamper-proof.</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>Logging: SLF4J, Logback, and Structured Logging</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-logging/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-logging/</guid><description>Logging done right gives you everything you need to diagnose production issues. Done wrong, it either buries you in noise or leaves you blind. This article covers the full logging stack — from basics to structured production logging.
The Logging Stack Your Code → SLF4J API → Logback (implementation) → Appenders (console, file, etc.) SLF4J is the facade — your code always calls LoggerFactory.getLogger() and log.info(). The implementation (Logback) is swappable without changing your code.</description></item><item><title>Microservices Architecture: When to Split and When Not to</title><link>https://blog.devops-monk.com/tutorials/spring-boot/microservices-architecture-guide/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/microservices-architecture-guide/</guid><description>Microservices are not a technology — they&amp;rsquo;re an organizational strategy. The right reason to split a monolith is team autonomy and independent deployment, not technical elegance. This article covers when splitting makes sense and how to do it without creating a distributed monolith.
What Microservices Actually Solve Microservices address two problems:
1. Independent deployment: Team A can deploy the Order Service without coordinating with Team B&amp;rsquo;s Payment Service. No shared deployment pipeline, no release freeze, no &amp;ldquo;all-hands&amp;rdquo; deploy windows.</description></item><item><title>Migrating from Spring Boot 3.x to 4.0</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-3-to-4-migration/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-3-to-4-migration/</guid><description>Migrating from Spring Boot 3.x to 4.0 is straightforward if you&amp;rsquo;ve kept up with deprecation warnings. This guide walks through every step — from dependency updates to API changes — with before/after examples.
Pre-Migration: Fix Boot 3 Deprecations Before bumping the version, fix all deprecation warnings in your current Boot 3.x project. Every deprecated API in Boot 3 is removed in Boot 4. In IntelliJ IDEA: Analyze → Code Cleanup with &amp;ldquo;Remove deprecated usages&amp;rdquo; enabled.</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>OAuth2 Authorization Server with Spring Security</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-security-oauth2-authorization-server/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-security-oauth2-authorization-server/</guid><description>Most teams use a managed auth provider (Keycloak, Auth0). But sometimes you need your own — multi-tenant SaaS, air-gapped environments, or full control over token contents. Spring Authorization Server provides a production-ready OAuth2 + OIDC implementation.
When to Build Your Own vs Use a Provider Use a managed provider (Keycloak/Auth0): Most applications. Faster to set up, maintained externally, handles compliance.
Build your own: Multi-tenant platforms issuing tokens on behalf of tenant auth providers, air-gapped or regulated environments, products that ARE the identity provider, or when you need full control over token structure and storage.</description></item><item><title>OAuth2 Resource Server: Validate JWTs from an Auth Provider</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-security-oauth2-resource-server/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-security-oauth2-resource-server/</guid><description>In production, you rarely build your own auth server. You use an external provider — Keycloak, Auth0, Okta, or AWS Cognito. This article shows how to configure Spring Boot as a Resource Server that validates JWTs issued by any OIDC-compliant provider.
The OAuth2 Architecture ┌─────────────────┐ │ Auth Server │ │ (Keycloak/Auth0) │ │ │ │ Issues JWTs │ │ Publishes JWKS │ └────────┬────────┘ │ ┌────────────┐ │ JWT │ Client │──────────►│ │ (Browser/ │ │ │ Mobile) │ │ └────────────┘ ▼ ┌─────────────────┐ Bearer │ Resource Server │ Token ►│ (Spring Boot) │ │ │ │ Validates JWT │ │ via JWKS URI │ └─────────────────┘ Client authenticates with the Auth Server and receives a JWT Client sends the JWT as Authorization: Bearer &amp;lt;token&amp;gt; to the Resource Server Resource Server validates the JWT by fetching the public key from the Auth Server&amp;rsquo;s JWKS endpoint If valid, the Resource Server processes the request Setup &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;org.</description></item><item><title>OWASP Top 10 for Spring Boot: Real Vulnerabilities and How to Fix Them</title><link>https://blog.devops-monk.com/2026/05/spring-boot-owasp-security/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/2026/05/spring-boot-owasp-security/</guid><description>The OWASP Top 10 lists the most critical web application security risks. Spring Boot apps have their own common failure patterns: exposed Actuator endpoints, secrets in properties files, SQL built from string concatenation, and Spring Security misconfiguration.
This guide covers the vulnerabilities that actually appear in Spring Boot applications and how to fix each one.
1. SQL Injection SQL injection remains one of the most critical vulnerabilities. It allows attackers to manipulate database queries.</description></item><item><title>Pagination and Sorting in Spring Boot</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-pagination-sorting/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-pagination-sorting/</guid><description>Returning all records from a large table in a single response is a recipe for slow APIs and crashed servers. Pagination is not optional — this article shows how to implement it properly with Spring Data.
The Problem with Returning Everything // Never do this for large datasets @GetMapping(&amp;#34;/api/orders&amp;#34;) public List&amp;lt;Order&amp;gt; getOrders() { return orderRepository.findAll(); // 1 million orders → OutOfMemoryError } Even for &amp;ldquo;small&amp;rdquo; tables, always paginate. Requirements change, data grows.</description></item><item><title>Password Encoding and User Authentication</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-security-authentication/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-security-authentication/</guid><description>Every application needs user registration and login. This article builds a complete authentication system — from storing passwords safely to handling failed login attempts.
Never Store Passwords in Plain Text Store a one-way hash, not the password. BCrypt is the industry standard:
@Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(12); // cost factor 12 → ~250ms per hash on modern hardware // strong enough to slow down brute-force attacks } BCrypt properties:</description></item><item><title>Producing and Consuming Kafka Messages</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-kafka-producer-consumer/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-kafka-producer-consumer/</guid><description>This article implements Kafka producers and consumers with the full production setup — error handling, retries, dead-letter topics, and idempotent consumers.
Producer: KafkaTemplate @Service @RequiredArgsConstructor @Slf4j public class OrderEventPublisher { private final KafkaTemplate&amp;lt;String, Object&amp;gt; kafkaTemplate; public void publishOrderCreated(Order order) { OrderCreatedEvent event = new OrderCreatedEvent( order.getId(), order.getCustomerId(), order.getCustomerEmail(), order.getItems().stream().map(OrderItemDto::from).toList(), order.getTotalAmount(), Instant.now() ); // Key = customerId: all events for same customer go to same partition kafkaTemplate.send(&amp;#34;order-events&amp;#34;, order.getCustomerId().toString(), event) .whenComplete((result, ex) -&amp;gt; { if (ex !</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>Reliable Event Publishing: The Transactional Outbox Pattern</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-kafka-outbox-pattern/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-kafka-outbox-pattern/</guid><description>There is a fundamental problem with publishing Kafka events after a database commit: if the application crashes between the commit and the publish, the event is lost forever. The Transactional Outbox Pattern solves this.
The Problem @Transactional public Order createOrder(CreateOrderRequest request) { Order order = orderRepository.save(buildOrder(request)); // DB committed ✓ kafkaTemplate.send(&amp;#34;order-events&amp;#34;, event); // ← Crash here → event lost, DB already committed // → Inventory never updated, customer never notified return order; } Two distributed systems (PostgreSQL and Kafka) can&amp;rsquo;t be in a single transaction.</description></item><item><title>Resilience Patterns with Resilience4j</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-cloud-resilience4j/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-cloud-resilience4j/</guid><description>In microservices, every network call can fail. A slow dependency can exhaust your thread pool, cascading into a full outage. Resilience4j provides the patterns to handle these failures gracefully — without hiding them.
Setup &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;io.github.resilience4j&amp;lt;/groupId&amp;gt; &amp;lt;artifactId&amp;gt;resilience4j-spring-boot3&amp;lt;/artifactId&amp;gt; &amp;lt;version&amp;gt;2.2.0&amp;lt;/version&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-aop&amp;lt;/artifactId&amp;gt; &amp;lt;/dependency&amp;gt; Circuit Breaker A circuit breaker wraps a remote call. When failures exceed a threshold, the circuit &amp;ldquo;opens&amp;rdquo; and calls fail immediately (without waiting for a timeout) — protecting your thread pool and giving the failing service time to recover.</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>Role-Based Access Control with @PreAuthorize</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-security-rbac/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-security-rbac/</guid><description>Roles and permissions control what authenticated users can do. This article implements a complete RBAC system — from URL-level rules to method-level security and resource ownership checks.
Roles vs Permissions Roles are coarse-grained groupings (USER, MANAGER, ADMIN).
Permissions are fine-grained actions (READ_ORDERS, WRITE_PRODUCTS, DELETE_USERS).
Assign permissions to roles:
ADMIN → all permissions MANAGER → READ_ORDERS, WRITE_ORDERS, READ_PRODUCTS, WRITE_PRODUCTS USER → READ_OWN_ORDERS, WRITE_OWN_ORDERS, READ_PRODUCTS Model permissions as a typed enum:
public enum Permission { // Order permissions READ_ORDERS, WRITE_ORDERS, DELETE_ORDERS, READ_OWN_ORDERS, WRITE_OWN_ORDERS, // Product permissions READ_PRODUCTS, WRITE_PRODUCTS, DELETE_PRODUCTS, // User management READ_USERS, WRITE_USERS, DELETE_USERS } public enum Role { USER(Set.</description></item><item><title>Service Discovery with Eureka</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-cloud-eureka-service-discovery/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-cloud-eureka-service-discovery/</guid><description>In a microservices environment, services scale up and down dynamically. You can&amp;rsquo;t hardcode IP addresses — a service running on 10 pods today has 10 different addresses. Service discovery solves this: services register themselves, and clients look up live instances by name.
What Service Discovery Does Without service discovery: Order Service → http://192.168.1.45:8081/api/inventory ← hardcoded, breaks when IP changes With service discovery: Order Service → &amp;#34;inventory-service&amp;#34; → Eureka → [192.168.1.45:8081, 192.</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 AI 2.0: Build a RAG Application with Spring Boot</title><link>https://blog.devops-monk.com/2026/05/spring-ai-rag-application/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/2026/05/spring-ai-rag-application/</guid><description>Spring AI 1.0 GA shipped in May 2025. It brings the Spring programming model to AI development: a unified ChatClient API that works across Claude, OpenAI, Gemini, Ollama, and Azure OpenAI — switching AI providers is changing one dependency.
This guide builds a complete RAG (Retrieval-Augmented Generation) application that answers questions about your documentation using any AI provider.
What Is RAG? A large language model (LLM) knows everything in its training data but nothing about your specific documents, code, or business data.</description></item><item><title>Spring AI: Build a RAG Application</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-spring-ai-rag/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-spring-ai-rag/</guid><description>Large language models know a lot — but not about your data. RAG (Retrieval-Augmented Generation) solves this: find the relevant context from your documents, inject it into the prompt, and let the model answer grounded in your data. This article builds a complete RAG API with Spring AI 2.0.
What You&amp;rsquo;ll Build A Q&amp;amp;A API over your product documentation:
User: &amp;#34;What&amp;#39;s the return policy for electronics?&amp;#34; → Search vector store for relevant docs → Inject matching paragraphs into prompt → Claude/GPT answers based on your actual docs Without RAG: the LLM guesses or hallucinate your policy.</description></item><item><title>Spring Bean Scopes and Lifecycle</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-bean-scopes-lifecycle/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-bean-scopes-lifecycle/</guid><description>Every bean in the Spring container has a scope (how many instances exist and for how long) and a lifecycle (what happens when it&amp;rsquo;s created and destroyed). Understanding these prevents subtle bugs and lets you optimize resource usage.
Bean Scopes Overview Scope Instances Available in singleton One per ApplicationContext All apps prototype New instance every time All apps request One per HTTP request Web apps session One per HTTP session Web apps application One per ServletContext Web apps Singleton (Default) By default, every Spring bean is a singleton — one instance per ApplicationContext.</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 Actuator: Health, Metrics, and Management Endpoints</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-actuator/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-actuator/</guid><description>A running application is not enough — you need to know if it&amp;rsquo;s healthy, how it&amp;rsquo;s performing, and what it&amp;rsquo;s doing. Spring Boot Actuator exposes that information through HTTP endpoints and metrics.
Setup &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-actuator&amp;lt;/artifactId&amp;gt; &amp;lt;/dependency&amp;gt; By default, only /actuator/health and /actuator/info are exposed over HTTP. Everything else is available via JMX. Enable what you need:
management: endpoints: web: exposure: include: health,info,metrics,prometheus,conditions,beans,env,loggers,threaddump,heapdump base-path: /actuator endpoint: health: show-details: when-authorized # or &amp;#39;always&amp;#39; (dev), &amp;#39;never&amp;#39; (public) show-components: when-authorized metrics: enabled: true server: port: 8081 # expose actuator on a separate port (not public-facing) Health Endpoint GET /actuator/health — used by Kubernetes liveness/readiness probes and load balancers:</description></item><item><title>Spring Boot Actuator: Production Monitoring with Prometheus and Grafana</title><link>https://blog.devops-monk.com/2026/05/spring-boot-actuator-prometheus-grafana/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/2026/05/spring-boot-actuator-prometheus-grafana/</guid><description>Spring Boot Actuator exposes production-ready operational endpoints — health checks, metrics, environment info, thread dumps — out of the box. Combined with Prometheus and Grafana, you get a full monitoring stack with minimal configuration.
This guide covers everything from initial setup to Kubernetes health probes, custom metrics, and securing your management endpoints.
Setup Dependencies &amp;lt;dependencies&amp;gt; &amp;lt;!-- Actuator --&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-actuator&amp;lt;/artifactId&amp;gt; &amp;lt;/dependency&amp;gt; &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;scope&amp;gt;runtime&amp;lt;/scope&amp;gt; &amp;lt;/dependency&amp;gt; &amp;lt;/dependencies&amp;gt; Basic configuration # application.</description></item><item><title>Spring Boot Caching: Multi-Level Cache with Caffeine + Redis</title><link>https://blog.devops-monk.com/2026/05/spring-boot-caching-caffeine-redis/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/2026/05/spring-boot-caching-caffeine-redis/</guid><description>Caching reduces database load and response latency. Spring Boot&amp;rsquo;s cache abstraction lets you add caching with annotations, then swap the implementation (Caffeine, Redis, multi-level) without changing your business code.
This guide covers Caffeine for in-JVM caching, Redis for distributed caching, and a multi-level cache that combines both.
Spring Cache Abstraction Spring&amp;rsquo;s cache abstraction uses three annotations:
Annotation Behaviour @Cacheable Cache the return value. On subsequent calls, return from cache without executing the method.</description></item><item><title>Spring Boot Docker: Multi-Stage Builds, Layered JARs, and Buildpacks</title><link>https://blog.devops-monk.com/2026/05/spring-boot-docker-guide/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/2026/05/spring-boot-docker-guide/</guid><description>There are three ways to containerise a Spring Boot application: a naive single-stage Dockerfile, a proper multi-stage Dockerfile with layered JARs, and Cloud Native Buildpacks. Each has different tradeoffs in build speed, image size, and maintenance overhead.
This guide covers all three approaches, explains why layered JARs matter for CI/CD speed, and shows how to produce small, secure, production-ready images.
The Problem with the Naive Dockerfile Most tutorials show this:</description></item><item><title>Spring Boot Full Observability: Prometheus + Grafana + Tempo + Loki</title><link>https://blog.devops-monk.com/2026/05/spring-boot-observability-stack/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/2026/05/spring-boot-observability-stack/</guid><description>Observability means you can answer &amp;ldquo;what is wrong and why&amp;rdquo; from your system&amp;rsquo;s outputs alone — without adding new instrumentation after an incident. It requires three types of data: metrics (what happened), traces (why it happened), and logs (the details).
Spring Boot 4 ships a single OpenTelemetry starter that covers all three. This guide shows how to wire up the complete observability stack: Prometheus + Grafana for metrics, Grafana Tempo for distributed tracing, and Grafana Loki for logs.</description></item><item><title>Spring Boot Integration: Zero-Config Setup and Full Properties Reference</title><link>https://blog.devops-monk.com/tutorials/liquibase/liquibase-spring-boot-integration/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/liquibase-spring-boot-integration/</guid><description>Running Liquibase from the CLI is fine for learning and for standalone scripts. In a real Spring Boot application, you want migrations to run automatically at startup — before the application code touches the database. Spring Boot&amp;rsquo;s Liquibase auto-configuration handles this with zero boilerplate: add the dependency, point to your changelog, and migrations run before the application context is ready.
This article covers the complete integration: Maven/Gradle setup, how auto-run works and why, the full spring.</description></item><item><title>Spring Boot JPA Performance: Solving N+1, Lazy Loading, and Query Optimization</title><link>https://blog.devops-monk.com/2026/05/spring-boot-jpa-performance/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/2026/05/spring-boot-jpa-performance/</guid><description>JPA makes database access simple. It also makes it dangerously easy to write code that fires 100 SQL queries to load 10 records. The N+1 problem alone has caused more production performance incidents than almost any other JPA issue.
This guide covers how to find and fix the five most common JPA performance problems: N+1 queries, LazyInitializationException, over-fetching, poor connection pool sizing, and Hibernate 6 breaking changes.
Enable SQL Logging First Before optimizing anything, see exactly what queries are firing:</description></item><item><title>Spring Boot Microservices Full Stack: Eureka + Gateway + Config Server + Resilience4j</title><link>https://blog.devops-monk.com/2026/05/spring-boot-microservices-full-stack/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/2026/05/spring-boot-microservices-full-stack/</guid><description>Building microservices requires more than splitting a monolith into services. You need service discovery, a gateway to route traffic, centralised configuration management, and resilience patterns to handle inevitable failures. Spring Cloud provides all of this as a cohesive stack.
This guide builds a complete microservices architecture from scratch: Eureka for service discovery, Spring Cloud Gateway for routing, Spring Cloud Config Server for centralised config, and Resilience4j for circuit breakers and retry.</description></item><item><title>Spring Boot OAuth2 + JWT: End-to-End Zero-Trust API Security</title><link>https://blog.devops-monk.com/2026/05/spring-boot-oauth2-jwt-security/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/2026/05/spring-boot-oauth2-jwt-security/</guid><description>Zero-trust API security means every request is validated independently — no session state, no &amp;ldquo;trusted network&amp;rdquo; assumptions. A JWT bearer token is issued by an authorisation server, signed cryptographically, and validated on every API call. The API never calls back to the authorisation server during validation; it verifies the token&amp;rsquo;s signature locally.
This guide covers the complete setup: dependencies, resource server configuration, token validation (both symmetric and asymmetric), extracting claims, role-based access control, method-level security, and the Spring Security 7 changes that break existing setups.</description></item><item><title>Spring Boot on Kubernetes</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-kubernetes/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-kubernetes/</guid><description>Kubernetes is the standard platform for running containerized microservices. Spring Boot integrates naturally with Kubernetes — Actuator probes map directly to Kubernetes probes, and Spring configuration maps to ConfigMaps and Secrets.
Core Kubernetes Resources Deployment # deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: order-service labels: app: order-service spec: replicas: 3 selector: matchLabels: app: order-service template: metadata: labels: app: order-service version: &amp;#34;1.2.3&amp;#34; spec: containers: - name: order-service image: devopsmonk/order-service:1.2.3 ports: - containerPort: 8080 name: http - containerPort: 8081 name: management # Resource limits — always set these resources: requests: memory: &amp;#34;256Mi&amp;#34; cpu: &amp;#34;250m&amp;#34; limits: memory: &amp;#34;512Mi&amp;#34; cpu: &amp;#34;1000m&amp;#34; # Environment from ConfigMap and Secret envFrom: - configMapRef: name: order-service-config - secretRef: name: order-service-secrets # Individual env vars env: - name: SPRING_PROFILES_ACTIVE value: prod - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.</description></item><item><title>Spring Boot on Kubernetes: Health Checks, Graceful Shutdown, and Config Management</title><link>https://blog.devops-monk.com/2026/05/spring-boot-kubernetes-guide/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/2026/05/spring-boot-kubernetes-guide/</guid><description>Running Spring Boot on Kubernetes is not just packaging the app in a container and deploying it. You need to configure health probes correctly, handle graceful shutdown so in-flight requests don&amp;rsquo;t get dropped, manage configuration without baking secrets into images, and make sure the JVM respects container memory limits.
This guide covers the production-critical Kubernetes configuration for Spring Boot applications.
Health Probes Kubernetes uses three probe types to manage pod lifecycle:</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 Testing with Testcontainers: The Right Way</title><link>https://blog.devops-monk.com/2026/05/spring-boot-testcontainers/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/2026/05/spring-boot-testcontainers/</guid><description>Testcontainers spins up real Docker containers for your tests — a real PostgreSQL database, a real Redis, a real Kafka broker. No more mocking JDBC connections or in-memory H2 databases that behave differently from production.
Spring Boot 3.1 added @ServiceConnection, which removes the boilerplate of configuring connection URLs manually. This guide covers the right patterns for fast, reliable integration tests with Testcontainers.
Why Testcontainers Over H2 Teams use H2 in-memory databases for testing because it&amp;rsquo;s fast.</description></item><item><title>Spring Boot Virtual Threads: Benchmarks, Pitfalls, and When NOT to Use Them</title><link>https://blog.devops-monk.com/2026/05/spring-boot-virtual-threads/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/2026/05/spring-boot-virtual-threads/</guid><description>Virtual Threads landed in Java 21 as a stable feature, and Spring Boot 3.2 added first-class support with a single property. The promise: write simple blocking code and get WebFlux-level throughput. The reality is mostly true — with some important exceptions.
This article covers what Virtual Threads actually are, how to enable them in Spring Boot, real benchmark numbers, the three pitfalls that will silently destroy your performance, and a decision framework for when to use them (and when not to).</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>Spring Data with PostgreSQL</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-postgresql/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-postgresql/</guid><description>PostgreSQL offers powerful features beyond basic SQL — JSONB, arrays, full-text search, advisory locks. This article shows how to use them from Spring Boot and how to tune HikariCP for production.
Project Setup &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-data-jpa&amp;lt;/artifactId&amp;gt; &amp;lt;/dependency&amp;gt; &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;org.postgresql&amp;lt;/groupId&amp;gt; &amp;lt;artifactId&amp;gt;postgresql&amp;lt;/artifactId&amp;gt; &amp;lt;scope&amp;gt;runtime&amp;lt;/scope&amp;gt; &amp;lt;/dependency&amp;gt; spring: datasource: url: jdbc:postgresql://localhost:5432/orderdb username: app password: ${DB_PASSWORD} driver-class-name: org.postgresql.Driver jpa: database-platform: org.hibernate.dialect.PostgreSQLDialect hibernate: ddl-auto: validate HikariCP — Connection Pool Tuning Spring Boot uses HikariCP by default — the fastest JDBC connection pool.</description></item><item><title>Spring IoC and Dependency Injection</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-ioc-dependency-injection/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-ioc-dependency-injection/</guid><description>Every Spring Boot application is built on one idea: the framework creates and wires your objects, not you. This article explains how that works and how to use it effectively.
Inversion of Control In traditional Java, you create objects yourself:
// You control the dependencies public class OrderService { private final OrderRepository repository = new JpaOrderRepository(); private final EmailService email = new SmtpEmailService(&amp;#34;smtp.gmail.com&amp;#34;); } Problems:
OrderService is tightly coupled to specific implementations You can&amp;rsquo;t swap JpaOrderRepository for a mock in tests without editing OrderService If SmtpEmailService needs its own dependencies, you must construct those too Inversion of Control (IoC) flips this: instead of creating your dependencies, you declare what you need and let a container provide them.</description></item><item><title>Spring Modulith: Build a Modular Monolith Before You Commit to Microservices</title><link>https://blog.devops-monk.com/2026/05/spring-modulith-guide/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/2026/05/spring-modulith-guide/</guid><description>Microservices solve real problems: independent deployability, team autonomy, technology flexibility. They also create real problems: distributed transactions, network latency, operational complexity. Many teams split into microservices too early, before they understand their domain well enough to draw stable boundaries.
Spring Modulith gives you module boundaries, enforced isolation, and event-driven decoupling inside a single deployable JAR. It&amp;rsquo;s the pragmatic middle ground.
The Modular Monolith Problem It Solves A typical Spring Boot monolith looks like this after a year:</description></item><item><title>Spring Security Fundamentals: Filter Chain, Authentication, and Authorization</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-security-fundamentals/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-security-fundamentals/</guid><description>Spring Security is powerful but famously hard to understand. This article demystifies the core: the filter chain, how requests are processed, and how authentication and authorization work before writing a line of security config.
Setup &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; The moment you add this dependency, Spring Boot&amp;rsquo;s auto-configuration secures all endpoints with HTTP Basic authentication. A random password is printed at startup. This is the starting point — you&amp;rsquo;ll replace the defaults.</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 Migrations: H2 vs Testcontainers vs Real MySQL</title><link>https://blog.devops-monk.com/tutorials/liquibase/liquibase-testing-migrations/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/liquibase/liquibase-testing-migrations/</guid><description>The CI pipeline from Article 19 validates migrations against a MySQL service container. But that&amp;rsquo;s not the same as testing that your application code works correctly after the migration runs. This article covers how to structure Spring Boot tests that validate both the migration and the application behaviour — and makes the case for replacing H2 with Testcontainers as your primary testing database.
Three Testing Strategies Strategy Database Speed Fidelity Use For H2 in-memory H2 (in-memory) Fastest Low — MySQL-specific SQL fails Unit tests that mock repositories H2 with MySQL mode H2 (MySQL compat) Fast Medium — most SQL works, some quirks Basic integration tests on simple schemas Testcontainers Real MySQL 8.</description></item><item><title>Testing Secured Endpoints</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-security-testing/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-security-testing/</guid><description>Security tests verify that your endpoints behave correctly for different users, roles, and authentication states. This article covers the full toolkit — from simple annotations to custom security contexts.
Setup &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; spring-boot-starter-test includes this automatically.
@WithMockUser — Simple Role-Based Tests The simplest way to run a test as an authenticated user:
@WebMvcTest(OrderController.class) class OrderControllerSecurityTest { @Autowired MockMvc mockMvc; @MockBean OrderService orderService; // No authentication @Test void unauthenticatedUserIsRejected() throws Exception { mockMvc.</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>Testing Spring Boot Apps: Unit Tests with JUnit 5 and Mockito</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-unit-testing/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-unit-testing/</guid><description>Good tests catch regressions, document behavior, and give you confidence to refactor. Bad tests slow you down. This article covers unit testing at the service layer — fast, focused, no Spring context needed.
Setup &amp;lt;!-- spring-boot-starter-test includes JUnit 5, Mockito, AssertJ --&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; This pulls in:
JUnit 5 (Jupiter) — test runner and assertions Mockito — mocking framework AssertJ — fluent assertions (assertThat(...)) Hamcrest — matcher library MockMvc — web layer testing (next article) Testcontainers integration Unit Tests vs Integration Tests Unit Integration Scope One class in isolation Multiple components together Dependencies All mocked Real or near-real Speed Milliseconds Seconds to minutes Context No Spring context Spring context loads Purpose Logic correctness Component interaction Start with unit tests.</description></item><item><title>Testing the Repository Layer with @DataJpaTest</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-repository-testing/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-repository-testing/</guid><description>Repository tests verify your queries work correctly against a real database. Spring Boot&amp;rsquo;s @DataJpaTest starts a minimal slice — only JPA components — making tests fast while still catching real SQL issues.
@DataJpaTest — What It Loads @DataJpaTest is a test slice annotation:
@DataJpaTest class OrderRepositoryTest { // Spring loads: // - Your @Entity classes // - Your @Repository interfaces // - JPA infrastructure (EntityManager, transactions) // - An in-memory H2 database (by default) // // Spring does NOT load: // - @Service, @Controller, @Component classes // - Security configuration // - The full ApplicationContext } Each test method runs in a transaction that&amp;rsquo;s rolled back at the end — no data pollution between tests.</description></item><item><title>Testing the Web Layer with @WebMvcTest and MockMvc</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-web-layer-testing/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-web-layer-testing/</guid><description>Controller tests verify HTTP mapping, request parsing, validation, serialization, and security — without starting a full server. @WebMvcTest + MockMvc gives you a fast, focused web layer test.
@WebMvcTest — What It Loads @WebMvcTest(OrderController.class) class OrderControllerTest { // Spring loads: // - Your @Controller class (and its dependencies) // - DispatcherServlet, MVC configuration // - Jackson ObjectMapper // - Security (if configured) // // Spring does NOT load: // - @Service, @Repository beans // - Database, JPA // // You @MockBean all services } Basic Controller Test @WebMvcTest(OrderController.</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>Transactions: @Transactional, Propagation, and Isolation Levels</title><link>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-transactions/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://blog.devops-monk.com/tutorials/spring-boot/spring-boot-transactions/</guid><description>A transaction ensures that a group of operations either all succeed or all fail — no partial state. Spring&amp;rsquo;s @Transactional makes this simple to use, but the underlying mechanics matter when things go wrong.
How @Transactional Works @Transactional is implemented via AOP (Aspect-Oriented Programming). Spring wraps your bean in a proxy:
Client calls orderService.create() │ ▼ Spring AOP proxy intercepts the call │ ▼ BEGIN TRANSACTION │ ▼ Your actual method body executes (all DB operations share one connection and transaction) │ ├─ No exception → COMMIT │ └─ RuntimeException thrown → ROLLBACK │ ▼ Client receives result (or exception) This means @Transactional only works when:</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>