<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Aquib Javed</title>
    <description>The latest articles on DEV Community by Aquib Javed (@aquib_javed_e55c5b2494560).</description>
    <link>https://dev.to/aquib_javed_e55c5b2494560</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3637288%2Ff2de3b90-a794-48a4-b2b4-580fbe6d9306.png</url>
      <title>DEV Community: Aquib Javed</title>
      <link>https://dev.to/aquib_javed_e55c5b2494560</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aquib_javed_e55c5b2494560"/>
    <language>en</language>
    <item>
      <title>Mastering Custom Validation Annotations in Spring Boot 3: Beyond @NotNull</title>
      <dc:creator>Aquib Javed</dc:creator>
      <pubDate>Sat, 20 Dec 2025 13:43:58 +0000</pubDate>
      <link>https://dev.to/aquib_javed_e55c5b2494560/mastering-custom-validation-annotations-in-spring-boot-3-beyond-notnull-27i3</link>
      <guid>https://dev.to/aquib_javed_e55c5b2494560/mastering-custom-validation-annotations-in-spring-boot-3-beyond-notnull-27i3</guid>
      <description>&lt;p&gt;Beyond @NotNull: Creating Custom Validation Annotations in Spring Boot 3&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Standard Jakarta Bean Validation annotations often fail to capture complex, multi-field business rules. This guide explains how to implement the &lt;code&gt;ConstraintValidator&lt;/code&gt; interface to create reusable, class-level custom annotations in Spring Boot. Anyone who wants to learn how to create their own custom annotation and handle business specific-logic, must go through the blog.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Audience:&lt;/strong&gt; Java developers familiar with Spring Boot and basic REST API development.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scope:&lt;/strong&gt; This guide covers the creation of a class-level validation annotation (&lt;code&gt;@OfficialInviteCode&lt;/code&gt;). It does not cover simple field-level regex validation or frontend integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Limitations of Standard Annotations
&lt;/h2&gt;

&lt;p&gt;Imagine you have these business rules for a VIP event:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Invite Code:&lt;/strong&gt; Must start with "VIP-".&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Age:&lt;/strong&gt; Guests must be older than 35.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Email:&lt;/strong&gt; Must end with a &lt;code&gt;.com&lt;/code&gt; domain.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Name:&lt;/strong&gt; Must be uppercase (business requirement).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Standard annotations like &lt;code&gt;@Email&lt;/code&gt; or &lt;code&gt;@Min&lt;/code&gt; handle these rules individually, but they often struggle with interdependent logic or specific business formats.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Imperative Approach (Anti-Pattern)
&lt;/h2&gt;

