9

根据 Glassfish 4.0 wiki,Glassfish 4.0 应该包含 JSR349 Bean Validation 1.1。:GF4 wiki 链接

根据 JSR349 规范,CDI 注入应该开箱即用:Bean Validation 1.1。CDI 集成

所以我相应地改变了我的 pom.xml:

<dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>1.1.0.Final</version>
        <scope>provided</scope>
 </dependency>

并尝试将 CDI Bean 注入 ConstraintValidator:

public class UniqueEmaiValidator implements ConstraintValidator<UniqueEmail, String> {

    @Inject
    private UserAccountService accountService;

    @Override
    public void initialize(UniqueEmail constraintAnnotation) {
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
         return !accountService.userExistsByEmail(value);
    }
}

但是,在测试应用程序时(使用 arquillian-glassfish-remote-3.1 1.0.0.CR4 运行 Arquillian 1.1.1),验证总是会失败,因为userAccountService它为 null,因此最终会抛出一个 错误NullPointerException

为了使 Bean Validation 1.1 正常工作,我缺少什么?

编辑:

A) 可以确认它不是由 Arquillian 远程测试引起的 - 也会引发 NPEx。在服务器上运行时

B) 在 GlassFish Server Open Source Edition 4.0 (build 89) 上运行

C) 我使用 Hibernate Validator 的 5.0.1.FINAL 明确地重新构建了 bean-validation.jar。mvn package输出:

[INFO] Building Validation API (JSR 349) version 1.1.0.Final, Hibernate Validator version 5.0.1.Final and its dependencies repackaged as OSGi bundle 2.1.92

GlassFish 服务器启动后,我收到以下信息:

INFO:   GlassFish Server Open Source Edition  4.0  (89) startup time : Felix (5,736ms), startup services(2,078ms), total(7,814ms)
INFO:   HV000001: Hibernate Validator 5.0.1.Final

所以我想重建确实奏效了。但是,它并没有解决我的 NullPointerException 问题:/

D)@Gunnar

这是实体类,使用@Constraint注释:

@Entity
public class UserAccount extends AbstractEntity implements VisibilitySettings {

  @UniqueEmail
  private String email;
  [...] 
}

注释本身:

@Constraint(validatedBy = {UniqueEmailValidator.class})
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UniqueEmail {

    String message() default "{validator.security.useraccount.emailexists}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

以及相应的ConstraintValidator

public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {

    @Inject
    private UserAccountService accountService;

    @Override
    public void initialize(UniqueEmail constraintAnnotation) {
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return !accountService.userExistsByEmail(value);
    }
}

UserAccountService注释为@ApplicationScoped @Transactional

4

2 回答 2

3

我解决了。我创建了一个限定符注释:

@Qualifier   
@Retention(RUNTIME)   
@Target({TYPE,FIELD})   
public @interface Validation {

}

我注释了我的 ConstraintValidator:

@Validation 
public class MyValidatorImpl implements ConstraintValidator<MyValidator, MyBean> {

}

我创建了我的 ConstraintValidatorFactory:

public class CDIConstraintValidatorFactory implements ConstraintValidatorFactory {

    @Override
    public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> type) {
        try{

            T t = getBeanInstance(type);
            if(t==null){
                t = type.newInstance();
            }
            return t;
        }catch(Exception e){
            throw new RuntimeException(e);
        }
    }

    private BeanManager getBeanManager() throws NamingException {
            String name = "java:comp/" + BeanManager.class.getSimpleName();
            InitialContext ic = new InitialContext();
            BeanManager beanManager = (BeanManager) ic.lookup(name);
            return beanManager;
    }

    public <T> T getBeanInstance(final Class<T> type) throws Exception{
        BeanManager beanManager =  getBeanManager();
        Validation v = type.getAnnotation(Validation.class);
        if(v!=null){
            final Set<Bean<?>> beans = beanManager.getBeans(type,v);
            beanManager.resolve(beans);
            if(!beans.isEmpty()){
                final Bean<T> bean = (Bean<T>) beanManager.resolve(beans);
                final CreationalContext<T> creationalContext = beanManager.createCreationalContext(bean);
                return (T) beanManager.getReference(bean, type,creationalContext);
            }else{
                return null;
            }
        }else{
            return null;
        }
    }



}

然后我将它注册为我的validation.xml:

<?xml version="1.0" encoding="UTF-8"?>
<validation-config    xmlns="http://jboss.org/xml/ns/javax/validation/configuration"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/configuration validation-configuration-1.1.xsd">
        <constraint-validator-factory>xxx.be.framework.validator.CDIConstraintValidatorFactory</constraint-validator-factory>
</validation-config> 

问候, 吉安卡罗

于 2014-01-27T14:08:45.947 回答
3

当通过Validation.buildDefaultValidatorFactory().

当通过 JPA 作为实体生命周期的一部分执行验证时,CDI 集成应该可以工作;如果没有,那么如何将 Bean Validation 集成到 GlassFish 中可能仍然存在问题。如果确实如此,您可以通过配置自定义约束验证器工厂来解决此问题,在该工厂中META-INF/validation.xml创建 CDI 托管验证器实例。

为此,您可以使用Hibernate Validator 的 CDI 集成提供的工厂作为起点。请注意,尽管通过 XML 配置时,工厂需要一个默认构造函数;为了满足该要求,您可以获得对BeanManagervia JNDI 的引用。

于 2013-11-12T14:36:36.327 回答