Spring Custom Validation 

When we use @Validated, we might want to build our own custom validator.
@Validatedを使用する場合は、独自のバリデータを作成することもできます。

Let's assume that we want to check whether the email the user inserts already exists. We want a more particular validation logic preventing the email address to be duplicated rather than predefined ones.
ユーザが挿入した電子メールがすでに存在するかどうかを確認したと仮定します。 Eメールアドレスが事前に定義されたものではなく重複しないように、より詳細な検証ロジックが必要です。

# 1. Define @interface
Using the @Constraint annotation, we specify the class that is going to validate the value of our field.
The message() can be either imported from a messageSource that has been set up, or we can have the message set to default will be showed in the user interface.
Constraint　annotationを使用して、フィールドの値を検証するクラスを指定します。
message() は、設定された messageSource からインポートするか、メッセージをデフォルトに設定してユーザ インターフェイスに表示することができます。


[Oracle Docs](https://docs.oracle.com/javase/7/docs/api/java/lang/annotation) explains annotations to this interface class as follows:
> 
@Retention - Indicates how long annotations with the annotated type are to be retained.
注釈タイプを持つ注釈が保持される期間を示します。
@Documented - Indicates that annotations with a type are to be documented by javadoc and similar tools by default. 
デフォルトでは、javadoc および同様のツールによってタイプを持つ注釈が文書化されることを示します。
@Target - Indicates the contexts in which an annotation type is applicable.
注釈タイプが適用されるコンテキストを示します。




```java
package com.example.user.annotation;

import com.example.common.DuplicateValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = DuplicateValidator.class)
public @interface Duplicate {

    public String message() default "";

    public Class<?>[] groups() default {};

    public Class<? extends Payload>[] payload() default{};


}

```


#2. Implements ConstaintValidator

```java

...
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.text.MessageFormat;

@Component
public class DuplicateValidator implements ConstraintValidator<Duplicate, String> {
    @Autowired
    private UserService userService;

    @Override
    public void initialize(Duplicate emailDuplicate) {

    }

    @Override
    public boolean isDuplicated(String email, ConstraintValidatorContext context){
        User user = userService.findByEmail(email);
        if (user != null) {
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate(
                MessageFormat.format("{0} already exists。", email)).addConstraintViolation();
        }
            return user==null;
        }
    }
```

#3. Apply it to the corresponding field in DTO


```java
import lombok.*;
import javax.validation.constraints.*;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserForm {

    /**
     * Name
     */
    @NotBlank
    private String firstName;

    /**
     * LastName
     */
    @NotBlank
    private String lastName;

    /**
     * メールアドレス
     */
    @NotBlank
    @Email
    @Duplicate
    private String email;

    /**
     * パスワード
     */
    @NotBlank
    private String password;

}
```

#4. Put @Validated annotation to Controller
In order for the DTO(which receives the request) to be validated, you should put @Validated annotation on the request.
DTO(要求を受信する)を検証するには、＠Validated annotationをrequestに挿入する必要があります。

```java
/**
 * sign-up 
 *
 * @param Register
 * @return
 * @throws Exception
 */
@PostMapping("/sign_up")
public ResponseEntity<?> userSignUp(@Validated SignUpForm signUpForm) {
    return ResponseEntity.ok(userService.insertUser(signUpForm));
}

```

#Tip
We should consider carefully when applying these validators.
When we mount an update function, the above validation would not work properly. This customized validation won't let us update user information without changing email address because it would recognize your email as the duplicated input. 
これらのバリデータを適用する際には、慎重に検討する必要があります。
アップデート機能を実装すると、上記の検証が正しく動作しません。 このカスタマイズされた検証では、Eメールアドレスを変更せずにユーザー情報を更新することはできません。これは、お客様のEメールを重複入力として認識するためです。

Keep in mind that we can not override the validator in inherited classes. 
継承されたクラスのバリデータを上書きすることはできません。

Reference about this issue : [stackoverflow]
(https://stackoverflow.com/questions/21841202/spring-mvc-validation-of-inherited-classes)