0

我正在开发一个使用 JSF 1.2 的 JSR-286 portlet 应用程序。我正在努力将我的 JSF 托管 bean 移动到 Spring bean,我注意到 Spring 如何处理请求范围与 JSF 如何处理请求范围之间似乎存在差异。

在我的 portlet 应用程序中,我有两个 portlet 位于同一页面上,并且都使用相同的起始 JSF portlet 页面视图。当我使用 JSF 托管请求 bean 时,会为每个 portlet 创建一个单独的请求 bean,这就是我要寻找的行为。当我使用 Spring bean 时,只会创建一个请求 bean,并在两个 portlet 之间共享。这是正常行为吗?有什么办法可以阻止它这样做吗?

在将我的 bean 移动到 Spring 之前,我原来的 faces-config.xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<faces-config
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
version="1.2">
<application>
    <state-manager>com.ibm.faces.application.DevelopmentStateManager</state-manager>
    <variable-resolver>com.ibm.faces.portlet.PortletVariableResolver</variable-resolver>
</application>
<factory>
    <faces-context-factory>com.ibm.faces.context.AjaxFacesContextFactory</faces-context-factory>
    <render-kit-factory>com.ibm.faces.renderkit.AjaxRenderKitFactory</render-kit-factory>
</factory>

<managed-bean>
    <managed-bean-name>sessionBean</managed-bean-name>
    <managed-bean-class>sanitycheck.SessionBean</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

<managed-bean>
    <managed-bean-name>pc_SanityCheckProjectView</managed-bean-name>
    <managed-bean-class>sanitycheck.SanityCheckProjectView</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
    <managed-property>
        <property-name>sessionBean</property-name>
        <value>#{sessionBean}</value>
    </managed-property>
</managed-bean>

<lifecycle>
    <phase-listener>com.ibm.faces.webapp.ValueResourcePhaseListener</phase-listener>
</lifecycle>

</faces-config>

将 bean 移动到 Spring 后我的 faces-config.xml 文件:

<?xml version="1.0" encoding="UTF-8"?>

<faces-config
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
version="1.2">
<application>
    <state-manager>com.ibm.faces.application.DevelopmentStateManager</state-manager>
    <variable-resolver>com.ibm.faces.portlet.PortletVariableResolver</variable-resolver>
    <variable-resolver>org.springframework.web.jsf.DelegatingVariableResolver</variable-resolver>
</application>
<factory>
    <faces-context-factory>com.ibm.faces.context.AjaxFacesContextFactory</faces-context-factory>
    <render-kit-factory>com.ibm.faces.renderkit.AjaxRenderKitFactory</render-kit-factory>
</factory>

<lifecycle>
    <phase-listener>com.ibm.faces.webapp.ValueResourcePhaseListener</phase-listener>
</lifecycle>

</faces-config>

还有我的 spring-web.xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans         http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

<bean id="sessionBean" class="sanitycheck.SessionBean" scope="session">
               <aop:scoped-proxy/>
    </bean>

<bean id="pc_SanityCheckProjectView" class="pagecode.SanityCheckProjectView" scope="request" init-method="init">
               <aop:scoped-proxy/>
    <property name="sessionBean" ref="sessionBean"/>
</bean>
</beans>

如有必要,我可以提供我的其他文件,请告诉我。谢谢!

编辑:将 aop:scoped-proxy 添加到 Spring bean。

编辑:添加 portlet.xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" id="com.ibm.faces.portlet.FacesPortlet.3a22ca3014">
<portlet>
    <portlet-name>SanityCheckProject</portlet-name>
    <display-name xml:lang="en">SanityCheckProject</display-name>
    <display-name>SanityCheckProject</display-name>
    <portlet-class>com.ibm.faces.portlet.FacesPortlet</portlet-class>
    <init-param>
        <name>com.ibm.faces.portlet.page.view</name>
        <value>/SanityCheckProjectView.jsp</value>
    </init-param>
    <init-param>
        <name>whichOne</name>
        <value>Portlet1</value>
    </init-param>
    <init-param>
        <name>wps.markup</name>
        <value>html</value>
    </init-param>
    <expiration-cache>0</expiration-cache>
    <supports>
        <mime-type>text/html</mime-type>
        <portlet-mode>view</portlet-mode>
    </supports>
    <supported-locale>en</supported-locale>
    <resource-bundle>com.ibm.sanitycheckproject.nl.SanityCheckProjectPortletResource</resource-bundle>
    <portlet-info>
        <title>SanityCheckProject</title>
        <short-title>SanityCheckProject</short-title>
        <keywords>SanityCheckProject</keywords>
    </portlet-info>
