在JSR-303自定义验证程序中未自动关联依赖项

时间:2018-08-28 22:21:00

标签: java spring bean-validation

我有一个包含User类的Spring Boot应用程序-所有字段都有标准的JSR-303批注(@ NotNull,@ Size等),并且验证工作正常。

但是,当我向用户添加自定义验证时,我无法将依赖项注入到自定义验证器中:

@Component
public class UniqueUsernameValidator implements 
ConstraintValidator<UniqueUsername, String> {

@Autowired
private UserRepository userRepository;

@Override
public boolean isValid(String username, ConstraintValidatorContext context) {
    // implements logic
}

@UniqueUsername批注声明为:

@Documented
@Retention(RUNTIME)
@Target({FIELD, ANNOTATION_TYPE, PARAMETER})
@Constraint(validatedBy = UniqueUsernameValidator.class)
@interface UniqueUsername {
   String message() default "{com.domain.user.nonUniqueUsername}";
   Class<?>[] groups() default { };
   Class<? extends Payload>[] payload() default { };
}

带注释的字段:

@NotBlank
@Size(min = 2, max = 30)
@UniqueUsername
private String username;

验证器用法:

@Service
public final class UserService {

   private final UserRepository userRepository;
   private final Validator validator;

   public UserService(UserRepository userRepository, Validator validator) 
   {
       this.userRepository = userRepository;
       this.validator = validator;
   }

   public void createUser(User user) {
      Set<ConstraintViolation<User>> validate = validator.validate(user);
      // logic...
   }
}

问题是UserRepository没有在UniqueUsernameValidator中自动接线。字段始终为空。

我正在使用LocalValidatorFactoryBean。

有人知道为什么自动布线不起作用吗?


@Controller
public class UserController {

private final UserService userService;

public UserController(UserService userService) {
    this.userService = userService;
}

@PostMapping("/user/new")
public String createUser(@ModelAttribute("newUser") User newUser, BindingResult bindingResult,
                         Model model) {
    userService.createUser(newUser);
    // omitted
}

3 个答案:

答案 0 :(得分:2)

您需要在User前面的公共String createUser(@ModelAttribute(“ newUser”)User newUser)中的实体类前面添加@Valid注释。

@RequestBody @Valid User user

答案 1 :(得分:0)

UserRepository实现需要一个注释,例如“ @Repository”或“ @Component”或“ @Service”。您的UserService通过构造函数获取资源库实例。也许使用了“ new UserRepositoryDao()”调用。在您的验证器中,您正在尝试自动接线。我想它要么未标注为服务,要么未标注在spring上下文路径中,或者未标注为spring.xml中的spring Bean

答案 2 :(得分:0)

几个月前,我已经处理过同样的问题。代替自动装配存储库,通过注释传递已经使用相同存储库的服务。

声明注释以接受唯一字段和执行验证的服务。

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

    String message() default "{com.domain.user.nonUniqueUsername}";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};

    Class<? extends UniqueUsernameValidation> service();      // Validating service
    String fieldName();                                       // Unique field
}

像下面这样在POJO上使用它:

@NotBlank
@Size(min = 2, max = 30)
@UniqueUsername(service = UserService.class, fieldName = "username")
private String username;

请注意,传递到注释(Class<? extends UniqueUsernameValidation> service())中的服务必须实现UniqueUsernameValidation接口。

public interface UniqueUsernameValidation {
    public boolean isUnique(Object value, String fieldName) throws Exception;
}

现在使传递的UserService实现上面的接口并覆盖它的唯一方法:

@Override
public boolean isUnique(Object value, String fieldName) throws Exception {
    if (!fieldName.equals("username")) {
        throw new Exception("Field name not supported");
    }

    String username = value.toString();
    // Here is the logic: Use 'username' to find another username in Repository
}

请不要忘记处理注释的UniqueUsernameValidator

public class UniqueUsernameValidator implements ConstraintValidator<UniqueUsername, Object>
{
    @Autowired
    private ApplicationContext applicationContext;

    private UniqueUsernameValidation service;

    private String fieldName;

    @Override
    public void initialize(UniqueUsername unique) {
        Class<? extends UniqueUsernameValidation> clazz = unique.service();
        this.fieldName = unique.fieldName();
        try {
            this.service = this.applicationContext.getBean(clazz);
        } catch(Exception ex) {
            // Cant't initialize service which extends UniqueUsernameValidator
        }
    }

    @Override
    public boolean isValid(Object o, ConstraintValidatorContext context) {
        if (this.service !=null) {
            // here you check whether the username is unique using UserRepository
            return this.service.isUnique(o, this.fieldName))                    
        }
        return false;
    }
}