&lt;p&gt;Developers often mix validation logic directly into the service or controller layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Avoid this approach
if (!dto.getInviteCode().startsWith("VIP-")) {
    throw new ValidationException("Invalid Code");
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach creates code duplication and makes unit testing difficult.&lt;br&gt;
The Declarative Approach (Best Practice)&lt;br&gt;
A better solution uses a custom annotation to centralize logic and separate concerns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@OfficialInviteCode
private String inviteCode;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This guide demonstrates how to implement this declaratively using class-level validation.&lt;br&gt;
Implementation Guide&lt;br&gt;
Follow this order of operations to implement custom validation:&lt;br&gt;
Define the DTO.&lt;br&gt;
Define the custom annotation interface.&lt;br&gt;
Implement the ConstraintValidator.&lt;br&gt;
Apply the annotation to the DTO.&lt;br&gt;
Create the Controller.&lt;br&gt;
Add a Global Exception Handler.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create the DTO Class
Define the data structure and apply standard constraints where applicable.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class GuestRegistrationDto {
    @NotNull
    private String name;

    @Min(value = 35, message = "Guest must be above 35")
    private int age;

    @Email
    @NotBlank
    private String email;

    private String inviteCode;

    // Getters and Setters
    public String getName() { return name; }
    public String getEmail() { return email; }
    public String getInviteCode() { return inviteCode; }
    public int getAge() { return age; }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Define the Custom Annotation
Create the @OfficialInviteCode interface. This interface acts as the contract for your validation logic.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = OfficialInviteCodeValidator.class)
public @interface OfficialInviteCode {

    String message() default "Invalid invite code";
    Class&amp;lt;?&amp;gt;[] groups() default {};
    Class&amp;lt;? extends jakarta.validation.Payload&amp;gt;[] payload() default {};
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Key Concepts:&lt;br&gt;
@Target(ElementType.TYPE): Indicates that you apply this annotation to the class level, allowing access to all fields within the class.&lt;br&gt;
@Retention(RetentionPolicy.RUNTIME): Ensures the annotation remains available to the JVM during execution for Spring's reflection mechanisms.&lt;br&gt;
@Constraint(validatedBy = ...): Links this annotation to the OfficialInviteCodeValidator class, which contains the logic.&lt;br&gt;
The Jakarta API Contract:&lt;br&gt;
The API mandates three attributes for every validation annotation:&lt;br&gt;
message(): The default error text.&lt;br&gt;
groups(): Enables validation groups (e.g., validate only on "Update" operations).&lt;br&gt;
payload(): Attaches custom metadata, such as severity levels.&lt;br&gt;
3.Implement the ConstraintValidator&lt;br&gt;
Override the isValid method to implement your specific business rules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class OfficialInviteCodeValidator implements ConstraintValidator&amp;lt;OfficialInviteCode, GuestRegistrationDto&amp;gt; {

    @Override
    public boolean isValid(GuestRegistrationDto dto, ConstraintValidatorContext constraintValidatorContext) {
        if (dto == null) return true;

        String name = dto.getName();
        String email = dto.getEmail();
        String code = dto.getInviteCode();
        int age = dto.getAge();

        // Business Logic Validation
        if (name != null &amp;amp;&amp;amp; !name.equals(name.toUpperCase())) {
            return false; 
        }
        if (email != null &amp;amp;&amp;amp; !email.endsWith(".com")) {
            return false;
        }
        if (code != null &amp;amp;&amp;amp; !code.startsWith("VIP-")) {
            return false;
        }
        if (age &amp;gt;= 35) {
            return true;         
        }
        return false;
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Annotate the DTO
Apply your new annotation to the class definition. The DTO now expresses both built-in constraints and your custom logic.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@OfficialInviteCode
public class GuestRegistrationDto {
    // fields...
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the DTO expresses all rules: built-in constraints + your custom constraint in one place.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create the REST Controller
Use the &lt;a class="mentioned-user" href="https://dev.to/valid"&gt;@valid&lt;/a&gt; annotation to trigger the validation logic before the method executes.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@RestController
public class GuestController {

    @PostMapping("/invite")
    public ResponseEntity&amp;lt;?&amp;gt; registerGuest(@Valid @RequestBody GuestRegistrationDto dto) {
        return ResponseEntity.ok("registered");
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Handle Exceptions Globally
Create a @RestControllerAdvice class to catch MethodArgumentNotValidException and return a clean JSON response.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Map&amp;lt;String, String&amp;gt; handleValidationException(MethodArgumentNotValidException ex) {
        String message = ex.getBindingResult()
                .getFieldErrors()
                .stream()
                .findFirst()
                .map(DefaultMessageSourceResolvable::getDefaultMessage)
                .orElse("Invalid request");

        Map&amp;lt;String, String&amp;gt; body = new HashMap&amp;lt;&amp;gt;();
        body.put("error", message);
        return body;
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Architecture &amp;amp; Best Practices&lt;br&gt;
To master custom validation, understand how Spring manages the lifecycle of validator classes.&lt;br&gt;
Leverage Dependency Injection&lt;br&gt;
Spring manages your ConstraintValidator implementation as a Bean. This allows you to inject services or repositories directly into the validator.&lt;br&gt;
Use Case: Verify data existence in the database (e.g., checking if a coupon code is valid) rather than just checking format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class CouponValidator implements ConstraintValidator&amp;lt;ValidCoupon, String&amp;gt; {

    @Autowired
    private CouponRepository couponRepository; // Spring injects this dependency

    @Override
    public boolean isValid(String code, ConstraintValidatorContext context) {
        return couponRepository.existsByCode(code);
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure Thread Safety&lt;br&gt;
Spring treats Validator classes as Singletons. The container creates one instance and reuses it for every request.&lt;br&gt;
The Risk: Storing request-specific data in instance variables causes race conditions.&lt;br&gt;
The Solution: Keep validators stateless. Perform all logic using only the local variables passed into the isValid() method.&lt;/p&gt;

&lt;p&gt;Conclusion&lt;br&gt;
Spring Boot’s validation framework extends well beyond simple null checks. By using ConstraintValidator, you create robust, reusable, and centralized business rules that keep your controllers and services clean.&lt;/p&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>developers</category>
    </item>
    <item>
      <title>How to Handle HttpClientErrorException in Spring RestTemplate: A Complete Guide</title>
      <dc:creator>Aquib Javed</dc:creator>
      <pubDate>Sun, 30 Nov 2025 12:18:35 +0000</pubDate>
      <link>https://dev.to/aquib_javed_e55c5b2494560/how-to-handle-httpclienterrorexception-in-spring-resttemplate-a-complete-guide-1f3p</link>
      <guid>https://dev.to/aquib_javed_e55c5b2494560/how-to-handle-httpclienterrorexception-in-spring-resttemplate-a-complete-guide-1f3p</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;RestTemplate&lt;/span&gt; &lt;span class="n"&gt;restTemplate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RestTemplate&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// If this URL returns 404, the app crashes!&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;restTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getForObject&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://api.example.com/users/999"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While working on a company project one evening, I encountered an &lt;code&gt;HttpClientErrorException&lt;/code&gt; from an API response. This error was new to me. After debugging, I solved it and learned how to handle it properly.&lt;/p&gt;

&lt;p&gt;In this post, I'll explain what this error is and how you can handle it in your code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding RestTemplate Exceptions
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;RestTemplate&lt;/code&gt; throws &lt;code&gt;RestClientResponseException&lt;/code&gt; and its subtypes when API calls fail. The most common subtypes are &lt;code&gt;HttpClientErrorException&lt;/code&gt; and &lt;code&gt;HttpServerErrorException&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is the exception hierarchy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;RestClientException&lt;/code&gt; → base class&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;RestClientResponseException&lt;/code&gt; extends &lt;code&gt;RestClientException&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HttpStatusCodeException&lt;/code&gt; extends &lt;code&gt;RestClientResponseException&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HttpClientErrorException&lt;/code&gt; extends &lt;code&gt;HttpStatusCodeException&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HttpServerErrorException&lt;/code&gt; extends &lt;code&gt;HttpStatusCodeException&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;RestClientResponseException&lt;/code&gt; is the base class that contains the actual HTTP response data.&lt;/p&gt;

&lt;h2&gt;
  
  
  How RestTemplate Handles HTTP Status Codes
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;RestTemplate&lt;/code&gt; returns &lt;code&gt;ResponseEntity&lt;/code&gt; only for successful 2xx responses.&lt;/p&gt;

&lt;p&gt;For 4xx errors, it throws &lt;code&gt;HttpClientErrorException&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For 5xx errors, it throws &lt;code&gt;HttpServerErrorException&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Catch HttpClientErrorException
&lt;/h2&gt;

&lt;p&gt;You will frequently encounter &lt;code&gt;HttpClientErrorException&lt;/code&gt; when calling external APIs using &lt;code&gt;RestTemplate&lt;/code&gt;. This exception indicates that the remote service returned a 4xx HTTP error. The request failed due to a client-side issue.&lt;/p&gt;

&lt;p&gt;If you do not handle this exception, your application may terminate unexpectedly.&lt;/p&gt;

&lt;p&gt;Wrap the API call inside a try-catch block. By catching the exception, you can gracefully handle the error. You can log it, return a meaningful response, or trigger fallback logic. This prevents your system from crashing.&lt;/p&gt;

&lt;p&gt;This ensures your application remains stable, resilient, and production-safe even when the external API returns invalid or unexpected responses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution: Use Try-Catch
&lt;/h2&gt;

&lt;p&gt;The easiest way to handle these exceptions is to wrap the call in a try-catch block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="n"&gt;restTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getForEntity&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://example.com/api/data"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Success: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBody&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpClientErrorException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Handles 4xx errors&lt;/span&gt;
    &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Client Error: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getStatusCode&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error Body: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getResponseBodyAsString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach keeps your application running and gives you visibility into what went wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enterprise-Level Error Handling with GlobalExceptionHandler
&lt;/h2&gt;

&lt;h2&gt;
  
  
  The Challenge with Try-Catch Blocks
&lt;/h2&gt;

&lt;p&gt;As the number of API calls increases in your application, handling everything with try-catch blocks becomes difficult. Managing exceptions manually in every method leads to code duplication and maintenance issues.&lt;/p&gt;

&lt;p&gt;To handle errors at an enterprise level, you should implement a Global Exception Handler.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a GlobalExceptionHandler?
&lt;/h2&gt;

&lt;p&gt;A GlobalExceptionHandler is a centralized error-handling mechanism. Instead of scattering try-catch blocks throughout your codebase, you define error-handling logic in one place. Spring automatically applies this logic to every controller in your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Implement GlobalExceptionHandler
&lt;/h2&gt;

&lt;p&gt;Here is the cleanest and simplest way to handle &lt;code&gt;HttpClientErrorException&lt;/code&gt; using a GlobalExceptionHandler:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create a Separate Class for Global Error Handling
&lt;/h3&gt;

&lt;p&gt;Large-scale systems must centralize all error handling in one place. This keeps your code clean and maintainable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Annotate with @ControllerAdvice
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;@ControllerAdvice&lt;/code&gt; annotation tells Spring to scan this class automatically and apply your error-handling logic to every controller in your project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Create a Method to Handle HttpClientErrorException
&lt;/h3&gt;

&lt;p&gt;Your global handler will catch the exception before it crashes the system and return a custom error response.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Use @ExceptionHandler
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;@ExceptionHandler&lt;/code&gt; annotation tells Spring exactly which exception this method should catch and handle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation Example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@ControllerAdvice&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GlobalExceptionHandler&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// Step 2: Handle RestTemplate 4xx errors (HttpClientErrorException)&lt;/span&gt;
    &lt;span class="nd"&gt;@ExceptionHandler&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpClientErrorException&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;handleHttpClientError&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpClientErrorException&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="c1"&gt;// Step 3: Create a simple JSON error response body&lt;/span&gt;
        &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LinkedHashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
        &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"timestamp"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getStatusCode&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getStatusCode&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

        &lt;span class="c1"&gt;// Step 4: Return the response with correct HTTP status&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getStatusCode&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;This code tells Spring: whenever &lt;code&gt;RestTemplate&lt;/code&gt; throws a 4xx error, handle it here instead of crashing.&lt;/p&gt;

&lt;p&gt;With this setup, you no longer need try-catch blocks around every API call. Spring automatically routes all &lt;code&gt;HttpClientErrorException&lt;/code&gt; instances to your global handler. This approach is clean, scalable, and production-ready.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro Tip&lt;/strong&gt;: Always log the error body (ex.getResponseBodyAsString()) inside your exception handler. APIs often return helpful validation messages (like 'Email already exists') inside the 400 Bad Request body, which you don't want to lose.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>webdev</category>
      <category>api</category>
      <category>springboot</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