</portlet>
<portlet>
    <portlet-name>SanityCheckPortlet2</portlet-name>
    <display-name xml:lang="en">SanityCheckPortlet2</display-name>
    <display-name>SanityCheckPortlet2</display-name>
    <portlet-class>com.ibm.faces.portlet.FacesPortlet</portlet-class>
    <init-param>
        <name>com.ibm.faces.portlet.page.view</name>
        <value>/SanityCheckProjectView.jsp</value>
    </init-param>
    <init-param>
        <name>whichOne</name>
        <value>Portlet2</value>
    </init-param>
    <init-param>
        <name>wps.markup</name>
        <value>html</value>
    </init-param>
    <expiration-cache>0</expiration-cache>
    <supports>
        <mime-type>text/html</mime-type>
        <portlet-mode>view</portlet-mode>
    </supports>
    <supported-locale>en</supported-locale>
    <resource-bundle>com.ibm.sanitycheckproject.nl.SanityCheckPortlet2PortletResource</resource-bundle>
    <portlet-info>
        <title>SanityCheckPortlet2</title>
        <short-title>SanityCheckPortlet2</short-title>
        <keywords>SanityCheckPortlet2</keywords>
    </portlet-info>
</portlet>
<default-namespace>http://SanityCheckProject/</default-namespace>
</portlet-app>
4

2 回答 2

0

在 Spring XML 配置中,您必须使用<aop:scoped-proxy/>标记。

http://static.springsource.org/spring/docs/3.0.x/reference/beans.html#beans-factory-scopes-other-injection

<!-- an HTTP Session-scoped bean exposed as a proxy -->
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
    <!-- this next element effects the proxying of the surrounding bean -->
    <aop:scoped-proxy/>
</bean>
于 2013-09-09T21:45:15.397 回答
0

我不知道这是否是最好的甚至是非常好的解决方案,但我最终所做的是创建两个自定义 portlet 范围,一个用于请求范围,一个用于会话范围。本质上,我的自定义范围所做的是,它们在请求的对象名称前加上 portlet 的名称空间,这似乎将所有内容分开。

这是我用于范围的代码:

请求范围:

import javax.faces.context.FacesContext;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.web.context.request.RequestScope;
import com.ibm.faces.portlet.httpbridge.ActionResponseWrapper;
import javax.portlet.RenderResponse;
import javax.portlet.filter.RenderResponseWrapper;

