5

カスタムにBeanを注入しようとしていますConstraintValidator。私はいくつかのことに出くわしました:

  • CDIはvalidation-api-1.1.0でサポートされています(ベータ版が利用可能)
  • Hibernate Validator 5はvalidation-api-1.1.0を実装しているようです(アルファ版が利用可能)
  • Seam検証モジュールを使用する
  • SpringのLocalValidatorFactoryBeanを使用する

すでにSpring(3.1.3.Release)を使用しているので、最後のものが私の状況に最も適しているようです。

XMLアプリケーションコンテキストにバリデーターファクトリを追加し、アノテーションを有効にしました。

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.1.xsd">

    <context:component-scan base-package="com.example" />
    <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
</beans>

バリデーター:

public class UsernameUniqueValidator implements
    ConstraintValidator<Username, String>
{
    @Autowired
    private PersonManager personManager;

    @Override
    public void initialize(Username constraintAnnotation)
    {
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context)
    {
        if (value == null) return true;
        return personManager.findByUsername(value.trim()) != null;
    }
}

検証は:に適用されPersonます

public class Person
{
    @Username
    private String username;
}

そしてバッキングビーン:

@Named
@Scope("request")
public class PersonBean
{
    private Person person = new Person();
    @Inject
    private PersonManager personManager;

    public create()
    {
        personManager.create(person);
    }
}

そして、JSFページには次のようなものがあります。

<p:inputText value="#{personBean.person.username}" />

バリデーターが呼び出されますが、フィールドは自動配線/挿入されず、nullのままです。もちろん、これはNullPointerExceptionをスローします。

私はこれをHibernateバリデーター4.2でテストしてLocalValidatorFactoryBeanいます(これができるはずだからです)。

4

3 に答える 3

2

私も同じ問題に直面しました。私の場合、Spring+MyFaces+RichFaces が使用されます。アプリケーションの起動時に、Spring は LocalValidatorFactoryBean を作成しますが、MyFaces はその Bean を検証ファクトリーとして使用しません。代わりに、MyFaces と RichFaces は両方とも、spring-faces モジュールでも独自のバリデーターを使用していました。

Face で LocalValidatorFactoryBean を使用する方法を理解するために、javax.faces.validator.BeanValidator createValidatorFactory メソッドの中を調べました。このメソッドは、検証が必要になるたびに ValidatorFactory を作成するために MyFaces によって使用されます。そのメソッドの内部では、次のことがわかります。

Map<String, Object> applicationMap = context.getExternalContext().getApplicationMap();
Object attr = applicationMap.get(VALIDATOR_FACTORY_KEY);
if (attr instanceof ValidatorFactory)
{
    return (ValidatorFactory) attr;
}
else
{
    synchronized (this)
    {
        if (_ExternalSpecifications.isBeanValidationAvailable())
        {
            ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
            applicationMap.put(VALIDATOR_FACTORY_KEY, factory);
            return factory;
        }
        else
        {
            throw new FacesException("Bean Validation is not present");
        }
    }
}

ご覧のとおり、新しいインスタンスを作成する前に、まずコンテキストから ValidatorFactory をロードしようとします。そこで、顔に Spring LocalValidatorFactoryBean を使用させるために、次のソリューションを実装しました。PostConstructApplicationEvent で実行される SystemEventListener を作成しました。このリスナーは、サーブレット コンテキストから Spring WebApplicationContext を取得し、そこから LocalValidatorFactoryBean のインスタンスを取得し、ExternalContext ApplicationMap に格納します。

public class SpringBeanValidatorListener implements javax.faces.event.SystemEventListener {
    private static final long serialVersionUID = -1L;

    private final Logger logger = LoggerFactory.getLogger(SpringBeanValidatorListener.class);

    @Override
    public boolean isListenerForSource(Object source) {
        return true;
    }

    @Override
    public void processEvent(SystemEvent event) {
        if (event instanceof PostConstructApplicationEvent) {
            FacesContext facesContext = FacesContext.getCurrentInstance();
            onStart(facesContext);
        }
    }

    private void onStart(FacesContext facesContext) {
        logger.info("--- onStart ---");

        if (facesContext == null) {
            logger.warn("FacesContext is null. Skip further steps.");
            return;
        }

        ServletContext context = getServletContext(facesContext);

        if (context == null) {
            logger.warn("ServletContext is not available. Skip further steps.");
            return;
        }

        WebApplicationContext webApplicationContext = (WebApplicationContext) context.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);

        if (webApplicationContext == null) {
            logger.warn("Spring WebApplicationContext was not set in ServletContext. Skip further steps.");
            return;
        }

        LocalValidatorFactoryBean validatorFactory = null;

        try {
            validatorFactory = webApplicationContext.getBean(LocalValidatorFactoryBean.class);
        } catch (BeansException ex){
            logger.warn("Cannot get LocalValidatorFactoryBean from spring context.", ex);
        }

        logger.info("LocalValidatorFactoryBean loaded from Spring context.");

        Map<String, Object> applicationMap = facesContext.getExternalContext().getApplicationMap();
        applicationMap.put(BeanValidator.VALIDATOR_FACTORY_KEY, validatorFactory);

        logger.info("LocalValidatorFactoryBean set to faces context.");
    }

    private ServletContext getServletContext(FacesContext facesContext) {
        return (ServletContext) facesContext.getExternalContext().getContext();
    }
}

そのため、MyFaces が初めて ValidatorFactory を取得しようとすると、LocalValidatorFactoryBean が既に存在し、MyFaces は新しいインスタンスを作成しません。

于 2013-07-05T15:17:00.527 に答える
0

私は春の専門家ではありませんが、beans.xml でPersonManagerを定義するか、@Component で注釈を付けることを期待します。@Component でアノテーションが付けられたアンマネージド Bean の自動配線も参照してください。

于 2013-01-17T09:23:30.210 に答える