首页 > 解决方案 > 使用自定义 javax 验证器时为存储库获取 NullPointerException

问题描述

我正在 Spring Boot 应用程序中处理 javax 验证 API。我有一个用户 bean,我必须验证请求中给出的用户名是唯一的并且不存在于数据库中。

我为此要求创建了自定义注释 ( UniqueUser.java) 和自定义验证器( )。UniqueUserValidator.java

public class User  {
    @NotNull
    @UniqueUser
    private String username;

    @NotNull
    private String password;

    @NotNull
    @Email
    private String email;

    @NotNull
    private String phone;
}

唯一用户.java

@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = UniqueUserValidator.class)
@Documented
public @interface NameMatch
{
    String message() default "User id already exists";

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

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

UniqueUserValidator.java

public class UniqueUserValidator implements ConstraintValidator<NameMatch, Object>
{
    @Autowired
    UserRepository userRepository;
   
    @Override
    public boolean isValid(String userName, final ConstraintValidatorContext context)
    {
        boolean isUserExist = false;
        if(userName!=null && !userName.isEmpty()) {
        Optional<Product> product = userRepository.findByUserId(userName);
           isUserExist = product.isPresent(); 
        }
        return isUserExist;
    }
}

我正在使用 Spring Data JPA 编写存储库类并从数据库中获取用户名。

用户存储库.java:

import org.springframework.data.repository.CrudRepository;

@Repository
interface UserRepository extends CrudRepository<User, Long> {

Optional<User> findByUserId();
 
}

验证配置.java

@Bean
public Validator validator (final AutowireCapableBeanFactory autowireCapableBeanFactory) {

    ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
        .configure().constraintValidatorFactory(new SpringConstraintValidatorFactory(autowireCapableBeanFactory))
        .buildValidatorFactory();
    Validator validator = validatorFactory.getValidator();

    return validator;
}

用户名验证测试.java

public class UserNameValidationTest
{

   @Autowired
   Validator validator;

    @Test
    public void whenExistingUser_thenFai()
    { 
        User user = new User();
        user.setUserName("testUSer"); //Existing user is set
        user.setPhone("12345678"); 
        user.setPassword("password123");
        user.setEmail("test@gmail.com");
 
        //Validate bean
        Set<ConstraintViolation<User>> constraintViolations = validator.validate(user);
 
        //Show errors
        if (constraintViolations.size() > 0) {
            for (ConstraintViolation<User> violation : constraintViolations) {
                System.out.println(violation.getMessage());
            }
        } else {
            System.out.println("Valid Object");
        }
        
        assertsEquals(true, constraintViolations.size()>0);
    }
}

在执行上述测试类时,我得到了NullPointerException自定义验证器类中的内容。有人可以帮我解决这个问题。userRepositorynull

标签: javaspringvalidationjunithibernate-validator

解决方案


当我们需要注入一些验证所需的 bean 时,这是使用 Hibernate 验证器的经典问题。

首先,userRepository内部为 null的原因UniqueUserValidator是因为您的 UniqueUserValidator 没有使用任何 Spring Bean 注释进行注释,因此甚至不会发生自动装配和依赖注入

现在,如果您考虑使用基于构造函数的in 注入,那么在您添加它的那一刻,您将在控制台中收到错误消息,抱怨找不到无参数的构造函数。userRepositoryUniqueUserValidator

错误:

java.lang.NoSuchMethodException: com.example.package.UniqueUserValidator.<init>()
...
...

要解决这个问题,你需要别的东西。一个休眠属性定制器。并摆脱您的 ValidationConfig.java。

@Component
public class ValidatorAddingCustomizer implements HibernatePropertiesCustomizer {

  private final ObjectProvider<Validator> provider;

  @Autowired
  public ValidatorAddingCustomizer(ObjectProvider<Validator> provider) {
    this.provider = provider;
  }

  @Override
  public void customize(Map<String, Object> hibernateProperties) {
    Validator validator = provider.getIfUnique();
    if (validator != null) {
      hibernateProperties.put("javax.persistence.validation.factory", validator);
    }
  }

}

最后,正如我所提到的,您需要修改UniqueUserValidator并在其中使用基于构造函数的注入,如下所示:

public class UniqueUserValidator implements ConstraintValidator<NameMatch, String>
{
    private final UserRepository userRepository;
    public UniqueUserValidator(UserRepository userRepository) {
      this.userRepository = userRepository;
    }
   
    @Override
    public boolean isValid(String userName, final ConstraintValidatorContext context)
    {
        ..... // whatever logic
    }
}

我注意到的另一件事是,在您的测试类中,您错过了两个类级别的注释@RunWith(SpringRunner.class)@SpringBootTest(classes = YourMainApplication.class)

我已经为此创建了一个要点。如果您需要整体图片,请查看。


推荐阅读