SoFunction
Updated on 2025-04-09

Spring Validator masters the detailed process of object verification from scratch

Spring Validator Study Guide: Mastering Object Verification from Zero

1. The role of the Validator interface: your data "goalkeeper"

Imagine that you have developed a user registration function, and the data submitted by the user may have various problems: the name is not filled in, the age is written as a negative number... If these errors are saved directly to the database, it will lead to errors in the subsequent process.ValidatorJust like a strict goalkeeper, checking whether each field complies with the rules before data enters the system.

Core mission

  • Check whether the object attribute is legal (such as non-number, numerical range).
  • Collect error information to facilitate subsequent prompts for users.

2. Two major methods of Validator interface: How to work?

1. supports(Class clazz): Can I handle this object?

  • effect: Determine whether Validator currently supports verification of objects of a certain class.
  • Key Choices
    • Exact matchreturn (clazz);→ Verify onlyPersonkind.
    • Flexible matchingreturn (clazz);→ SupportPersonand its subclasses.

Sample Scenario

  • If you have oneStudent extends Person,useequalshour,StudentThe object will not be verified;isAssignableFromThen it will be checked.

2. validate(Object target, Errors errors): Perform verification!

  • effect: Write specific verification rules and record them when an error is foundErrorsObject.
  • Common toolsValidationUtilsSimplify non-empty checks.

Sample code

public void validate(Object target, Errors errors) {
    // Check whether name is empty    (errors, "name", "");
    Person person = (Person) target;
    // Check whether the age is legal    if (() < 0) {
        ("age", "", "Age cannot be negative!");
    }
}

3. Handle nested objects: How to avoid duplicate code?

Suppose you have oneCustomerClass, includingAddressObject:

public class Customer {
    private String firstName;
    private String surname;
    private Address address; // Nested objects}

Problem: Verify all fields directly in one Validator

shortcoming

  • If other categories (such asOrder) also includesAddress, the address verification code needs to be written repeatedly.
  • Maintenance difficulty: When modifying address rules, multiple codes need to be changed.

Correct way to do it:

Split Validator and use it in combination!

Step 1: Create an independent Validator for each class

  • AddressValidator(Check address):
public class AddressValidator implements Validator {
    @Override
    public boolean supports(Class<?> clazz) {
        return (clazz);
    }
    @Override
    public void validate(Object target, Errors errors) {
        (errors, "street", "");
        (errors, "city", "");
    }
}

CustomerValidator(Check the client and reuse the AddressValidator):

public class CustomerValidator implements Validator {
    private final Validator addressValidator;
    // Inject AddressValidator through constructor    public CustomerValidator(Validator addressValidator) {
         = addressValidator;
    }
    @Override
    public boolean supports(Class&lt;?&gt; clazz) {
        return (clazz);
    }
    @Override
    public void validate(Object target, Errors errors) {
        // 1. Verify the client's direct field (firstName, incident)        (errors, "firstName", "");
        (errors, "surname", "");
        Customer customer = (Customer) target;
        Address address = ();
        // 2. Verify nested Address objects        if (address == null) {
            ("address", "");
            return;
        }
        // 3. Key: Switch the wrong path to "address" to prevent field name conflicts        ("address");
        try {
            (addressValidator, address, errors);
        } finally {
            (); // Restore the original path        }
    }
}

Step 2: Actual use

// Create ValidatorAddressValidator addressValidator = new AddressValidator();
CustomerValidator customerValidator = new CustomerValidator(addressValidator);
// Prepare test dataCustomer customer = new Customer();
(""); // Empty name(new Address()); // Empty address// Perform verificationErrors errors = new BeanPropertyBindingResult(customer, "customer");
(customer, errors);
// Output errorif (()) {
    ().forEach(error -&gt; {
        ("Field:" + () + "." + ());
    });
}
// Output result:// Fields:// Fields:

4. Key Skills and Frequently Asked Questions

1. Error path management

pushNestedPathandpopNestedPath
Make sure the error fields of nested objects are prefixed (e.g.) to avoid conflicts with the field name of the main object.

2. Defensive programming

When combining Validator, check whether the injected Validator supports the target type:

public CustomerValidator(Validator addressValidator) {
    if (!()) {
        throw new IllegalArgumentException("The Address type must be supported!");
    }
     = addressValidator;
}

3. International support

Error code (such as) can correspond to language resource files (such asmessages_zh.properties), implement multilingual prompts:

