Instead of injecting the Spring beans via EL in a @ManagedProperty
annotation (executed on the ManagedBean initialization), obtain the beans evaluating the EL at runtime.
With this approach, this is what the JSF beans should look like:
@ManagedBean
@ViewScoped
public class SomeMB implements Serializable {
private static final long serialVersionUID = 1L;
private static SomeService someService() {
return SpringJSFUtil.getBean("someService");
}
// ...
And the utility class SpringJSFUtil.java that gets the bean via EL:
import javax.faces.context.FacesContext;
public class SpringJSFUtil {
public static <T> T getBean(String beanName) {
if (beanName == null) {
return null;
}
return getValue("#{" + beanName + "}");
}
@SuppressWarnings("unchecked")
private static <T> T getValue(String expression) {
FacesContext context = FacesContext.getCurrentInstance();
return (T) context.getApplication().evaluateExpressionGet(context,
expression, Object.class);
}
}
This eliminates the Spring bean property (at the cost of doing a few more EL evaluations), thus avoiding all the serialization issues of having the property in first place.
The same approach, using OmniFaces:
In my actual code, I use the evaluateExpressionGet(String expression)
method of an utility class available from OmniFaces. So, for those of you who use it too, this is what my code really look like:
import static org.omnifaces.util.Faces.evaluateExpressionGet;
@ManagedBean
@ViewScoped
public class SomeMB implements Serializable {
private static final long serialVersionUID = 1L;
private static SomeService someService() {
return evaluateExpressionGet("#{someService}");
}
// ...
Notice that here the method gets the full EL ("#{expression}"), not just the Spring bean name (or you would get a ClassCastException).