I use custom annotations to gracefully realize the complex verification of business

background

Suppose we want to develop a function of creating super club members. The conditions for creation are:

  1. The id requirements of members are [1-10]
  2. The phone number should start with 185
  3. The registration date cannot be less than May 15

Usually we would write this:

public void creatSupperClubMember(Member member) {
  //1. Verify member id
  //2. Check the phone number
  //3. Verify the registration date
  
  //4. Create super club members
}
copy

In the above method, we found that in addition to the creation of super members, more than half of our code is checking parameters. The problem is that the code we really need is 4. To some extent, 1, 2 and 3 are redundant code.

If our verification is very complex, this method will be difficult to read. Through analysis, we find that this method actually does two things

  1. Calibration parameters
  2. Create super member

Therefore, we refactor the code as follows:

public void creatSupperClubMember(Member member) {
 //1. Parameter verification
    validatorBeforeCreate()
 //2. Create super club members
}

public void validatorBeforeCreate() {
  //1. Verify member id
  //2. Check the phone number
  //3. Verify the registration date
}
copy

This will make our code clearer. In fact, when writing methods, we need to consider the principle of single responsibility. The parameter verification of business belongs to non business code to some extent. The above functions can be abstracted as follows:

check

Yes, business logic and non business logic. Is there any way to decouple business logic from non business logic.

We can use annotation verification.

In fact, in our usual development, we use annotation verification in many places:

I believe everyone has written the above code. We don't need to write parameter verification in the method. We use annotation on the field to realize the required verification and range verification of parameters.

However, the existing annotations can not meet our requirements, and the actual parameter verification is more complex. So I decided to write a parameter verification annotation myself.

Annotation model

What I want is

@ValidatorHandler(validators = XXXXValidator.class)
public int createXXX(XXX xxx) {

}
copy

We add a @ ValidatorHandler annotation to the business code we write. The XXXXValidator class in the annotation is the class that really writes the verification function, and the business parameters will be transferred to this verification class.

public class XXXValidator extends AbstractValidator {
 @Override
 public void check(Object o) {
        //Get parameters
  XXXx xxx = (XXX)o;
        //Related business verification
 }
}
copy

The verification class model is shown above.

annotation

We separate the business code from the verification code through annotations.

Write verification notes

First, let's define the annotation:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidatorHandler {
    /**
     *  Checked class
     */
    Class<?> validators();
}
copy

In terms of the function and method of this annotation, the parameter validators is the class of the verification class.

Then write and implement the function of verifying annotations

In order to show the code clearly, I use pictures.

The above code realizes several functions:

"Instantiate the validation class corresponding to validators" Get the business parameters and pass them to the parameters of the check method in the check class Execute the check method in the check class Execute business code "

In order to standardize the writing of verification classes, we need to define an interface

public interface AbstractValidator {
    void check(Object o);
}
copy

All validation classes must implement AbstractValidator and implement the check method.

In the annotation implementation function here, I only get the first parameter in the business function, that is, the first parameter of our business method will be verified. You can think about this: if there are multiple parameters of the business function, how to write the function class of the annotation?

Code test

After the above comments have been developed, we begin to test the following functions:

First, we write a verification class:

As shown in the figure above, in the verification class, we have implemented the three verification functions required at the beginning of the article.

Then we write business code and create super club members

@ValidatorHandler(validators = MemberValidator.class)
public void creatSupperClubMember(Member member) {
    System.out.println("member Start adding");
    //Here is our business code
    System.out.println("member End of addition");
}
copy

Let's write another method to simulate the incoming data from the front end:

The test method is as follows:

@Test
public void testCreateMember() {
    Member member = initData();
    testMemberService.creatSupperClubMember(member);
    System.out.println("End of creation");
}
copy

Verification id

private Member initData() {
    Member member = new Member();
    member.setId("11");
    member.setMobile("17790990033");
    member.setCreateBy("Miss Li");
    member.setCreateDate(new Date());
    member.setIsActive("Y");
    return member;
}
copy

Execute the test method and find that the console prints as follows. We find that id = 11 does not meet the requirements and the verification is successful.

We modify id = 5, and the verification of ID passes.

member.setId("5");
copy

Verify phone number moblie

Then we execute it again and find that the mobile phone number does not start with 185, and the verification here is also successful.

If the mobile phone number is changed to 185, the verification is passed.

member.setMobile("18590990033");
copy

Verification registration date

Among the parameters of member, the registration date is 2021-05-15, which is less than 2021-05-20, which does not meet the requirements.

private Member initData() {
    String registerDateStr = "2021-05-15";
    Date registerDate = DateUtil.parse(registerDateStr);

    Member member = new Member();
    member.setId("5");
    member.setMobile("18590990033");
    member.setCreateBy("Miss Li");
    member.setCreateDate(registerDate);
    member.setIsActive("Y");
    return member;
}
copy

The following errors will appear on the console, and the verification is successful.

Check all pass

private Member initData() {
    Member member = new Member();
    member.setId("5");
    member.setMobile("18590990033");
    member.setCreateBy("Miss Li");
    member.setCreateDate(new Date());
    member.setIsActive("Y");
    return member;
}
copy

When all our parameters meet the business verification requirements, our verification passes, and a super club member will be created.

The above is the whole content of this article. When our business logic verification is very complex, we can use the verification annotation above to separate the verification logic from the business logic, which is conducive to the decoupling of business and non business, and also meets the single responsibility principle of the design principle. In addition to being easy to read, another advantage is that when we don't need verification, we can comment out the verification annotation on the business method, so we don't have to modify it in the business code, so as to reduce the risk of bug s caused by modifying the business code.

If the article is useful to you, you are welcome to like it and forward it 😀.

Posted by BlueSkyIS on Thu, 05 May 2022 17:35:04 +0300