【问题标题】:Vaadin, how to autowire spring component in custom constraint validatorVaadin,如何在自定义约束验证器中自动装配弹簧组件
【发布时间】:2022-09-30 00:14:31
【问题描述】:

我正在开发一个使用spring boot作为后端和vaadin作为前端的应用程序。我需要添加验证,这需要进行数据库检查 - 是在这个特定示例中注册的电子邮件。

我想要实现的示例:

@Component
public class EmailExistsValidator implements ConstraintValidator<EmailExists, CharSequence> {

    private final UserRepo userRepo;

    @Autowired
    public EmailExistsValidator(UserRepo userRepo) {
        this.userRepo = userRepo;
    }

    @Override
    public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
        //check email does not exist logic here
    }
}

我已经在 spring mvc 和 spring rest 应用程序中成功使用了这个设置,没有额外的配置。不幸的是,上述内容不适用于 vaadin。经过一些调试后,我发现 spring 确实创建并管理了这些组件,但它们不是正在使用的组件。相反,vaadin 在实际验证发生时创建和管理ConstraintValidator 的其他实例。验证通过Binder.writeBeanIfValid() 完成,如果这很重要的话。

我经历了:

  1. Autowired Repository is Null in Custom Constraint Validator
  2. Spring Boot: repository does not autowire in the custom validator
  3. 上面链接的所有问题作为可能的解决方案
  4. 还有几个问题,很遗憾我找不到了
  5. 我尝试获取WebApplicationContext,以便使用AutowireCapableBeanFactory.autowireBean() 自动连接带注释的字段。不出所料,当 vaadin 创建/管理实例时,上下文是 null,因此它不起作用。

    我目前正在使用什么。

    @Component
    public class EmailExistsValidator implements ConstraintValidator<EmailExists, CharSequence> {
    
        private static UserRepo repo;
    
        private final UserRepo userRepo;
    
        public EmailExistsValidator() {
            this.userRepo = repo;
        }
    
        @Bean
        public static UserRepo setRepo(UserRepo userRepo) {
            repo = userRepo;
            return repo;
        }
    
        @Override
        public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
            //validation logic
        }
    }
    

    这种方法基于this answer(来自我链接的第二个问题)。它完成了这项工作(只有这对我有用),但它对我的口味来说太老套了。

    如何将 vaadin 配置为使用 spring 管理的ConstraintValidators,而不是 vaadin 管理的?或者如何在 vaadin 创建和管理的ConstraintValidators 中自动装配弹簧组件?

    标签: java spring-boot vaadin vaadin23


    【解决方案1】:

    由于 Spring 不管理组件,经常会出现自动布线不起作用的情况。

    为此,您可以创建一个包含 ApplicationContext 的类,如下所示:

    @Component
    public class ApplicationContextHolder implements ApplicationContextAware {
    
        private static ApplicationContext applicationContext;
    
        public static <T> T getBean(Class<T> type) {
            return applicationContext.getBean(type);
        }
    
        @Override
        public void setApplicationContext(@SuppressWarnings("NullableProblems") ApplicationContext applicationContext) throws BeansException {
            ApplicationContextHolder.applicationContext = applicationContext;
        }
    }
    

    现在您可以使用此类通过静态方法获取对 bean 的引用:

    UserRepo userRepo = ApplicationContextHolder.getBean(UserRepo.class);
    

    【讨论】:

    • 谢谢,这比我目前使用的要好得多,我将切换到这种方法。不过,这并不是我所希望的。是否可以将 vaadin 配置为在 spring 托管组件中优先搜索验证器,并且只有在找不到创建实例的情况下才可以?还是将其配置为自行进行自动装配?基本上是您的方法,但不是手动执行,而是让 vaadin 为您执行。
    • 没有 Vaadin 使用没有 Spring 的 Bean Validation
    • 如果你需要做 I18N,请阅读我的博文martinelli.ch/…
    【解决方案2】:

    使用Simon's answer 作为灵感,我最终扩展了ApplicationContextHolder 以自动装配手动创建的bean。

    @Component
    public class ApplicationContextHolder implements ApplicationContextAware {
    
        private static ApplicationContext applicationContext;
        private static AutowireCapableBeanFactory factory;
    
        public static <T> T getBean(Class<T> beanClass) {
            return applicationContext.getBean(beanClass);
        }
    
        public static <T> void autowireBean(T bean) {
            factory.autowireBean(bean);
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            ApplicationContextHolder.applicationContext = applicationContext;
            factory = applicationContext.getAutowireCapableBeanFactory();
        }
    }
    

    当验证器依赖多个 bean 时,这会很方便。

    public class EmailExistsValidator implements ConstraintValidator<EmailExists, CharSequence> {
    
        @Autowired
        private FirstBean firstBean;
        @Autowired
        private SecondBean secondBean;
        @Autowired
        private ThirdBean thirdBean;
    
        public EmailExistsValidator() {
            ApplicationContextHolder.autowireBean(this);
        }
    
        @Override
        public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
            //validation logic
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2012-09-22
      • 2014-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-18
      • 1970-01-01
      相关资源
      最近更新 更多