我正在使用 Quarkus 3.6.3 和 Vaadin 24.3.0。我遇到的问题是,由于任何类文件的更改,当 quarkus 应用程序在开发模式下实时重新加载时,必填字段的必填指示符不再显示。
我有一个显示一些文本字段的视图,我使用
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
实例中。PredefinedScopeBeanMetaDataManager
的新实例,该实例再次保存带有 bean 验证注释的类映射。但是这个新实例不用于进一步查找,而是使用具有空映射的old实例,因为它保存在上面描述的常量中。感谢您的侦探工作。这肯定是一个错误。
据我所知,我们在 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);
}
}