At the beginning of heaven and earth to open

Five years ago, tech Building B, floor 1.

Xiao Ming’s eyes straight hook staring at the screen, hands crackling on the keyboard.

There is no thinking, thinking will only make Xiao Ming’s speed down.

Good programmers don’t need to think at all, just as they don’t need to write documentation and comments.

“It’s really a simple need,” Said Xiao Ming. “There’s no challenge.”

Like countless web developers, What Ming does today is a user registration function.

First define the corresponding user registration object:

public class UserRegister {

    /** * Name */
    private String name;

    /** * Original password */
    private String password;

    /** * Confirm password */
    private String password2;

    /** * Gender */
    private String sex;

    // getter & setter & toString()
}
Copy the code

The document also has a simple restriction:

(1) Name The name must be between 1 and 32 bits

(2) Password The password must be a string of 6-32 characters

(3) password2 Confirm password must be the same as password

(4) Sex must be either BOY or GIRL.

“It’s not hard,” the relentless coding machine began to pound away at the keyboard, and soon the basic verification method was written:

private void paramCheck(UserRegister userRegister) {
    / / 1. Name
    String name = userRegister.getName();
    if(name == null) {
        throw new IllegalArgumentException("Name must not be empty");
    }
    if(name.length() < 1 || name.length() > 32) {
        throw new IllegalArgumentException("Names must be between 1 and 32 in length.");
    }

    / / 2. The password
    String password = userRegister.getPassword();
    if(password == null) {
        throw new IllegalArgumentException("Password cannot be empty.");
    }
    if(password.length() < 6 || password.length() > 32) {
        throw new IllegalArgumentException("Password must be between 6 and 32 in length.");
    }
    //2.2 Confirm password
    String password2 = userRegister.getPassword2();
    if(! password.equals(password2)) {throw new IllegalArgumentException("Confirm password must be the same as password.");
    }

    / / 3. Gender
    String sex = userRegister.getSex();
    if(! SexEnum.BOY.getCode().equals(sex) && ! SexEnum.GIRL.getCode().equals(sex)) {throw new IllegalArgumentException("Gender must be specified as GIRL/BOY"); }}Copy the code

After finishing work, Xiaoming submitted the code, and ran away from work early.

First Hibernate Validator

“Xiao Ming, I simply looked at your code today.” “Said the project manager, seemingly casually.

Xiao Ming stopped his work and looked at the project manager, meaning to ask him to continue.

“The whole is still relatively rigorous, but write too much verification code.”

“Too much verification code? Do not verify the data users fill in how to do?” Xiao Ming didn’t understand.

“If you have time to check your code, check out the Hibernate-Validator framework.”

“Yes, I have time to look.”

Said, Xiao Ming heart ten thousand do not want to.

What kind of hibernation frame is affecting how fast I can move bricks.

Later, Xiao Ming was reluctant to search for hibernate-Validator and felt good.

The framework provides a number of built-in annotations to facilitate the development of daily validation and greatly improve the reusability of validation methods.

Therefore, Xiaoming improved his calibration method:

public class UserRegister {

    /** * Name */
    @notnull (message = "Name cannot be empty ")
    @length (min = 1, Max = 32, message = "name Length must be between 1 and 32 ")
    private String name;

    /** * Original password */
    @notnull (message = "Password must not be empty ")
    @length (min = 1, Max = 32, message = "password must be between 6 and 32 ")
    private String password;

    /** * Confirm password */
    @notnull (message = "Confirm password cannot be null ")
    @length (min = 1, Max = 32, message = "confirm password must be between 6 and 32 ")
    private String password2;

    /** * Gender */
    private String sex;

}
Copy the code

The verification method is adjusted as follows:

private void paramCheck2(UserRegister userRegister) {
    / / 1. Name
    ValidateUtil.validate(userRegister);

    //2.2 Confirm password
    String password2 = userRegister.getPassword2();
    if(! userRegister.getPassword().equals(password2)) {throw new IllegalArgumentException("Confirm password must be the same as password.");
    }

    / / 3. Gender
    String sex = userRegister.getSex();
    if(! SexEnum.BOY.getCode().equals(sex) && ! SexEnum.GIRL.getCode().equals(sex)) {throw new IllegalArgumentException("Gender must be specified as GIRL/BOY"); }}Copy the code

ValidateUtil is based on a simple utility class:

public class ValidateUtil {

    /** * Use Hibernate annotations to validate */
    private  static Validator validator = Validation
            .byProvider(HibernateValidator.class)
            .configure().failFast(true)
            .buildValidatorFactory()
            .getValidator();

    public static <T> void validate(T t) {
        Set<ConstraintViolation<T>> constraintViolations = validator.validate(t);
        // Throw a validation exception
        if (constraintViolations.size() > 0) {
            final String msg = constraintViolations.iterator().next().getMessage();
            throw newIllegalArgumentException(msg); }}}Copy the code

But Xiao Ming is still not satisfied. Can sex check be further optimized?

The answer is yes, xiao Ming found that Hibernate-Validator supports custom annotations.

This is a powerful feature, and a good framework should provide users with more possibilities.

So Xiao Ming implemented a custom annotation:

@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = MyEnumRangesValidator.class)
public @interface MyEnumRanges {

    Class<? extends Enum> value();

    String message(a) default "";

}
Copy the code

MyEnumRangesValidator is implemented as follows:

