2017-10-05 2 views
4

Ich habe ein kleines Beispielprojekt erstellt, um zwei Probleme zu zeigen, die bei der Konfiguration der Spring Boot-Validierung und ihrer Integration in Hibernate auftreten. Ich habe bereits andere Antworten ausprobiert, die ich über das Thema gefunden habe, aber leider funktionierten sie nicht für mich oder die Hibernate-Validierung wurde deaktiviert.Inject Repository in ConstraintValidator mit Spring 4 und Nachrichteninterpolationskonfiguration

Ich möchte einen benutzerdefinierten Validator implementieren ConstraintValidator<ValidUser, User> und injizieren Sie es in meine UserRepository. Gleichzeitig möchte ich das Standardverhalten von Hibernate beibehalten, das bei Update/Persist nach Validierungsfehlern sucht.

Ich schreibe hier zur Vollständigkeit Hauptabschnitte der App.

Individuelle Konfiguration In dieser Klasse I mit einem benutzerdefinierten MessageSource einen benutzerdefinierten Validator gesetzt, so Frühling Nachrichten aus der Datei resources/messages.properties

@Configuration 
public class CustomConfiguration { 

    @Bean 
    public MessageSource messageSource() { 
     ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); 
     messageSource.setBasenames("classpath:/messages"); 
     messageSource.setUseCodeAsDefaultMessage(false); 
     messageSource.setCacheSeconds((int) TimeUnit.HOURS.toSeconds(1)); 
     messageSource.setFallbackToSystemLocale(false); 
     return messageSource; 
    } 

    @Bean 
    public LocalValidatorFactoryBean validator() { 
     LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean(); 
     factoryBean.setValidationMessageSource(messageSource()); 
     return factoryBean; 
    } 

    @Bean 
    public MethodValidationPostProcessor methodValidationPostProcessor() { 
     MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor(); 
     methodValidationPostProcessor.setValidator(validator()); 
     return methodValidationPostProcessor; 
    } 

} 

Die Bohne Nichts Besonderes hier, wenn nicht der Brauch lesen Prüfer @ValidUser

@ValidUser 
@Entity 
public class User extends AbstractPersistable<Long> { 
    private static final long serialVersionUID = 1119004705847418599L; 

    @NotBlank 
    @Column(nullable = false) 
    private String name; 

    /** CONTACT INFORMATION **/ 

    @Pattern(regexp = "^\\+{1}[1-9]\\d{1,14}$") 
    private String landlinePhone; 

    @Pattern(regexp = "^\\+{1}[1-9]\\d{1,14}$") 
    private String mobilePhone; 

    @NotBlank 
    @Column(nullable = false, unique = true) 
    private String username; 

    @Email 
    private String email; 

    @JsonIgnore 
    private String password; 

    @Min(value = 0) 
    private BigDecimal cashFund = BigDecimal.ZERO; 

    public User() { 

    } 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    public String getLandlinePhone() { 
     return landlinePhone; 
    } 

    public void setLandlinePhone(String landlinePhone) { 
     this.landlinePhone = landlinePhone; 
    } 

    public String getMobilePhone() { 
     return mobilePhone; 
    } 

    public void setMobilePhone(String mobilePhone) { 
     this.mobilePhone = mobilePhone; 
    } 

    public String getUsername() { 
     return username; 
    } 

    public void setUsername(String username) { 
     this.username = username; 
    } 

    public String getEmail() { 
     return email; 
    } 

    public void setEmail(String email) { 
     this.email = email; 
    } 

    public String getPassword() { 
     return password; 
    } 

    public void setPassword(String password) { 
     this.password = password; 
    } 

    public BigDecimal getCashFund() { 
     return cashFund; 
    } 

    public void setCashFund(BigDecimal cashFund) { 
     this.cashFund = cashFund; 
    } 

} 

Benutzerdefinierter Validator Hier versuche ich, das Repository zu injizieren. Das Repository ist immer null, wenn ich die Hibernate-Validierung nicht deaktiviere.

public class UserValidator implements ConstraintValidator<ValidUser, User> { 
    private Logger log = LogManager.getLogger(); 

    @Autowired 
    private UserRepository userRepository; 

    @Override 
    public void initialize(ValidUser constraintAnnotation) { 
    } 

    @Override 
    public boolean isValid(User value, ConstraintValidatorContext context) { 
     try { 
      User foundUser = userRepository.findByUsername(value.getUsername()); 

      if (foundUser != null && foundUser.getId() != value.getId()) { 
       context.disableDefaultConstraintViolation(); 
       context.buildConstraintViolationWithTemplate("{ValidUser.unique.username}").addConstraintViolation(); 

       return false; 
      } 
     } catch (Exception e) { 
      log.error("", e); 
      return false; 
     } 
     return true; 
    } 

} 

messages.properties

#CUSTOM VALIDATORS 
ValidUser.message = I dati inseriti non sono validi. Verificare nuovamente e ripetere l'operazione. 
ValidUser.unique.username = L'username [${validatedValue.getUsername()}] è già stato utilizzato. Sceglierne un altro e ripetere l'operazione. 

#DEFAULT VALIDATORS 
org.hibernate.validator.constraints.NotBlank.message = Il campo non può essere vuoto 

