7

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

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

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

问题是,我不断从 Spring ( ServiceLocatorFactoryBeanNotSerializableException )中获取一个类,它正被SomeService bean 使用。

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

或者,还有什么其他方法可以解决这个问题?

我一直在阅读其他几个类似的问题,但找不到任何与此问题完全相关的问题。

4

4 回答 4

4

不是通过@ManagedProperty注释中的 EL 注入 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

在我的实际代码中,我使用OmniFaces提供的实用程序类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)。

于 2012-07-16T20:23:22.667 回答
2

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

于 2013-02-08T13:02:47.470 回答
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。

于 2014-01-10T19:03:33.977 回答
-1

请记住 Spring 手册中的这一点(链接到 spring):

基于构造函数还是基于 setter 的 DI?

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

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

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

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

于 2012-06-06T15:13:08.323 回答