# 13. JSF 集成
# 13.1.导言
Spring Web Flow 提供了一种 JSF 集成,允许你使用带有 Spring Web Flow 控制器的 JSF UI 组件模型。Web Flow 还提供了用于 JSF 环境的 Spring 安全标记库,有关更多详细信息,请参见Section 13.9, “Using the Spring Security Facelets Tag Library”。
Spring Web Flow2.5 需要 JSF2.2 或更高版本。
# 13.2.配置 web.xml
第一步是将请求路由到web.xml
文件中的DispatcherServlet
。在这个示例中,我们将所有以/spring/
开头的 URL 映射到 Servlet。 Servlet 需要进行配置。在 Servlet 中使用init-param
来传递contextConfigLocation
。这是 Web 应用程序 Spring 配置的位置。
<servlet>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/web-application-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<url-pattern>/spring/*</url-pattern>
</servlet-mapping>
为了让 JSF 正确地引导,FacesServlet
必须在web.xml
中进行配置,就像正常情况一样,即使在使用 Spring Web 流的 JSF 时,通常根本不需要通过它路由请求。
<!-- Just here so the JSF implementation can initialize, *not* used at runtime -->
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Just here so the JSF implementation can initialize -->
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.faces</url-pattern>
</servlet-mapping>
在 web.xml 中,使用 Facelets 而不是 JSP 通常需要这样做:
!-- Use JSF view templates saved as *.xhtml, for use with Facelets -->
<context-param>
<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
<param-value>.xhtml</param-value>
</context-param>
# 13.3.配置用于 JSF 的 Web 流
本节将解释如何使用 JSF 配置 Web 流。同时支持 Java 和 XML 风格的配置。以下是 XML 中的 Web 流和 JSF 的示例配置:
<?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:webflow="http://www.springframework.org/schema/webflow-config"
xmlns:faces="http://www.springframework.org/schema/faces"
si:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/webflow-config
http://www.springframework.org/schema/webflow-config/spring-webflow-config.xsd
http://www.springframework.org/schema/faces
http://www.springframework.org/schema/faces/spring-faces.xsd">
<!-- Executes flows: the central entry point into the Spring Web Flow system -->
<webflow:flow-executor id="flowExecutor">
<webflow:flow-execution-listeners>
<webflow:listener ref="facesContextListener"/>
</webflow:flow-execution-listeners>
</webflow:flow-executor>
<!-- The registry of executable flow definitions -->
<webflow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices" base-path="/WEB-INF">
<webflow:flow-location-pattern value="**/*-flow.xml" />
</webflow:flow-registry>
<!-- Configures the Spring Web Flow JSF integration -->
<faces:flow-builder-services id="flowBuilderServices" />
<!-- A listener maintain one FacesContext instance per Web Flow request. -->
<bean id="facesContextListener"
class="org.springframework.faces.webflow.FlowFacesContextLifecycleListener" />
</beans>
下面是 Java 配置中的相同示例:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.faces.config.*;
@Configuration
public class WebFlowConfig extends AbstractFacesFlowConfiguration {
@Bean
public FlowExecutor flowExecutor() {
return getFlowExecutorBuilder(flowRegistry())
.addFlowExecutionListener(new FlowFacesContextLifecycleListener())
.build();
}
@Bean
public FlowDefinitionRegistry flowRegistry() {
return getFlowDefinitionRegistryBuilder()
.setBasePath("/WEB-INF")
.addFlowLocationPattern("**/*-flow.xml").build();
}
主要的要点是安装FlowFacesContextLifecycleListener
,它在 Web 流请求的持续时间内管理单个 FacesContext,以及使用flow-builder-services
自定义名称空间中的flow-builder-services
元素来配置 JSF 环境的呈现。
在 JSF 环境中,你还需要这样的 Spring MVC 相关配置:
<?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:faces="http://www.springframework.org/schema/faces"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/faces
http://www.springframework.org/schema/faces/spring-faces.xsd">
<faces:resources />
<bean class="org.springframework.faces.webflow.JsfFlowHandlerAdapter">
<property name="flowExecutor" ref="flowExecutor" />
</bean>
</beans>
resources
自定义名称空间元素将 JSF 资源请求委托给 JSF 资源 API。JsfFlowHandlerAdapter
是通常用于 Web 流的FlowHandlerAdapter
的替换。这个适配器用JsfAjaxHandler
而不是SpringJavaSciprtAjaxHandler
初始化自己。
当使用 Java Config 时,AbstractFacesFlowConfiguration
基类会自动注册JsfResourceRequestHandler
,因此没有进一步的工作要做。
# 13.4.替换 JSF 管理的 Bean 设施
当将 JSF 与 Spring Web 流一起使用时,你可以将 JSF Managed Bean 工具完全替换为 Web 流管理变量和 Spring Managed bean 的组合。它为你提供了对托管对象的生命周期的更多控制,并为域模型的初始化和执行提供了定义良好的挂钩。此外,由于你可能已经在业务层中使用了 Spring,因此它减少了必须维护两个不同的托管 Bean 模型的概念开销。
在进行纯粹的 JSF 开发时,你很快就会发现请求作用域不够长,不足以存储驱动复杂事件驱动视图的会话模型对象。在 JSF 中,通常的选择是开始将事情放入会话范围,在进入应用程序的另一个视图或功能区域之前,需要清理对象,这是一个额外的负担。真正需要的是介于请求和会话范围之间的托管范围。JSF 提供了可以通过 UIViewRoot.getViewMap()以编程方式访问的闪存和视图范围。 Spring Web Flow 提供对 Flash、View、Flow 和 Conversation 范围的访问。这些作用域通过 JSF 变量解析器无缝地集成在一起,并且在所有 JSF 应用程序中都是相同的。
# 13.4.1.使用流量变量
声明和管理模型的最简单和最自然的方法是使用流量变量。你可以在流程的开始声明这些变量:
<var name="searchCriteria" class="com.mycompany.myapp.hotels.search.SearchCriteria"/>
然后通过 EL 在一个流的 JSF 视图模板中引用这个变量:
<h:inputText id="searchString" value="#{searchCriteria.searchString}"/>
请注意,在从模板引用变量时,不需要在变量的作用域中加上前缀(如果需要更具体地说明,可以这样做)。与标准的 JSF bean 一样,将搜索所有可用的作用域以寻找匹配的变量,因此你可以在流定义中更改变量的作用域,而无需修改引用该变量的 EL 表达式。
你还可以定义视图实例变量,这些变量的作用域为当前视图,并在转换到另一个视图时自动进行清理。这在 JSF 中非常有用,因为视图通常被构造为在转换到另一个视图之前处理跨多个请求的多个页面内事件。
要定义视图实例变量,可以在view-state
定义中使用var
元素:
<view-state id="enterSearchCriteria">
<var name="searchCriteria" class="com.mycompany.myapp.hotels.search.SearchCriteria"/>
</view-state>
# 13.4.2.使用范围 Spring bean
虽然定义 AutoWired 流实例变量提供了很好的模块化和可读性,但可能会出现希望利用 Spring 容器的其他功能的情况,例如 AOP。在这些情况下,你可以在 Spring 应用程序上下文中定义 Bean,并为其提供一个特定的 Web 流范围:
<bean id="searchCriteria" class="com.mycompany.myapp.hotels.search.SearchCriteria" scope="flow"/>
这种方法的主要区别在于,在通过 EL 表达式首次访问 Bean 之前,不会对 Bean 进行完全初始化。这种通过 EL 的惰性实例化非常类似于 JSF 托管 bean 的典型分配方式。
# 13.4.3.操纵模型
在视图呈现之前需要初始化模型(例如通过从数据库加载持久实体)是很常见的,但是 JSF 本身并没有为这种初始化提供任何方便的挂钩。流定义语言通过其Actions为此提供了一种自然的工具。 Spring Web 流为将一个动作的结果转换为特定于 JSF 的数据结构提供了一些额外的便利。例如:
<on-render>
<evaluate expression="bookingService.findBookings(currentUser.name)"
result="viewScope.bookings" result-type="dataModel" />
</on-render>
这将获取bookingService.findBookings
方法的结果,并将其包装到自定义的 JSF 数据模型中,以便该列表可以在标准的 JSF DataTable 组件中使用:
<h:dataTable id="bookings" styleClass="summary" value="#{bookings}" var="booking"
rendered="#{bookings.rowCount > 0}">
<h:column>
<f:facet name="header">Name</f:facet>
#{booking.hotel.name}
</h:column>
<h:column>
<f:facet name="header">Confirmation number</f:facet>
#{booking.id}
</h:column>
<h:column>
<f:facet name="header">Action</f:facet>
<h:commandLink id="cancel" value="Cancel" action="cancelBooking" />
</h:column>
</h:dataTable>
# 13.4.4.数据模型实现
在上面的示例中,result-type=“datamodel”将使用自定义的DataModel
类型包装 list<Booking>。自定义DataModel
提供了额外的便利,例如可以在请求范围之外的存储中进行序列化,以及访问 EL 表达式中当前选定的行。例如,在从一个视图回发操作事件时,你可以在选定行的模型实例上执行操作,该视图中的操作事件是由 DataTable 中的组件触发的:
<transition on="cancelBooking">
<evaluate expression="bookingService.cancelBooking(bookings.selectedRow)" />
</transition>
Spring Web 流提供了两种自定义的数据模型类型:OneSelectionTrackingListDataModel
和ManySelectionTrackingListDataModel
。由于名称表示它们会跟踪一个或多个选定的行。这是在SelectionTrackingActionListener
侦听器的帮助下完成的,该侦听器响应 JSF 操作事件,并调用SelectinAware
数据模型上的 appoRiate 方法来记录当前单击的行。
要理解这是如何配置的,请记住FacesConversionService
在启动时针对别名“datamodel”注册了DataModelConverter
。在流定义中使用 result-type=“datamodel”时,将使用DataModelConverter
。转换器然后用OneSelectionTrackingListDataModel
的实例包装给定的列表。要使用ManySelectionTrackingListDataModel
,你需要注册自己的自定义转换器。
# 13.5.使用 Spring Web 流处理 JSF 事件
Spring Web 流允许你以解耦的方式处理 JSF 动作事件,不需要你的 Java 代码中的 JSF API 的直接依赖。实际上,这些事件通常可以完全用 Flow Definiton 语言来处理,而不需要任何定制的 Java 操作代码。这允许更敏捷的开发过程,因为在连接事件(JSF 视图模板和 SWF 流定义)中操作的工件可以立即刷新,而不需要构建和重新部署整个应用程序。
# 13.5.1.处理 JSF 页面内操作事件
在 JSF 中,一个简单但常见的情况是,需要向一个导致以某种方式操纵模型的事件发出信号,然后重新显示相同的视图,以反映模型已更改的状态。流定义语言在transition
元素中对此有特殊的支持。
这方面的一个很好的例子是一个分页列表结果表。假设你希望能够只加载和显示一个大型结果列表的一部分,并允许用户对结果进行分页。加载和显示列表的初始view-state
定义为:
<view-state id="reviewHotels">
<on-render>
<evaluate expression="bookingService.findHotels(searchCriteria)"
result="viewScope.hotels" result-type="dataModel" />
</on-render>
</view-state>
你可以构造一个 JSF DataTable,该 DataTable 显示当前的hotels
列表,然后在表下面放置一个“more results”链接:
<h:commandLink id="nextPageLink" value="More Results" action="next"/>
此 CommandLink 从其 Action 属性发出“Next”事件的信号。然后,你可以通过添加view-state
定义来处理该事件:
<view-state id="reviewHotels">
<on-render>
<evaluate expression="bookingService.findHotels(searchCriteria)"
result="viewScope.hotels" result-type="dataModel" />
</on-render>
<transition on="next">
<evaluate expression="searchCriteria.nextPage()" />
</transition>
</view-state>
在这里,你通过增加 SearchCriteria 实例上的页面数量来处理“Next”事件。然后使用更新的条件再次调用on-render
操作,这将导致将结果的下一页加载到数据模型中。由于在transition
元素上没有to
属性,因此将重新呈现相同的视图,并且模型中的更改将反映在视图中。
# 13.5.2.处理 JSF 动作事件
除了页面内事件之外,下一个逻辑级别是需要导航到另一个视图的事件,并在此过程中对模型进行一些操作。使用纯 JSF 实现这一点需要向 faces-config.xml 添加导航规则,还可能需要在 JSF 托管的 Bean 中添加一些中间 Java 代码(这两个任务都需要重新部署)。使用流定义语言,你可以在一个地方以与页面内事件处理方式非常相似的方式简洁地处理这样的情况。
继续处理结果的分页列表的用例,假设我们希望显示的 DataTable 中的每一行都包含指向该行实例的详细页的链接。可以向包含以下commandLink
组件的表中添加一列:
<h:commandLink id="viewHotelLink" value="View Hotel" action="select"/>
这会引发“select”事件,你可以通过向现有的view-state
元素添加另一个transition
元素来处理该事件:
<view-state id="reviewHotels">
<on-render>
<evaluate expression="bookingService.findHotels(searchCriteria)"
result="viewScope.hotels" result-type="dataModel" />
</on-render>
<transition on="next">
<evaluate expression="searchCriteria.nextPage()" />
</transition>
<transition on="select" to="reviewHotel">
<set name="flowScope.hotel" value="hotels.selectedRow" />
</transition>
</view-state>
这里,通过将当前选定的酒店实例从 DataTable 推入流作用域来处理“select”事件,以便它可以被“reviewhotel”view-state
引用。
# 13.5.3.执行模型验证
在对模型应用更改之前,JSF 为在字段级验证输入提供了有用的工具,但是当你需要在应用了更新之后在模型级执行更复杂的验证时,通常,你不得不在托管的 Bean 中向你的 JSF 操作方法添加更多的自定义代码。这种类型的验证通常是域模型本身的责任,但是很难在不引入对域层中的 JSF API 的不希望的依赖的情况下,将任何错误消息传播回视图。
有了 WebFlow,你就可以在业务代码中使用通用的、低层次的MessageContext
,在呈现时,添加在那里的任何消息都将可用于FacesContext
。
例如,假设你有一个视图,用户在其中输入必要的详细信息以完成酒店预订,并且你需要确保入住和退房日期符合给定的一组业务规则。你可以从transition
元素调用这样的模型级验证:
<view-state id="enterBookingDetails">
<transition on="proceed" to="reviewBooking">
<evaluate expression="booking.validateEnterBookingDetails(messageContext)" />
</transition>
</view-state>
在这里,“Proceed”事件是通过调用 Booking 实例上的模型级验证方法来处理的,传递一般的MessageContext
实例,以便可以记录消息。然后,这些消息可以与带有h:messages
组件的任何其他 JSF 消息一起显示,
# 13.5.4.在 JSF 中处理 Ajax 事件
JSF 提供了用于发送 Ajax 请求以及在服务器端执行部分处理和呈现的内置支持。你可以的 通过 <f:ajax>指定用于部分呈现的 ID 列表 Facelets 标签。
在 Spring Web Flow 中,你还可以通过 Render 操作指定用于在服务器端进行部分呈现的 ID:
<view-state id="reviewHotels">
<on-render>
<evaluate expression="bookingService.findHotels(searchCriteria)"
result="viewScope.hotels" result-type="dataModel" />
</on-render>
<transition on="next">
<evaluate expression="searchCriteria.nextPage()" />
<render fragments="hotels:searchResultsFragment" />
</transition>
</view-state>
# 13.6.在页面上嵌入流
默认情况下,当流进入视图状态时,它会在呈现视图之前执行客户端重定向。这种方法被称为 post-redirect-get。它的优点是将一个视图的表单处理与下一个视图的呈现分离开来。因此,浏览器的后退和刷新按钮可以无缝地工作,而不会引起任何浏览器警告。
通常情况下,从用户的角度来看,客户端重定向是透明的。然而,在某些情况下,后重定向可能不会带来同样的好处。例如,有时在页面上嵌入一个流并通过 Ajax 请求只刷新呈现该流的页面区域来驱动它可能是有用的。在这种情况下,不仅没有必要使用客户端重定向,而且在保持页面周围内容不变方面也不是所需的行为。
要指示一个流应该在“页面嵌入”模式下执行,你所需要做的就是传递一个名为“模式”的额外的流输入属性,其值为“嵌入”。下面是一个顶级容器流以嵌入式模式调用子流的示例:
<subflow-state id="bookHotel" subflow="booking">
<input name="mode" value="'embedded'"/>
</subflow-state>
当以“页面嵌入”模式启动时,子流将不会在 Ajax 请求期间发出流执行重定向。
如果你想查看嵌入式流的示例,请参考 WebFlow-PrimeFaces-Showcase 项目。你可以在本地签出源代码,将其构建为 Maven 项目,并将其导入到 Eclipse 中:
cd some-directory
svn co https://src.springframework.org/svn/spring-samples/webflow-primefaces-showcase
cd webflow-primefaces-showcase
mvn package
# import into Eclipse
你需要查看的特定示例位于“Advanced Ajax”选项卡下,它被称为“带有嵌入式子流的 Top Flow”。
# 13.7.在相同的状态下重定向
默认情况下,只要当前请求不是 Ajax 请求,Web 流就会进行客户端重定向,即使它仍然处于相同的视图状态。例如,在表单验证失败之后,这是非常有用的。如果用户点击 Refresh 或 Back,他们将不会看到任何浏览器警告。如果网络流不做重定向,他们就会这么做。
这可能会导致 JSF 环境特有的问题,在 JSF 环境中,特定的 Sun Mojarra 侦听器组件缓存 FacesContext,假设在整个 JSF 生命周期中都有相同的实例。然而,在 Web 流中,呈现阶段被暂时搁置,客户端重定向执行。
Web 流的默认行为是可取的,而且 JSF 应用程序不太可能遇到这个问题。这是因为在 JSF 组件库中,Ajax 通常是默认启用的,并且在 Ajax 请求期间,Web 流不会重定向。但是,如果遇到此问题,你可以在相同的视图中禁用客户端重定向,如下所示:
<webflow:flow-executor id="flowExecutor">
<webflow:flow-execution-attributes>
<webflow:redirect-in-same-state value="false"/>
</webflow:flow-execution-attributes>
</webflow:flow-executor>
# 13.8.使用 JSF 处理文件上传
大多数 JSF 组件提供程序都包含某种形式的“文件上传”组件。通常,在使用这些组件时,JSF 必须完全控制对多部分请求的解析,并且 Spring MVC 的MultipartResolver
不能使用。
Spring Web 流已经用来自 PrimeFaces 的文件上传组件进行了测试。检查你的 JSF 组件库的文档,以便其他提供者了解如何配置文件上传。
# 13.8.1.用 primeFaces 上传文件
PrimeFaces 提供了用于上载文件的<p:fileUpload>
组件。为了使用该组件,你需要配置org.primefaces.webapp.filter.FileUploadFilter
Servlet 过滤器。过滤器需要根据 Spring MVC 的DispatcherServlet
在你的web.xml
中进行配置:
<filter>
<filter-name>PrimeFaces FileUpload Filter</filter-name>
<filter-class>org.primefaces.webapp.filter.FileUploadFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>PrimeFaces FileUpload Filter</filter-name>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
</filter-mapping>
<context-param>
<param-name>primefaces.UPLOADER</param-name>
<param-value>commons</param-value>
</context-param>
有关更多详细信息,请参见PrimeFaces 文档 (opens new window)。
# 13.9.使用 Spring Security Facelets 标记库
要使用库,你需要创建一个.taglib.xml
文件,并将其注册在web.xml
中。
创建具有以下内容的/WEB-INF/springsecurity.taglib.xml
文件:
<?xml version="1.0"?>
<!DOCTYPE facelet-taglib PUBLIC
"-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
"http://java.sun.com/dtd/facelet-taglib_1_0.dtd">
<facelet-taglib>
<namespace>http://www.springframework.org/security/tags</namespace>
<tag>
<tag-name>authorize</tag-name>
<handler-class>org.springframework.faces.security.FaceletsAuthorizeTagHandler</handler-class>
</tag>
<function>
<function-name>areAllGranted</function-name>
<function-class>org.springframework.faces.security.FaceletsAuthorizeTagUtils</function-class>
<function-signature>boolean areAllGranted(java.lang.String)</function-signature>
</function>
<function>
<function-name>areAnyGranted</function-name>
<function-class>org.springframework.faces.security.FaceletsAuthorizeTagUtils</function-class>
<function-signature>boolean areAnyGranted(java.lang.String)</function-signature>
</function>
<function>
<function-name>areNotGranted</function-name>
<function-class>org.springframework.faces.security.FaceletsAuthorizeTagUtils</function-class>
<function-signature>boolean areNotGranted(java.lang.String)</function-signature>
</function>
<function>
<function-name>isAllowed</function-name>
<function-class>org.springframework.faces.security.FaceletsAuthorizeTagUtils</function-class>
<function-signature>boolean isAllowed(java.lang.String, java.lang.String)</function-signature>
</function>
</facelet-taglib>
接下来,在 web.xml 中注册上述文件 taglib:
<context-param>
<param-name>javax.faces.FACELETS_LIBRARIES</param-name>
<param-value>/WEB-INF/springsecurity.taglib.xml</param-value>
</context-param>
现在你已经准备好在视图中使用标记库了。你可以使用授权标记有条件地包括嵌套内容:
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:sec="http://www.springframework.org/security/tags">
<sec:authorize ifAllGranted="ROLE_FOO, ROLE_BAR">
Lorem ipsum dolor sit amet
</sec:authorize>
<sec:authorize ifNotGranted="ROLE_FOO, ROLE_BAR">
Lorem ipsum dolor sit amet
</sec:authorize>
<sec:authorize ifAnyGranted="ROLE_FOO, ROLE_BAR">
Lorem ipsum dolor sit amet
</sec:authorize>
</ui:composition>
你还可以在任何 JSF 组件的呈现属性或其他属性中使用几个 EL 函数中的一个:
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:sec="http://www.springframework.org/security/tags">
<!-- Rendered only if user has all of the listed roles -->
<h:outputText value="Lorem ipsum dolor sit amet" rendered="#{sec:areAllGranted('ROLE_FOO, ROLE_BAR')}"/>
<!-- Rendered only if user does not have any of the listed roles -->
<h:outputText value="Lorem ipsum dolor sit amet" rendered="#{sec:areNotGranted('ROLE_FOO, ROLE_BAR')}"/>
<!-- Rendered only if user has any of the listed roles -->
<h:outputText value="Lorem ipsum dolor sit amet" rendered="#{sec:areAnyGranted('ROLE_FOO, ROLE_BAR')}"/>
<!-- Rendered only if user has access to given HTTP method/URL as defined in Spring Security configuration -->
<h:outputText value="Lorem ipsum dolor sit amet" rendered="#{sec:isAllowed('/secured/foo', 'POST')}"/>
</ui:composition>
# 13.10.第三方组件库集成
Spring Web 流 JSF 集成力求与任何第三方 JSF 组件库兼容。通过在 SWF 驱动的 JSF 生命周期内尊重 JSF 规范的所有标准语义,第三方库通常应该“正常工作”。需要记住的主要一点是,由于 Web 流请求不是通过标准 FacesServlet 路由的,所以 web.xml 中的配置会略有变化。通常,传统上映射到 FacesServlet 的任何内容都应该映射到 Spring DispatcherServlet。(如果你正在逐页迁移遗留的 JSF 应用程序,也可以映射到这两个应用程序)。