【问题标题】:Getting NullPointerException for repository while using custom javax validator使用自定义 javax 验证器时为存储库获取 NullPointerException
【发布时间】:2020-07-19 20:13:36
【问题描述】:

我正在开发 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;
}

UniqueUser.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 编写 Repository 类并从 DB 中获取用户名。

UserRepository.java:

import org.springframework.data.repository.CrudRepository;

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

Optional<User> findByUserId();
 
}

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

UserNameValidationTest.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,因为 userRepository 在自定义验证器类中是 null。有人可以帮我解决这个问题吗?

【问题讨论】:

  • 删除你的 validator 方法(Spring 已经完成了所有这些以及更多开箱即用的操作,并确保你的测试用例使用正确的配置(因为配置太少,无法判断)有关正在运行的完整测试的信息)。
  • @user3244519 请查看答案,希望对您有所帮助

标签: java spring validation junit hibernate-validator


【解决方案1】:

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

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

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

错误:

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)

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

【讨论】:

  • 我们是否应该实例化一个 UniqueUserValidator 对象,明确地将 UserRepository 对象传递给它?谢谢
  • 没有@dushkin。 Spring 负责实例化。
  • 我添加了 ValidatorAddingCustomizer 类。我导入了 java.util.Map、javax.validation.Validator 和 org.springframework.beans.factory.ObjectProvider。那是对的吗?我还完全按照您的说明修改了 Validator 类。请注意,我的问题不是在测试时,而是在调用 REST API 时,我需要使用 RestController 注释类而不是存储库类,并且我使用 Spring Boot 2.3.5。当我可以使用 API 时,我得到:“HV000064:无法实例化 ConstraintValidator:com.ttt.bbsim.validation.TeamIdValidator”。请帮忙:(
  • 你听懂了我的意思吗?我上面给出的链接
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多