public class MyEnumRangesValidator implements
        ConstraintValidator<MyEnumRanges.String> {

    private MyEnumRanges myEnumRanges;

    @Override
    public void initialize(MyEnumRanges constraintAnnotation) {
        this.myEnumRanges = constraintAnnotation;
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return getEnumValues(myEnumRanges.value()).contains(value);
    }

    /** * Get information about the enumeration value **@paramEnumClass Enumeration class *@returnEnumeration description@since0.0.9 * /
    private List<String> getEnumValues(Class<? extends Enum> enumClass) {
        Enum[] enums = enumClass.getEnumConstants();

        return ArrayUtil.toList(enums, new IHandler<Enum, String>() {
            @Override
            public String handle(Enum anEnum) {
                returnanEnum.toString(); }}); }}Copy the code

Limit the current field value to within the specified enumeration range. Use this annotation for all future references to the enumeration range.

Then add @myEnumranges to the sex field:

@notnull (Message = "gender must not be null ")
@myenumranges (message = "gender must be within BOY/GIRL range ", value = SexEnum. Class)
private String sex;
Copy the code

In this way, the verification method can be simplified as follows:

private void paramCheck3(UserRegister userRegister) {
    / / 1. Name
    ValidateUtil.validate(userRegister);
    //2.2 Confirm password
    String password2 = userRegister.getPassword2();
    if(! userRegister.getPassword().equals(password2)) {throw new IllegalArgumentException("Confirm password must be the same as password."); }}Copy the code

Xiao Ming smiled with satisfaction.

But his smile only lasted a moment, for he found something unsatisfactory.

Can I get rid of the password confirmation code?

It seems that you can’t do this directly using the Hibernate-Validator framework.

Shortcomings of the framework

All this makes Xiao Ming very painful, he found that the framework itself does have a lot of shortcomings.

Scenarios that hibernate-Validator cannot satisfy

The most popular Framework for Java today is Hibernate-Validator, but there are some scenarios that can’t be satisfied.

Such as:

  1. Verify that the new password is the same as the confirm password. (Relationships between different properties of the same object)

  2. When the value of one attribute meets a certain condition, the parameter verification of other values is performed.

  3. Multiple property values, at least one of which cannot be null

In fact, hibernate-Validator is weak when dealing with association relationships between multiple fields.

This project combines the original advantages to carry out this function enhancement.

Validation-api is too complex

The validation-API provides rich feature definitions, but it also presents a problem.

It’s very complicated to implement.

In practice, however, we often do not need such a complex implementation.

Valid-api provides a much simpler SET of apis for users to implement themselves.

Customization is inflexible

Hibernate-validator in use, custom constraint implementations are annotation-based and not flexible enough to validate individual attributes.

In this project, attribute verification constraints and annotation constraints are distinguished to facilitate reuse and extension.

Procedural programming vs. annotated programming

The Hibernate-Validator core supports annotated programming and bean-based validation.

One problem is that property validation is not flexible, and sometimes for bean validation, you still have to write it yourself.

This project supports fluent-API for procedural programming and annotated programming.

Combine flexibility and convenience as much as possible.

The birth of the Valid tool

So Xiao Ming spent a long time to write a verification tool, hoping to make up for the above tools.

Open source address: github.com/houbb/valid

features

  • Support fluent – validation

  • Support jSR-303 annotations, support all hibenrate-Validator commonly used annotations

  • Support i18n

  • User-defined policies are supported

  • Supports user-defined annotations

  • Support validation for attributes

  • Support procedural programming and annotated programming

  • You can specify the validation conditions

Quick start

Maven is introduced into

<dependency>
    <groupId>com.github.houbb</groupId>
    <artifactId>valid-jsr</artifactId>
    <version>0.2.2</version>
</dependency>
Copy the code

coding

The tool class uses:

User user = new User();
user.sex("what").password("old").password2("new");

ValidHelper.failOverThrow(user);
Copy the code

Error as follows:

A ValidRuntimeException is thrown with the following information:

Name: value <null> is not expected,password: value <old> is not expected,sex: value <what> is not expectedCopy the code

The definition of User is as follows:

public class User {

    /** * Name */
    @HasNotNull({"nickName"})
    private String name;

    /** * nickname */
    private String nickName;

    /** * Original password */
    @AllEquals("password2")
    private String password;

    /** * New password */
    private String password2;

    /** * Gender */
    @Ranges({"boy", "girl"})
    private String sex;

    /** * Fail type enumeration */
    @EnumRanges(FailTypeEnum.class)
    private String failType;

    //Getter and Setter
}
Copy the code

Built-in annotations are introduced as follows:

annotations instructions
@AllEquals The values of the current field and the specified field must all be equal
@HasNotNull At least one value of the current field and the specified field is not null
@EnumRanges The current field value must be within the scope of the enumeration attribute
@Ranges The current field value must be within the specified property range

Xiaoming made some minor improvements in hibernater when designing the verification tool.

You can make connections between fields to provide more power.

Each annotation has its own procedural method, allowing you to switch between annotations and procedures.

The @condition annotation validation Condition is built in to make annotation validation more flexible.

Xiao Ming looked up at the clock on the wall. The night was already too deep. Seeing is better than hearing a hundred stories.

Open source address: github.com/houbb/valid

summary

This open source tool is the product of everyday work that doesn’t want to write too many validation methods. It’s still in its early stages and has a lot of work to do.

I hope you like it, though.

I am an old horse, looking forward to the next reunion with you.