Quarkus Live Reload 后 Vaadin Bean 验证不起作用

问题描述 投票:0回答:1

我正在使用 Quarkus 3.6.3 和 Vaadin 24.3.0。我遇到的问题是,由于任何类文件的更改,当 quarkus 应用程序在开发模式下实时重新加载时,必填字段的必填指示符不再显示。

Java 类

我有一个显示一些文本字段的视图,我使用

BeanValidationBinder
将字段绑定到 JPA 实体类
User
的实例。这个类很简单,看起来像这样:

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class User {

    @NotNull
    @Column
    private String firstName;

    @NotNull
    @Column
    private String lastName;

    // Getters and Setters
}

为了简单起见,视图类如下所示:

@Route(value = "user", layout = MainLayout.class)
public class UserFormView extends VerticalLayout {

    private final TextField firstName = new TextField("First name");

    private final TextField lastName = new TextField("Last name");

    private final Binder<User> binder = new BeanValidationBinder<>(User.class);

    public UserFormView() {

        binder.forField(firstName).withNullRepresentation("").bind("firstName");
        binder.forField(lastName).withNullRepresentation("").bind("lastName");
        binder.readBean(new User());

        add(firstName, lastName);
    }
}

问题

实时重新加载后,方法

propertyDescriptor
中的变量
BeanValidationBinder#configureRequired
null
,因此不会处理域类的bean验证注释来设置所需的指示符和绑定器的验证。

现在我想知道实时重新加载后是否也应该正确显示所需的指标。

调试

通过调试我发现:

  • 最初启动应用程序时,会创建一个
    PredefinedScopeBeanMetaDataManager
    的实例,并保存类的 beanMetaDataMap,其中带有 Bean 验证注释作为
    BeanMetaDataImpl
    的实例。该实例保存在常量
    CloseAsNoopValidatorFactoryWrapper
    内的
    LazyFactoryInitializer#FACTORY
    实例中。
  • 实时重新加载后,该实例的 beanMetaDataMap 被清除,并创建
    PredefinedScopeBeanMetaDataManager
    的新实例,该实例再次保存带有 bean 验证注释的类映射。但是这个新实例不用于进一步查找,而是使用具有空映射的old实例,因为它保存在上面描述的常量中。
java quarkus bean-validation hibernate-validator vaadin-flow
1个回答
1
投票

感谢您的侦探工作。这肯定是一个错误。

据我所知,我们在 Quarkus 能做的不多。 Vaadin Flow 应避免将

ValidatorFactory
存储在
LazyFactoryInitializer#FACTORY
的静态字段中。或者至少在应用程序停止时清理所有内容,然后创建一个新应用程序。当前的代码无论如何都会导致类加载器泄漏,任何服务器都会重新加载应用程序。

按照 Ivan Kaliuzhnyi 在 Flow 跟踪器中的评论 https://github.com/vaadin/flow/issues/4481#issuecomment-1712720926,即使它一点也不漂亮,你也应该能够解决它。

从评论中粘贴以保留历史记录:

@Configuration
public class ValidatorConfig implements InitializingBean {

    @Autowired
    private ValidatorFactory validatorFactory;

    @Bean
    public ValidatorFactory validatorFactory(MessageSource messageSource) {
        var bean = new LocalValidatorFactoryBean();
        bean.setValidationMessageSource(messageSource);
        return bean;
    }

    @Bean
    public HibernatePropertiesCustomizer hibernatePropertiesCustomizer(ValidatorFactory validatorFactory) {
        // AvailableSettings.JPA_VALIDATION_FACTORY -> javax.persistence.validation.factory
        // AvailableSettings.JAKARTA_VALIDATION_FACTORY -> jakarta.persistence.validation.factory
        return hibernateProperties ->
            hibernateProperties.put(AvailableSettings.JAKARTA_VALIDATION_FACTORY, validatorFactory);
    }

    @Override
    public void afterPropertiesSet() throws Exception {

        var className = "com.vaadin.flow.data.validator.BeanValidator.LazyFactoryInitializer";
        var fieldName = "FACTORY";
        var field = FieldUtils.getDeclaredField(ClassUtils.getClass(className), fieldName, true);

        var unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
        unsafeField.setAccessible(true);
        var unsafe = (Unsafe) unsafeField.get(null);

        unsafe.putObject(unsafe.staticFieldBase(field),
                unsafe.staticFieldOffset(field),
                validatorFactory);

    }
}
© www.soinside.com 2019 - 2024. All rights reserved.