当前位置: 首页 > 知识库问答 >
问题:

JSR-303自定义验证程序中未自动连接依赖项

蒯坚白
2023-03-14

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

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

@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中没有自动配置。字段总是null。

我正在使用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个答案

南宫星波
2023-03-14

几个月前我也处理过同样的问题。不要自动构建存储库,而是通过注释传递已经使用相同存储库的服务。

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

@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;

注意传递到annotation(Class)中的服务

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;
    }
}

高奇
2023-03-14

UserRepository实现需要像“@Repository”或“@Component”或“@Service”这样的注释。用户服务通过构造函数获取存储库实例。可能使用了“new UserRepositoryDao()”调用。在你的验证器中,你试图自动连线。我猜它要么没有被注释为服务,要么没有在spring上下文路径中加载,要么在spring中作为spring bean加载。xml

柯默
2023-03-14

您需要在用户前面的公共字符串createUser(@modeldattribute(“newUser”)User newUser)中的实体类前面添加@Valid注释。

@RequestBody @Valid User user
 类似资料:
  • 我有一个可以创建和编辑组的应用程序。每个团体都有一个独特的头衔。当有人试图创建新组时,我会检查是否有具有该标题的组,如果有,则验证失败。问题是,当有人试图编辑组而不更改标题时,根据我的自定义验证,有一个组具有该标题(有人正在编辑的标题),而我的验证失败,因此用户无法保存该组。所以我想问一下,是否有人能解决这个问题。非常感谢您的回答! 我的班级 我的验证器接口 我的验证器

  • 我正在用Spring和Thymeleaf填表: MyForm如下所示: 正如您所看到的,我做了一个自定义注释,它应该检查输入值是否可以解析为: 现在在我的Controller类中,我正在执行以下操作: 但是,当试图将放入文本字段以测试验证时,获取: 下面是我读到的示例,并希望用自定义验证器进行扩展:http://viralpatel.net/blogs/spring-mvc-hashmap-for

  • 我的bean中有两个字段 当字段key=“A”,“value”应该跟在其他“key”的特定Regex后面时,它可以是任何内容。 如何根据键定义此值验证。

  • 编写自定义约束时,一个验证器实现可以验证多个注释。例如,我有几个注释,它们规定了不同的@size注释,但我希望它们都指向同一个验证器类,该类执行一些全局检查,即所有注释都必须与某个正则表达式匹配。据我所知,该实现采用了一种注释类型。 一个注释 验证器

  • 我注意到,在Spring中,当声明使用注释的自定义验证器bean时,JSR-303验证被完全忽略。有趣的是,自定义验证器甚至不需要任何类填写或使用。Spring扫描其组件的事实似乎足以让Spring在对象绑定期间跳过JSR-303验证。从自定义验证器中删除,然后重新启动web应用程序,即可按预期启用JSR-303验证。使用注释自定义验证器有其用途,例如使用Spring Autowire依赖项。 考

  • 我试图创建一个类级别的JSR-303验证定义,用于检查一个属性是否在时间上先于另一个属性出现。因为这个验证只对日历属性有意义,所以我想知道是否可以在initialize方法中测试属性类型。 我的注释定义是: 以及验证程序实现: 这样做明智吗?如果明智,您建议我使用什么方法?如果属性类型不正确,应该采取什么行动?