如何在反序列化时重新注入瞬态@ManagedProperty?

时间:2012-05-16 12:05:29

标签: spring jsf-2 dependency-injection

我正在使用Spring和JSF 2来创建Web应用程序。 业务对象保存在Spring容器中,我使用@ManagedProperty将它们注入Managed Beans,如下所示:

@ManagedBean
@ViewScoped
public class SomeMB implements Serializable {
    private static final long serialVersionUID = 1L;

    @Getter @Setter
    @ManagedProperty("#{someService}")
    private SomeService someService;
    // ...

问题是,我一直从Spring( ServiceLocatorFactoryBean )获得一个NotSerializableException类,它正被 SomeService bean使用。

如果我成功transient,我怎样才能在反序列化后重新注入它?

或者,解决这个问题的其他方法是什么?

我一直在阅读其他几个类似的问题,但找不到任何完全解决这个问题的问题。

4 个答案:

答案 0 :(得分:3)

不是通过EL在@ManagedProperty注释中注入Spring bean(在ManagedBean初始化上执行),而是获取在运行时评估EL的bean。

使用这种方法,这就是JSF bean应该是这样的:

@ManagedBean
@ViewScoped
public class SomeMB implements Serializable {
    private static final long serialVersionUID = 1L;

    private static SomeService someService() {
        return SpringJSFUtil.getBean("someService");
    }
    // ...

通过EL获取bean的实用程序类 SpringJSFUtil.java

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);
    }
}

这消除了Spring bean属性(以进行更多EL评估为代价),从而避免了将属性放在首位的所有序列化问题。

使用OmniFaces

的方法相同

在我的实际代码中,我使用了来自utility classOmniFaces evaluateExpressionGet(String expression)方法。所以,对于那些使用它的人来说,这就是我的代码真实的样子:

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}");
    }
    // ...

请注意,此方法获取完整的EL(“#{expression}”),而不仅仅是Spring bean名称(或者您将获得ClassCastException)。

答案 1 :(得分:2)

在Spring @Service上尝试@Scope(value = BeanDefinition.SCOPE_SINGLETON,proxyMode = ScopedProxyMode.INTERFACES)。这应该将一个可序列化的代理对象注入到托管bean中,这将在反序列化后重新定位服务。

答案 2 :(得分:0)

对于那些要遵循的人 - 我在注入的ResourceBundle时遇到了类似的问题。使用BalusC的部分回答,我做了以下几点:

@ManagedProperty(value="#{myBundle}")
private transient ResourceBundle myBundle;

private Object readResolve() {
    myBundle = FacesContext.getCurrentInstance().getApplication()
        .evaluateExpressionGet(FacesContext.getCurrentInstance(), "#{myBundle}",
        ResourceBundle.class);
    return this;
}

这样,只有在反序列化托管bean时才会评估EL。

答案 3 :(得分:-1)

请记住Spring手册( link to spring):

  

基于构造函数或基于setter的DI?

     

由于您可以混合使用基于构造函数和基于Setter的DI,因此对于可选依赖项使用构造函数参数和使用可选依赖项的setter是一个很好的经验法则。请注意,在setter上使用@Required注释可用于使setter成为必需的依赖项。

     

Spring团队通常提倡setter注入,因为大量的构造函数参数可能变得难以处理,尤其是当属性是可选的时。 Setter方法还使该类的对象可以在以后重新配置或重新注入。通过JMX MBean进行管理是一个引人注目的用例。

     

一些纯粹主义者喜欢基于构造函数的注射。提供所有对象依赖性意味着对象始终以完全初始化的状态返回到客户端(调用)代码。缺点是对象变得不太适合重新配置和重新注入。

     

使用对特定班级最有意义的DI。有时,在处理您没有源的第三方类时,会选择您。遗留类可能不会暴露任何setter方法,因此构造函数注入是唯一可用的DI。