我正在尝试将bean注入自定义ConstraintValidator
。我遇到过一些事情:
最后一个似乎最适合我的情况,因为我们已经使用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;
}
支持bean:
@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}" />
调用验证器但该字段未自动装配/注入并保持为空。这当然会产生NullPointerException。
我正在使用Hibernate验证器4.2进行测试(因为LocalValidatorFactoryBean
我应该能够这样做)。
答案 0 :(得分:0)
我不是春天专家,但是我希望您在beans.xml中定义 PersonManager 或者使用@Component注释它。另请参阅Autowiring Unmanaged Beans Annotated With @Component
答案 1 :(得分:0)
我也面临同样的问题。在我的例子中使用Spring + MyFaces + RichFaces。在应用程序启动期间,Spring会创建它的LocalValidatorFactoryBean,但MyFaces不会将该bean用作验证工厂。相反,MyFaces和RichFaces都使用自己的验证器,即使是spring-faces模块。
要想弄清楚如何使用LocalValidatorFactoryBean,我在javax.faces.validator.BeanValidator中查看了createValidatorFactory方法。每次需要验证时,MyFaces都会使用此方法创建ValidatorFactory。在该方法内部,您可以看到以下内容:
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。所以我实现了以下解决方案,使face使用Spring LocalValidatorFactoryBean:我创建了一个在PostConstructApplicationEvent上运行的SystemEventListener。该侦听器从servlet上下文获取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不会创建新实例。
答案 2 :(得分:0)
绝对是使用密钥BeanValidator.VALIDATOR_FACTORY_KEY将自己的自定义ValidatorFactory添加到应用程序映射的方法。 但是,除了使用javax.faces.event.SystemEventListener之外,您还可以从spring方面接近它。将ValidatorFactory注册为ServletContext中的属性就足以将其拾取并添加到应用程序映射中(无论您使用什么,它都是ServletContext或PortletContext的抽象)。
所以问题是:如何将spring bean作为属性添加到ServletContext中。我的解决方案是使用一个实现ServletContextAware的辅助bean:
public class ServletContextAttributePopulator implements ServletContextAware {
Map<String,Object> attributes;
public Map<String, Object> getAttributes() {
return attributes;
}
public void setAttributes(Map<String, Object> attributes) {
this.attributes = attributes;
}
@Override
public void setServletContext(ServletContext servletContext) {
for (Map.Entry<String,Object> entry : attributes.entrySet()) {
servletContext.setAttribute(entry.getKey(), entry.getValue());
}
}
}
请注意,您可以将此类用于要添加到ServletContext的任何类型的bean。
在你的春天环境中,你会添加:
<bean id="servletContextPopulator" class="my.package.ServletContextAttributePopulator">
<property name="attributes">
<map>
<entry key="javax.faces.validator.beanValidator.ValidatorFactory" value-ref="validator"/>
</map>
</property>
</bean>
其中&#34;验证者&#34;是LocalValidatorFactoryBean的id。