public class PortletRequestScope extends RequestScope {

@Override
public Object get(String name, ObjectFactory objectFactory) {
    Object response = FacesContext.getCurrentInstance().getExternalContext().getResponse();
    if (response instanceof RenderResponse) {
        String namespace=((RenderResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
        return super.get(namespace + name, objectFactory);
    }
    else if (response instanceof RenderResponseWrapper) {
        String namespace=((RenderResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
        return super.get(namespace + name, objectFactory);
    }
    else if (response instanceof ActionResponseWrapper) {
        String namespace=((ActionResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
        return super.get(namespace + name, objectFactory);
    }
    else {
        writeError(response);
    }
    return super.get(name, objectFactory);
}

@Override
public void registerDestructionCallback(String name, Runnable callback) {
    Object response = FacesContext.getCurrentInstance().getExternalContext().getResponse();
    if (response instanceof RenderResponse) {
        String namespace=((RenderResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
        super.registerDestructionCallback(namespace+name, callback);
    }
    else if (response instanceof RenderResponseWrapper) {
        String namespace=((RenderResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
        super.registerDestructionCallback(namespace+name, callback);
    }
    else if (response instanceof ActionResponseWrapper) {
        String namespace=((ActionResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
        super.registerDestructionCallback(namespace+name, callback);
    }
    else {
        writeError(response);
    }
    super.registerDestructionCallback(name, callback);
}

@Override
public Object remove(String name) {
    Object response = FacesContext.getCurrentInstance().getExternalContext().getResponse();
    if (response instanceof RenderResponse) {
        String namespace=((RenderResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
        return super.remove(namespace+name);
    }
    else if (response instanceof RenderResponseWrapper) {
        String namespace=((RenderResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
        return super.remove(namespace+name);
    }
    else if (response instanceof ActionResponseWrapper) {
        String namespace=((ActionResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
        return super.remove(namespace+name);
    }
    else {
        writeError(response);
    }
    return super.remove(name);
}

protected void writeError(Object response) {
    System.err.println("Error in PortletRequestScope");
    System.err.println("Response is unrecognized class: " + response.getClass().getCanonicalName());
    System.err.println("Please modify code to handle class");
}

}

会议范围:

import javax.faces.context.FacesContext;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.web.context.request.SessionScope;
import com.ibm.faces.portlet.httpbridge.ActionResponseWrapper;
import javax.portlet.RenderResponse;
import javax.portlet.filter.RenderResponseWrapper;

public class PortletSessionScope extends SessionScope {

@Override
public Object get(String name, ObjectFactory objectFactory) {
        Object response =  FacesContext.getCurrentInstance().getExternalContext().getResponse();
        if (response instanceof RenderResponse) {
            String namespace=((RenderResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
            return super.get(namespace + name, objectFactory);
        }
        else if (response instanceof RenderResponseWrapper) {
            String namespace=((RenderResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
            return super.get(namespace + name, objectFactory);
        }
        else if (response instanceof ActionResponseWrapper) {
            String namespace=((ActionResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
            return super.get(namespace + name, objectFactory);
        }
        else {
            writeError(response);
        }
    return super.get(name, objectFactory);
}

@Override
public void registerDestructionCallback(String name, Runnable callback) {
    Object response = FacesContext.getCurrentInstance().getExternalContext().getResponse();
    if (response instanceof RenderResponse) {
        String namespace=((RenderResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
        super.registerDestructionCallback(namespace+name, callback);
    }
    else if (response instanceof RenderResponseWrapper) {
        String namespace=((RenderResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
        super.registerDestructionCallback(namespace+name, callback);
    }
    else if (response instanceof ActionResponseWrapper) {
        String namespace=((ActionResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
        super.registerDestructionCallback(namespace+name, callback);
    }
    else {
        writeError(response);
    }
    super.registerDestructionCallback(name, callback);
}

@Override
public Object remove(String name) {
    Object response = FacesContext.getCurrentInstance().getExternalContext().getResponse();
    if (response instanceof RenderResponse) {
        String namespace=((RenderResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
        return super.remove(namespace+name);
    }
    else if (response instanceof RenderResponseWrapper) {
        String namespace=((RenderResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
        return super.remove(namespace+name);
    }
    else if (response instanceof ActionResponseWrapper) {
        String namespace=((ActionResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
        return super.remove(namespace+name);
    }
    else {
        writeError(response);
    }
    return super.remove(name);
}

protected void writeError(Object response) {
    System.err.println("Error in PortletSessionScope");
    System.err.println("Response is unrecognized class: " + response.getClass().getCanonicalName());
    System.err.println("Please modify code to handle class");
}

}

然后,在我的 spring-web.xml 中,我定义了我的自定义范围:

<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
    <map>
        <entry key="portletRequestScope">
            <bean class="com.test.scope.PortletRequestScope"/>
        </entry>
        <entry key="portletSessionScope">
            <bean class="com.test.portlet.scope.PortletSessionScope"/>
        </entry>
    </map>
</property>
</bean>

当我定义我的实际 Spring bean 时,我使用了我的自定义范围而不是常规范围——例如:

<bean id="sessionBean" class="com.test.managedbeans.SessionBean"
scope="portletSessionScope" lazy-init="true"/>

至少,这样做似乎适用于我在 WebSphere Portal 上的 JSF + Spring 的特定情况,希望这对其他人有用。

于 2014-02-18T20:34:25.313 回答