自定义验证器导致 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
我想实现 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