自定义验证器导致 StackOverflowException

Custom validator causes StackOverflowException

我想实现 ConstraintsValidator 在注册新用户之前验证电子邮件是否可用,使用 spring 的依赖注入 @Autowired 在验证器中注入 JPA 存储库以进行数据库搜索。

我更改了 hibernate 的验证器工厂,以便 spring 实例化验证器,以便我可以使用 @Autowired

一切正常,但好像验证进入了一个无限循环,导致 Whosebugexception.

注意:验证是自动完成的(我没有调用 validator.validate()),因为我使用的是 REST JPA 存储库

@Getter
@Setter
@Entity
@UniqueCompteEmail
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Compte implements Serializable, UserDetails {

    private static final long serialVersionUID = -5230227676515387462L;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Integer id;

    @NotBlank
    @NotNull
    @Column(unique = true)
    private String username;

    @NotNull
    @NotBlank
    @Size(min = 6)
    private String password;

    @Email
    @NotNull
    @NotBlank
    @Column(unique = true)
    private String email;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return new HashSet<GrantedAuthority>();
    }

    @Override
    public String getPassword() {
        return this.password;
    }

    @Override
    public String getUsername() {
        return this.username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }

    public abstract CompteType getTypeCompte();

    public abstract void setTypeCompte(CompteType typeCompte);

    public static enum CompteType {
        ETUDIANT, ADMINISTRATEUR
    }
}

@Repository
public interface CompteRepository extends JpaRepository<Compte, Integer> {

    public Optional<Compte> findByUsername(String username);

    public Optional<Compte> findByEmail(String email);
}

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UniqueCompteEmailValidator.class)
@Target({ ElementType.TYPE })
public @interface UniqueCompteEmail {

    String message() default "{com.mssmfactory.bacsimulator.uniquecompteemail.message}";

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

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

public class UniqueCompteEmailValidator implements ConstraintValidator<UniqueCompteEmail, Compte> {

    @Autowired
    private CompteRepository compteRepository;

    @Override
    public void initialize(UniqueCompteEmail constraintAnnotation) {
    }

    @Override
    public boolean isValid(Compte value, ConstraintValidatorContext context) {
        if (value != null) {
            Optional<Compte> compte = this.compteRepository.findByEmail(value.getEmail());

            return !compte.isPresent();
        } else
            return false;
    }
}

@Component
public class ValidatorAddingCustomizer implements HibernatePropertiesCustomizer {

    @Autowired
    private ValidatorFactory validatorFactory;

    public void customize(Map<String, Object> hibernateProperties) {
        if (validatorFactory != null) {
            hibernateProperties.put("javax.persistence.validation.factory", validatorFactory);
        }
    }
}

由于您没有定义任何事件,因此将为所有事件调用验证器。

事件是(参见here):

  • BeforeCreateEvent
  • AfterCreateEvent
  • BeforeSaveEvent
  • AfterSaveEvent
  • BeforeLinkSaveEvent
  • AfterLinkSaveEvent
  • BeforeDeleteEvent
  • AfterDeleteEvent

并且因为您要从数据库中获取验证方法,所以您陷入了无限循环。

你只需要用一个对流名称来注释验证器。

@Component("beforeCreateCompteValidator")
public UniqueCompteEmailValidator implements Validator<Compte>{
}

并删除其他注释。

Spring 识别此验证器并附加到右钩子。

有关详细信息,请参阅 Spring Validation