# === USER === 
Pattern.user.landlinePhone = Il numero di telefono non è valido. Dovrebbe essere nel formato E.123 internazionale (https://en.wikipedia.org/wiki/E.123) 

In meinen Tests, können Sie aus dem Quellcode versuchen, habe ich zwei Probleme:

  1. Das injizierte Repository innerhalb UserValidator null ist wenn ich die Hibernate-Validierung nicht deaktiviere (spring.jpa.properties.javax.persistence.validation.mode = none)
  2. Auch wenn ich den Hibernate-Validator deaktiviere, schlagen meine Testfälle fehl, weil Spring die Standard-Stringinterpolation für Validierungsnachrichten wie [Constraint] verwendet. [Klassenname Kleinbuchstaben]. [PropertyName]. Ich möchte die Constraint-Annotation nicht mit dem Wertelement wie diesem @NotBlank(message="{mycustom.message}") verwenden, weil ich nicht den Punkt sehe, der seine eigene Konvetion für die Interpolation hat, und ich kann das ausnutzen ... das bedeutet weniger Codierung.

I attach the code; Sie können nur Junit-Tests ausführen und Fehler anzeigen (die Hibernate-Validierung ist aktiviert, überprüfen Sie application.properties).

Was mache ich falsch? Was könnte ich tun, um diese beiden Probleme zu lösen?

====== ====== UPDATE

Nur um zu klären, Frühlings- und Validierungsdokumentation Lesen https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation-beanvalidation-spring-constraints sie sagen:

standardmäßig die LocalValidatorFactoryBean eine SpringConstraintValidatorFactory konfiguriert, dass Spring verwendet ConstraintValidator-Instanzen erstellen. Dadurch können Ihre benutzerdefinierten ConstraintValidators wie alle anderen Spring-Bean von der Abhängigkeitsinjektion profitieren.

Wie Sie sehen können, kann eine ConstraintValidator-Implementierung ihre Abhängigkeiten @Autowired wie jede andere Spring-Bean haben.

In meiner Konfigurationsklasse habe ich meine LocalValidatorFactoryBean erstellt, während sie schreiben. Eine weitere interessante Fragen sind this und this, aber ich hatte kein Glück mit ihnen.

====== ====== UPDATE 2

Nach vielen reseach scheint mit Hibernate Validator der Injektion nicht vorgesehen ist.

fand ich ein paar Art und Weise können Sie das tun:

erste Weg

diese Konfiguration Klasse erstellen:

@Configuration 
public class HibernateValidationConfiguration extends HibernateJpaAutoConfiguration { 

    public HibernateValidationConfiguration(DataSource dataSource, JpaProperties jpaProperties, 
      ObjectProvider<JtaTransactionManager> jtaTransactionManager, 
      ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) { 
     super(dataSource, jpaProperties, jtaTransactionManager, transactionManagerCustomizers); 
    } 

    @Autowired 
    private Validator validator; 

    @Override 
    protected void customizeVendorProperties(Map<String, Object> vendorProperties) { 
     super.customizeVendorProperties(vendorProperties); 
     vendorProperties.put("javax.persistence.validation.factory", validator); 
    } 
} 

2. Art und Weise

ein Dienstprogramm erstellen Bohne

@Service 
public class BeanUtil implements ApplicationContextAware { 

    private static ApplicationContext context; 

    @Override 

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 

     context = applicationContext; 

    } 

    public static <T> T getBean(Class<T> beanClass) { 

     return context.getBean(beanClass); 

    } 

} 

und dann in der Validator Initialisierung:

@Override 
public void initialize(ValidUser constraintAnnotation) { 
userRepository = BeanUtil.getBean(UserRepository.class); 
em = BeanUtil.getBean(EntityManager.class); 
} 

sehr wichtig

In beiden Fällen, um Ihnen die es funktioniert machen auf diese Weise die Einheit-Manager auf "reset" :

Wie auch immer, ich weiß nicht, ob das wirklich ein sicherer Weg ist. Probably it's not a good practice access to the persistence layer at all.

Antwort

0

Wenn Sie wirklich Injektion in Ihrem Validator verwenden müssen, um versuchen, das Hinzufügen @Configurable Anmerkung darauf:

@Configurable(autowire = Autowire.BY_TYPE, dependencyCheck = true) 
public class UserValidator implements ConstraintValidator<ValidUser, User> { 
    private Logger log = LogManager.getLogger(); 

    @Autowired 
    private UserRepository userRepository; 

    // this initialize method wouldn't be needed if you use HV 6.0 as it has a default implementation now 
    @Override 
    public void initialize(ValidUser constraintAnnotation) { 
    } 

    @Override 
    public boolean isValid(User value, ConstraintValidatorContext context) { 
     try { 
      User foundUser = userRepository.findByUsername(value.getUsername()); 

      if (foundUser != null && foundUser.getId() != value.getId()) { 
       context.disableDefaultConstraintViolation(); 
       context.buildConstraintViolationWithTemplate("{ValidUser.unique.username}").addConstraintViolation(); 

       return false; 
      } 
     } catch (Exception e) { 
      log.error("", e); 
      return false; 
     } 
     return true; 
    } 

} 

Aus der Dokumentation zu dieser Anmerkung:

Markiert eine Klasse für Spring- förderfähig zu sein gesteuerte Konfiguration

Das sollte also Ihr Null-Problem lösen. Um es jedoch zu funktionieren, müssten Sie AspectJ konfigurieren ...(Überprüfen Sie, wie @Configurable im Frühjahr dafür zu verwenden ist)

+0

Danke für die Antwort. Ich aktualisierte meine Frage und wies darauf hin, dass die Abhängigkeitsinjektion ohne einen speziellen Trick funktionieren sollte, basierend auf dem, was sie sagen. Ich versuche jedoch, Ihren Weg mit EnableSpringConfigured und EnableLoadTimeWeaving zu verfolgen, aber das injizierte Repository ist immer Null. – drenda

Verwandte Themen