当前位置: 首页 > 知识库问答 >
问题:

使用Spring4和消息插值配置在ConstraintValidator中注入存储库

东郭元魁
2023-03-14

我创建了一个小的示例项目来展示我在Spring Boot验证的配置及其与Hibernate的集成中遇到的两个问题。我已经尝试了我找到的关于这个主题的其他回复,但不幸的是,它们对我不起作用,或者要求禁用Hibernate验证。

我想使用一个实现constraintvalidator 的自定义验证器,并在其中注入我的userrepository。同时,我希望保持Hibernate的默认行为,即在更新/持久期间检查验证错误。

我在这里写完整的主要部分的应用程序

自定义配置在该类中,我使用自定义messageSource设置了一个自定义验证器,因此Spring将从文件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;
    }

}

如果不是自定义验证器@validuser,bean在这里没有什么特别之处

@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;
    }

}

这里的自定义验证器是我尝试注入存储库的地方。如果不是在我禁用Hibernate验证时,存储库总是null。

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

}

消息.属性

#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)

在我的测试中,你可以从源代码中尝试,我有两个问题:

  1. 如果不禁用Hibernate验证(spring.jpa.properties.javax.persistence.validation.mode=none)
  2. UserValidator中注入的存储库为空
  3. 即使我禁用了Hibernate验证器,我的测试用例也会失败,因为有些东西阻止Spring对验证消息使用默认字符串插值,该消息应该类似于[Constraint].[class name lowercase].[propertyName]。我不想将约束注释与value元素一起使用,比如@NotBlank(Message=“{MyCustom.Message}”),因为考虑到它有自己的插值传递,而且我可以利用它,所以我看不出有什么意义...这意味着更少的编码。

我附上密码;您可以只运行Junit测试并看到错误(启用Hibernate验证,检查Application.Properties)。

我做错了什么?我能做些什么来解决这两个问题呢?

=======更新=======

默认情况下,LocalValidatorFactoryBean配置一个使用Spring创建ConstraintValidatorFactory实例的SpringConstraintValidatorFactory。这允许您的自定义ConstraintValidators像任何其他Spring bean一样受益于依赖注入。

正如您所看到的,ConstraintValidator实现可能像任何其他Spring bean一样具有其依赖关系@Autowired。

在我的配置类中,我创建了LocalValidatorFactoryBean

另一个有趣的问题是这个和这个,但我没有运气。

=======更新2=======

经过大量研究,似乎与Hibernate验证器没有提供注入。

我找到了几种方法:

第1路

创建此配置类:

 @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);
    }
}
    @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);

    }

}

然后在验证程序初始化中:

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

非常重要

在这两种情况下,为了使it工作,您必须以以下方式“重置”实体管理器:

@Override
public boolean isValid(User value, ConstraintValidatorContext context) {
    try {
        em.setFlushMode(FlushModeType.COMMIT);
        //your code
    } finally {
        em.setFlushMode(FlushModeType.AUTO);
    }
}

不管怎样,我不知道这是否真的是一个安全的方法。很可能这根本不是一个访问持久层的好做法。

共有1个答案

金宣
2023-03-14

如果您确实需要在验证器中使用注入,请尝试在其上添加@configurable注释:

@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;
    }

}

从文档到注释:

将类标记为符合Spring驱动配置的条件

 类似资料:
  • 我用玩Web应用程序(没什么不寻常的)。Guice用于依赖注入。如何在类构造函数中注入属性值?代码如下。 在当前配置中,如果不运行应用程序,就无法测试。我希望能够在单元测试中实例化。完美的解决方案[从我的角度来看]应该是这样的: 不幸的是,这段代码不起作用,因为Guice没有“my”。bar绑定: 没有绑定使用 @com.google.inject.name.Named(value=my.bar)

  • 我试图从WSO2 ESB填充一个JMS消息存储。 [2014-07-29 16:43:45,633]错误-JmsStore Store[JMSMS]。无法创建到代理的新连接。初始上下文工厂:[org.apache.qpid.jn di.PropertiesFileInitialContextFactory];提供程序URL:[repository/conf/jndi.properties];连接工

  • 我正在使用WSO2ESB4.7.0和ActiveMQ5.8.0。我希望将消息存储在队列中,为此我尝试使用这段代码,但它不工作。我创建了存储库,这将有助于存储消息,我的conumeing过程已经在JAVA代码中完成,所以我不必担心消费,如果我这样做了,我的消息将出现在acivemq UI中。我的配置如下所示 我试图通过代理传递消息,为此我编写了简单的代理服务 在这种情况下,我的队列没有在Active

  • 我试图使用-@runwith(SpringJUnit4ClassRunner.class)@ContextConfiguration({“classpath:some.xml”})运行spring JUnit测试用例 xml在内存db详细信息中定义了bean

  • 尝试使用配置单元HPL/SQL存储过程将数据插入配置单元表。但无法插入时间戳值。我的表定义如下: 我使用配置单元cli插入值没有任何问题。 知道为什么吗? 谢谢,

  • 问题内容: 我正在尝试配置Jenkins(在Windows Server2008上运行)以连接到GitLab6.0存储库(在Linux机器上)。 在本地窗口框中,我可以使用SSH URL在GitLab服务器上克隆项目。我在使用HTTP URL时遇到了一些问题,但是我确实设法使用Eclipse插件通过HTTP与身份验证进行连接。 在Jenkins项目配置屏幕上,我尝试过: http://myserv