# messages_zh.properties
=The name cannot be empty
=Street address cannot be empty

5. Summary: Why is it designed like this?

  • Code reuseAddressValidatorCan be verified by other classes (such asOrderCompany) Use directly.
  • Single Responsibility: Each Validator is only responsible for the verification of one class, with clear logic and easy to maintain.
  • Flexible expansion: Add a new nested object (such asPaymentInfo) When you just create a new Validator and inject it, no need to modify the existing code.

3.2. Parsing the error code into error message: In-depth analysis and instance demonstration

1. Core concept: Multi-level analysis of error codes

When you call in SpringrejectValueWhen the method registration error is wrong (for example, checking the user's age is illegal), Spring will not only record the single error code you specified, butAutomatically generate a set of hierarchical error codes. This design allows developers to flexibly define error messages through different levels of error codes to achieve a "specific to general" coverage strategy.

2. Error code generation rules

Assume thatPersonValidatorThe following verification logic is triggered:

("age", "");

Generated error code(From high to low by priority):

  • Field name + error code + field type
  • Field name + error code
  • Original error code

3. Matching strategy for message resource files

Spring'sMessageSourceWill follow the priority order of the error code in the message resource file (egFind the corresponding message in )Once a match is found, stop searching immediately

Sample message resource file

# 
=Age must be an integer and not exceed 120 age
=Can't exceed the age 120 age
=The input value is unreasonable

Matching process

  • Priority search→ Use if it exists.
  • If not found, look up→ Use if it exists.
  • Last search→ As a bottom-up message.

4. Practical demonstration: From code to error message

Step 1: Create entity class and verification device

// 
public class Person {
    private String name;
    private int age;
    // getters/setters
}
// 
public class PersonValidator implements Validator {
    @Override
    public boolean supports(Class<?> clazz) {
        return (clazz);
    }
    @Override
    public void validate(Object target, Errors errors) {
        Person person = (Person) target;
        if (() > 120) {
            ("age", "");
        }
    }
}

Step 2: Configure the message resource file

existsrc/main/resources/Definition in:

# Specific to field type=Age must be an integer and cannot exceed 120 age
# To specific fields=Can't exceed the age 120 age
# General error=The entered value is invalid

Step 3: Write the test code

@SpringBootTest
public class ValidationTest {
    @Autowired
    private MessageSource messageSource;
    @Test
    public void testAgeValidation() {
        Person person = new Person();
        (150); // Trigger an error        Errors errors = new BeanPropertyBindingResult(person, "person");
        PersonValidator validator = new PersonValidator();
        (person, errors);
        // Extract error message        ("age").forEach(error -&gt; {
            String message = ((), null, ());
            ("Error message:" + message);
        });
    }
}

Output result

Error message: Age must be an integer and cannot be older than 120 years old

Analysis
becauseIf it exists in the message file, use the message first. If you delete this line,It will match, and so on.

5. Custom error code generation strategy

DefaultDefaultMessageCodesResolverThe generated code format is:
Error code + field name + field type
If you need to modify the rules, you can customize itMessageCodesResolver

Example: Simplify error code

@Configuration
public class ValidationConfig {
    @Bean
    public MessageCodesResolver messageCodesResolver() {
        DefaultMessageCodesResolver resolver = new DefaultMessageCodesResolver();
        (.POSTFIX_ERROR_CODE);
        return resolver;
    }
}

Effect
CallrejectValue("age", "")The generated code becomes:

6. Frequently Asked Questions and Solutions

Question 1: How to view the actual generated error code?

Print the error object in the test code:

("age").forEach(error -&gt; {
    ("Error Code List:" + (()));
});

Output

Error code list: [, , ]

Question 2: How to represent field types in code

Spring uses a simple class name for a field (e.g.intString). For custom types (e.g.Address), will be used in the codeaddress(Class name lowercase).

7. Summary: Why do we need to hierarchically Error Codes?

  • Flexible coverage: Allows customization of messages for specific fields or types, and provides a universal guarantee.
  • International friendly: Different languages ​​can define messages at different levels without modifying code.
  • Code decoupling: Separate the verification logic from specific error messages to improve maintainability.

Study Suggestions

  • Observation through debugging()The output of the code generation rules are in-depth.
  • Priority to using field-level error codes in projects (e.g.) to improve the accuracy of error messages.

This is the article about Spring Validator Learning Guide: This is the end of this article about mastering object verification from scratch. For more related Spring Validator object verification content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!