Spring @Autowired into JSF custom validator
Here’s the scenario, create a custom JSF validator, injects a bean via Spring’s @Autowired
.
package com.mkyong.user;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.FacesValidator;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.mkyong.user.bo.UserService;
@Component
@Scope("request")
@FacesValidator("UsernameValidator")
public class UsernameValidator implements Validator {
@Autowired
UserService userService;
@Override
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
String username = value.toString();
if(userService.isUsernameDuplicated(username)){
FacesMessage facesMsg = new FacesMessage("Username is duplicated");
facesMsg.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(facesMsg);
}
}
}
In JSF / XHTML page, link it via validatorId
.
<h:inputText id="username">
<f:validator validatorId="usernameValidator" />
</h:inputText>
Problem
During application startup, log file shows that Spring is created the “UsernameValidator” bean, and UserService
is injected successful.
However, when access from JSF page, the bean userService
from “UsernameValidator” is showing null, caused a NullPointerException.
Solution
This is because @FacesValidator
isn’t managed by Spring’s container. To fix it, reference the custom validator via binding
, instead of validatorId
.
P.S Spring, CDI and JSF, did a really bad job here (integrate each others), hope they can fix it in future release.
<h:inputText id="username">
<f:validator binding="#{usernameValidator}" />
</h:inputText>
In custom validator class, uses Spring or CDI only, @FacesValidator
is not necessary.
@Component
@Scope("request")
public class UsernameValidator implements Validator {
@Autowired
UserService userService;
//...
@Named
@RequestScoped
public class UsernameValidator implements Validator {
@Autowired
UserService userService;
//...
Of course, make sure Spring component auto scan is enabled.
<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.mkyong" />
<context:annotation-config />
</beans>
Thank you very much you really save my soul
binding=”dcValidator”: Cannot convert dcValidator of type class java.lang.String to interface javax.faces.validator.Validator
please help.
Sorry little-bit rush, forget EL Expression in binding. But still i am getting NullPointerException.
Just one memo to add. Do not throw ValidatorException(FacesMessage) in your validate(…) method. These exceptions will propagete to your code.
Try using FacesContext instead:
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
…
context.addMessage(component.getClientId(context), duplicateMsg);
…
}
Thanks very much. Just what I was looking for. Keep bloging 🙂
Hello,
First, i would like to thank you for your big work, your tutorial is very helpful.
I tried to follow your tutorial for spring inject with validator using binding instead of validatorId. I noticed, if you have more than one validation you have to use @Componenet(“uniqueName”), and you call . Otherwise, the spring framework will not do the dependecy injection correctly since all validator classes will Heritage from javax.faces.validator.Validator.
It’s like the dependency inject is done by type, if you don’t use @Componenet(“uniqueName”).
Thanks