将struts程序移植到JSF

本文介绍如何集成Struts、Tiles与JSF技术,解决集成过程中出现的各种问题,如响应提交、资源未找到及表单提交显示等问题。

 将Struts 应用程序移植到 JSF

为了将 Struts Web 应用程序与 JSF 集成,遵循以下步骤:

  • 将 struts-faces.jar 文件与特定于 JSF 的 JAR(jsf-api.jar、jsf-ri.jar) 添加到 Web 应用程序的 WEB-INF/lib目录中。 
  • 如果准备使用 JSF 和 JSTL,则将特定于 JSTL 的 JAR(jstl.jar、standard.jar)添加到 WEB-INF/lib 文件夹中。这一步只有在部署到常规 Tomcat 时才会需要。JWSDP 已经提供了这些 JAR。 
  • 修改 Web 应用程序部署描述符 ( /WEB-INF/web.xml)以便有一个 Faces Servlet 项, 如清单 5 所示。 
  • 修改 JSP 页面以使用 JSF 和 Struts-Faces 标记而不是 Struts 标记。特别是用 Struts-Faces 相应标记替换 html、b ase、 form 和 errors 标记。用 JSF 相应标记替换 text 、 textarea 和 radio 标记。Struts-Faces 没有单独针对这些的标记。尽管没有要求,但是您可能还会考虑用 JSTL 标记替换 Struts Logic 标记。 
  • 对于每一个使用 JSF 标记的 JSP,修改 struts-config.xml 文件以在指向该 JSP 的 Action Mapping 中的 global-forwards和 local-forwards中加入前缀 /faces。 
  • 如果 Web 应用程序使用了任何您创建的自定义组件,那么您就需要用 JSF 实现的默认 RenderKit 注册它们。可以通过在 WEB-INF 文件中创建一个 faces-config.xml 文件、并增加每一个组件和 renderer 的项做到这一点。不过,要记住 faces-config.xml 文件已经绑定在 struts-faces.jar 文件中了。您必须从 struts-faces.jar 文件中提出它、加入自己的内容并将它放到 WEB-INF文件夹中。


清单 5. 在 web.xml 中声明 FacesServlet

 

<!-- JavaServer Faces Servlet Configuration -->
<servlet>
<servlet-name>faces </servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet </servlet-class>
<load-on-startup>1 </load-on-startup>
</servlet>

<!-- JavaServer Faces Servlet Mapping -->
<servlet-mapping>
  <servlet-name>faces </servlet-name>
  <url-pattern>/faces/* </url-pattern>
</servlet-mapping>

集成 Struts-Faces 和 Tiles 的挑战

Struts-Faces 库提供了 Struts 与 JSF 之间的一个高效的桥梁,使得在 J2EE Web 应用程序中拥有丰富的表示层成为现实。您可以通过在组合体中添加 Titles 使表示层更丰富,这样不仅得到了 Struts 和 JSF 组合的好处,而且还可以高效地重复使用不同的 JSP 页面,因为它们将由可以根据需要添加或者删除的组件部分或者 tiles 所构成。

本文已经展示了 Struts 和 JSP 的集成,您会想将 Tiles 加入到组合中只是小事一桩,是不是?

不幸的是,JSF 仍然处于早期阶段,还没有给出最后的发布。基于这一考虑,Struts-Faces 集成软件开发仍然在不断地发展以包括 JSF 的不同的功能,并且还没有支持 Tiles。

Struts 和 Tiles 可以无缝地共同工作,但是在集成之路上您会遇到路障。在下面几小节中,您会看到在与 Tiles 共同使用 Struts-Faces 集成库时经常遇到的问题的汇总。对于每一个问题,我们详细说明了一个修改 Struts-Faces 类的解决方案。我们将用一个航班搜索示例解释这个解决方案。

清单 6 展示了航班搜索页面的布局。注意我们称它为航班搜索页面而不是 FlightSearch.jsp。这是因为 FlightSearch JSP 是用户在 foobar 旅行 Web 站点看到的合成页面的主体。

现在,我们保持实际的 FlightSearch.jsp 不变。我们将随着进展改变它。在您这边,也需要用航班搜索页的定义创建一个 Tiles 定义文件。清单 7(紧接着清单 6)展示了 Tiles 定义文件中航班搜索页的一项。注意对带有 extends 属性的主布局模板的重复使用。

在清单 6 和 7 后是每一个可能的挑战。


清单 6. 航班搜索例子的 Tiles 布局

 

<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %>
<%@ taglib uri="http://jakarta.apache.org/struts/tags-faces"prefix="s" %>

<!-- Layout component parameters: header, menu, body, footer -->
<s:html>
<head>
  <title> <tiles:getAsString name="title"/> </title>
  <s:base/>
</head>
<body>
  <TABLE border="0" width="100%" cellspacing="5">
    <tr>
      <td> <tiles:insert attribute="header"/> </td>
    </tr>

    <tr>
      <td> <tiles:insert attribute="body"/> </td>
    </tr>

    <tr> <td> <hr> </td> </tr>

    <tr>
      <td> <tiles:insert attribute="footer" /> </td>
    </tr>
  </TABLE>
</body>
</s:html>
<!-- Master Layout definition  -->
<definition name="foobar.master-layout"
  path="/faces/layout/MasterLayout.jsp">

      <put name="title"  value="Welcome to Foo Bar Travels" />
      <put name="header" value="/faces/common/header.jsp" />
      <put name="footer" value="/faces/common/footer.jsp" />
      <put name="body"   value="" />
</definition>

  <!-- Definition for Flight Search Page -->
<definition name="/foobar.flight-search"
  extends="foobar.master-layout">

      <put name="body"   value="/faces/FlightSearch.jsp" />
</definition>

响应已经提交

这是您在试图访问航班搜索表单时马上会看到的第一个问题。小心查看堆栈跟踪。您会看到问题出在类 com.sun.faces.lifecycle.ViewHandlerImpl 上。这是一个实现了 ViewHandler 接口的 JSF-RI 类。

图 2展示了 ViewHandler 所扮演的角色。这是一个将请求转发给下一页的类。在转发请求时,它不在转发前检查响应的状态 -- 这只有在使用 Tiles 时才会发生,因为 Tiles 内部将 JSP 页面包括在响应内,而 JSF-RI 在第一次转发后提交响应、然后试图再次转发给下面的包括 JSP 的 Tiles。

要解决这个问题,必须创建一个自定义的 ViewHandler 实现,它将检查响应的状态以确定它是否提交过。如果响应没有提交过,那么请求就转发给下一页,否则,就加入请求并显示相应的 JSP。我们将创建一个名为 STFViewHandlerImpl 的类,它实现了 ViewHandler 接口并实现了所需要的方法 renderView()。 清单 8 展示了 STFViewHandlerImpl 中的 renderView() 方法:


清单 8. STFViewHandlerImpl 中的 renderView()方法

RequestDispatcher rd = null;
Tree tree = context. getTree ( );
String requestURI = context. getTree ( ). getTreeId ( );
rd = request. getRequestDispatcher (requestURI );

/** If the response is committed, include the resource **/
if ( !response. isCommitted ( ) ) {
   rd. forward (request, context. getServletResponse ( ) );
}
else {
   rd. include (request, context. getServletResponse ( ) );
}

现在您实现了自己的 ViewHandler ,如何通知 JSF-RI 使用您的 ViewHandler 而不是默认的实现呢?要回答这个问题,就必须理解 FacesServlet 的工作过程。

在 Faces 初始化过程中, FacesServlet 会让 LifecycleFactory 实现返回 Lifecycle 类的一个实现,如清单 9 所示:


清单 9. FacesServlet 中 Faces 的初始化

 

//Get the LifecycleFactory from the Factory Finder
LifecycleFactory factory = (LifecycleFactory )
  FactoryFinder. getFactory ( "javax.faces.lifecycle.LifecycleFactory" );

//Get the context param from web.xml
String lifecycleID =
getServletContext ( ). getInitParameter ( "javax.faces.lifecycle.LIFECYCLE_ID" );

//Get the Lifecycle Implementation
Lifecycle lifecycle = factory. getLifecycle (lifeCycleID );

Lifecycle 实现对象拥有在呈现响应阶段要使用的 ViewHandler 。您可以通过对 Lifecycle 实现调用 setViewHandler 方法让自己的 ViewHandler 实现成为默认的。

现在问题变为如何得到默认 Lifecycle 实现?回答是不需要这样做。只要创建一个新的实现并用一个惟一 ID 注册它,如清单 10 所示:


清单 10. 注册自定义 ViewHandler 和 Lifecycle

 

//Get the LifecycleFactory from the Factory Finder
LifecycleFactory factory = (LifecycleFactory )
  FactoryFinder. getFactory ( "javax.faces.lifecycle.LifecycleFactory" );

//Create a new instance of Lifecycle implementation -
//com.sun.faces.lifecycle.LifecycleImpl
//According to the documentation, factory.getLifecycle("STFLifecycle")
//should work, but JSF-RI has a defect.
//Hence this workaround of creating a RI class explicitly.
LifecycleImpl stfLifecycleImpl = new LifecycleImpl ( );

//Create a new instance of our STFViewHandler and set it on the Lifecycle
stfLifecycleImpl. setViewHandler ( new STFViewHandlerImpl ( ) );

//Register the new lifecycle with the factory with a unique
//name "STFLifecycle"
factory. addLifecycle ( "STFLifecycle", stfLifecycleImpl );
您可以看到  lifecycleId 硬编码为  STFLifecycle 。实际上不是这样。当您回过头分析  清单 9时就会清楚。  FacesServlet 从在 web.xml 文件中声明的上下文参数中得到名为  javax.faces.lifecycle.LIFECYCLE_ID 的 lifecycle ID,如下所示:
     <context-param>
        <param-name>javax.faces.lifecycle.LIFECYCLE_ID </param-name>
        <param-value>STFLifecycle </param-value>
    </context-param>

因为 FacesServlet 取决于其初始化时的 Lifecycle 实现,在 清单 10中展示的代码应该在 FacesServlet 初始化之前执行。通过创建另一个 servlet 并在 FacesServlet 之前初始化它而做到这一点。

但是一种更聪明的办法是实现一个 ServletContextListener 接口。这个类声明两个方法: contextInitialized() 和 contextDestroyed() ,在 Web 应用程序被创建及 Web 应用程序被销毁之前会分别调用它们。因而 清单 10中的代码在 contextInitialized() 方法中执行,而自定义 ViewHandler 已经用标识名 STFLifecycle 注册到 Lifecycle ,并且可被 FacesServlet 使用。 ServletContextListener 类本身是在 web.xml 文件中声明的,如下所示:

<listener>
  <listener-class>foo.bar.stf.application.STFContextListener
  </listener-class>
</listener>

这不是注册一个带有自定义 ViewHandler 的 Lifecycle 惟一方法。事实上 FactoryFinder 实现了自己的发现算法以发现 Factory 对象,包括 LifecycleFactory 。这些机制按照顺序包括在系统属性中查看工厂实现类名的机制、faces.properties file、或者 1.3 Services 发现机制( META-INF/services/{factory-class-name} )。不过,我们讨论的这种机制是最容易的,也是最不具有破坏性的一种。

404 Resource Not Found

在解决了提交响应的问题后,单击任何一个 Tiles 特定的链接或者输入一个会呈现 Faces 响应的 URL。在这里,可以输入显示 FlightSearchForm 的 URL。

在这样做了以后,您会得到一个 foobar.flight-search - 404 Resource Not Found 错误。 foobar.flight-search 是航班搜索页面的 Tiles 定义的名字。 FacesRequestProcessor 不能处理 Tiles 请求(因为它扩展的是RequestProcessor 而不是 TilesRequestProcessor ),所以会得到错误。

为解决这个问题,我们将创建一个名为 STFRequestProcessor (表示 Struts-Tiles-Faces Request Processor)的新的请求处理程序。现在我们将拷贝 FacesRequestProcessor 的所有代码到这个新类中。惟一的区别是 STFRequestProcessor 继承的是 TilesRequestProcessor 而不是继承常规的 RequestProcessor 。这个新的 RequestProcessor 可以处理 Tiles 请求。清单 11 详细列出了这个 STFRequestProcessor :


清单 11. STFRequestProcessor.java

正如您所知道的, Struts 框架的 RequestProcessor 是在 struts-config.xml 文件中指定的。将下面的项添加到 struts-cinfig.xml 文件中后, STFRequestProcessor 就成为处理程序:

<controller processorClass="foobar.stf.application.STFRequestProcessor" />

表单提交显示返回同一个表单

由于 STFRequestProcessor 的作用,这时您就可以浏览并查看航班页面了。不过,在提交航班搜索表单时,您会得到返回来的同一个表单,而且没有页头和页脚!并且没有验证错误。事实上,根本就没有进行验证!

为了了解到底发生了什么事情,我们用浏览器回到航班页面并检查 HTML 源代码。您会看到像下面这样的一项:

 


 

注意表单 action 是指向 JSP 页而不是一个 .do 的。啊哈!这就是问题!这不是由于同时使用 Tiles 和 Struts-Faces 而带来的新问题,Struts-Faces 的默认行为是让 JSP 与表单 action 有同样的名字。这种行为在有单一的 JSP 页(如在前面的 Struts-Faces 例子中)时没有问题。 清单 3展示了原来的 FlightSearch.jsp,让我们继续并像下面这样修改 action:

 


 

当然,光有这种修改并不能解决问题。作了这种改变后,您就会发现 STFRequestProcessor 不能找到 ActionForm 。显然还需要其他的改变。

不过,在继续往下之前,看一下图&#160 5。它显示了在呈现负责 Struts-Faces 表单的 faces 时相关的一系列事件。这与 图 3相同,除了在 FormComponent 中突出显示的方法 createActionForm()。 由 Struts-Faces API 提供的 FormComponent 类是 javax.faces.component.UIForm 的特殊子类,它支持请求或者会话范围的表单 Bean。

图 5. 呈现 Struts-Faces 响应 
单击这里以查看该图。

正如您所看到的, createActionForm() 方法使用 action 名以从 Struts 配置文件中得到 ActionMapping 。因为没有对于 /listFlights.do 的 ActionMapping ,所以 Struts 不能找到 ActionForm。

这个问题的解决方法是使用 org.apache.struts.util.RequestUtils 。 RequestUtils 中的 static 方法 getActionMappingName() 具有足够的智能解析映射到正确 ActionMapping 的路径( /x/y/z)或者后缀( .do)。

清单 12 以粗体显示对 createActionForm 方法的改变。我们没有对 Struts-Faces 中的 FormComponent 作这些改变,而是通过继承 FormComponent 并覆盖 createActionForm() 方法创建了一个新的 STFFormComponent。


清单 12. FormComponent 中修改过的 createActionForm() 方法


 

对新的 STFFormComponent 还要作一项改变。Struts-Faces 将 action 名本身作为表单名。这需要改变,因为 action 带有后缀 .do,而表单名没有后缀 .do。所以我们在 STFFormComponent 上增加一个名为 action的新属性,并覆盖 getAction() 和 setAction() 方法。

FormRenderer 的改变

必须对 FormRenderer (以 HTML 格式呈现 Struts-Faces 表单的类)的 encodeBegin 方法进行类似于 清单 10所示的修改。

同样,通过继承 FormRenderer 做到这一点。此外,还必须改变写出到 HTML 的表单 action。清单 13以粗体详细列出了这些改变:


清单 13. FormRenderer 的改变


 

FormTag的改变 
正如您已经知道的,当组件和 renderer 改变时,标记也必须改变。在这里,通过继承 Struts-Faces 中的 FormTag 创建一个新的标记: STFFormTag 。不必改变任何功能,只要覆盖 getComponentType() 和 getRendererType() 方法。清单 14 展示了从 STFFormComponent 覆盖的方法:


清单 14. FormTag 的改变


 

修改 faces-config.xml 文件

自定义组件和 renderer 必须在 faces-config.xml 文件中声明,这样 JSF 框架才可以初始化并使用它们。现在我们已经创建了一个新组件 STFFormComponent 和一个新 renderer STFFormRenderer 。

现在我们将在 faces-config.xml 文件中增加一个声明,如清单 15 所示。 component-class 是组件的完全限定类名。 component-type 指的是在 STFFormTag ( 清单 12)中用于标识组件的名字。以类似的方式发现和解释 renderer。注意 faces-config.xml 文件是在 struts-faces.jar 文件中的。从 struts-faces.jar 文件中取出这个文件并将它放到 Web 应用程序的 WEB-INF文件夹中并修改它。


清单 15. 在 faces-config.xml 中声明自定义组件和 renderer


 

修改 struts-faces.tld 文件

您不会在这个示例 Struts-Faces 应用程序中看到 struts-faces.tld 文件,它打包到了 struts-faces.jar 文件中。打开并分析这个文件。它声明了一个名为 org.apache.struts.faces.taglib.LifecycleListener 的类,这个类实现了 ServletContextListener 并初始化 FacesRequestProcessor 。

因为希望使用新的 STFRequestProccessor ,所以必须将这个文件从 struts-faces.jar 文件中删除,将它放到 Web 应用程序的 WEB-INF 文件夹中,并删除侦听器声明。如果让这个 tld 文件保持原样,那么在初始化这个 Web 应用程序时,除了 STFRequestProcessor ,还会实例化一个 FacesRequestProcessor。

修改 base href 标记 
现在,您已经完成了 Struts、Tiles、JSF 集成的最困难的部分了。您甚至可以浏览航班搜索页面,并输入搜索标准查看航班列表。现在试着从航班列表页面返回航班搜索表单。您会得到一个 HTTP 400 错误。这个错误的原因是 HTML base href 标记。它被设置为 Master Layout 页面。

 


 

程序所有页面浏览都是相对于布局页面计算的。如果加入的 base href 标记只达到 Web 应用程序上下文则会很方便,像这样:

 


 

我们可以通过定制 Struts-Faces BaseTag 做到这一点。这个类中的改变相当微不足道。只须在 base href 中去掉 HttpServletRequest.getServletPath() 。

因为这些改变是与显示相关的,所以为它创建了一个名为 STFBaseRenderer 的新 renderer。这个新标记称为 STFBaseTag ,它声明 STFBaseRenderer 作为其关联的 renderer。不需要新的组件。

有了这些信息,通过继承 BaseTag 并覆盖 getRendererType 方法创建新的 STFBaseTag ,如下所示:

 


 

到目前为止所作的改变

恭喜!经过这些相对较小的修改,您已经成功地集成了 Struts、Tiles 和 JSF,并保留了您以前在这些技术上所做的所有投资。本文演示了如何将 JSF 强大的前端能力、 Tiles 的内容格式编排优势以及 Struts 控制器层的灵活性结合在一个包中,使得创建一个 J2EE Web 应用程序成为一项更容易的任务。

标题SpringBoot智能在线预约挂号系统研究AI更换标题第1章引言介绍智能在线预约挂号系统的研究背景、意义、国内外研究现状及论文创新点。1.1研究背景与意义阐述智能在线预约挂号系统对提升医疗服务效率的重要性。1.2国内外研究现状分析国内外智能在线预约挂号系统的研究与应用情况。1.3研究方法及创新点概述本文采用的技术路线、研究方法及主要创新点。第2章相关理论总结智能在线预约挂号系统相关理论,包括系统架构、开发技术等。2.1系统架构设计理论介绍系统架构设计的基本原则和常用方法。2.2SpringBoot开发框架理论阐述SpringBoot框架的特点、优势及其在系统开发中的应用。2.3数据库设计与管理理论介绍数据库设计原则、数据模型及数据库管理系统。2.4网络安全与数据保护理论讨论网络安全威胁、数据保护技术及其在系统中的应用。第3章SpringBoot智能在线预约挂号系统设计详细介绍系统的设计方案,包括功能模块划分、数据库设计等。3.1系统功能模块设计划分系统功能模块,如用户管理、挂号管理、医生排班等。3.2数据库设计与实现设计数据库表结构,确定字段类型、主键及外键关系。3.3用户界面设计设计用户友好的界面,提升用户体验。3.4系统安全设计阐述系统安全策略,包括用户认证、数据加密等。第4章系统实现与测试介绍系统的实现过程,包括编码、测试及优化等。4.1系统编码实现采用SpringBoot框架进行系统编码实现。4.2系统测试方法介绍系统测试的方法、步骤及测试用例设计。4.3系统性能测试与分析对系统进行性能测试,分析测试结果并提出优化建议。4.4系统优化与改进根据测试结果对系统进行优化和改进,提升系统性能。第5章研究结果呈现系统实现后的效果,包括功能实现、性能提升等。5.1系统功能实现效果展示系统各功能模块的实现效果,如挂号成功界面等。5.2系统性能提升效果对比优化前后的系统性能
在金融行业中,对信用风险的判断是核心环节之一,其结果对机构的信贷政策和风险控制策略有直接影响。本文将围绕如何借助机器学习方法,尤其是Sklearn工具包,建立用于判断信用状况的预测系统。文中将涵盖逻辑回归、支持向量机等常见方法,并通过实际操作流程进行说明。 一、机器学习基本概念 机器学习属于人工智能的子领域,其基本理念是通过数据自动学习规律,而非依赖人工设定规则。在信贷分析中,该技术可用于挖掘历史数据中的潜在规律,进而对未来的信用表现进行预测。 二、Sklearn工具包概述 Sklearn(Scikit-learn)是Python语言中广泛使用的机器学习模块,提供多种数据处理和建模功能。它简化了数据清洗、特征提取、模型构建、验证与优化等流程,是数据科学项目中的常用工具。 三、逻辑回归模型 逻辑回归是一种常用于分类任务的线性模型,特别适用于二类问题。在信用评估中,该模型可用于判断借款人是否可能违约。其通过逻辑函数将输出映射为0到1之间的概率值,从而表示违约的可能性。 四、支持向量机模型 支持向量机是一种用于监督学习的算法,适用于数据维度高、样本量小的情况。在信用分析中,该方法能够通过寻找最佳分割面,区分违约与非违约客户。通过选用不同核函数,可应对复杂的非线性关系,提升预测精度。 五、数据预处理步骤 在建模前,需对原始数据进行清理与转换,包括处理缺失值、识别异常点、标准化数值、筛选有效特征等。对于信用评分,常见的输入变量包括收入水平、负债比例、信用历史记录、职业稳定性等。预处理有助于减少噪声干扰,增强模型的适应性。 六、模型构建与验证 借助Sklearn,可以将数据集划分为训练集和测试集,并通过交叉验证调整参数以提升模型性能。常用评估指标包括准确率、召回率、F1值以及AUC-ROC曲线。在处理不平衡数据时,更应关注模型的召回率与特异性。 七、集成学习方法 为提升模型预测能力,可采用集成策略,如结合多个模型的预测结果。这有助于降低单一模型的偏差与方差,增强整体预测的稳定性与准确性。 综上,基于机器学习的信用评估系统可通过Sklearn中的多种算法,结合合理的数据处理与模型优化,实现对借款人信用状况的精准判断。在实际应用中,需持续调整模型以适应市场变化,保障预测结果的长期有效性。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值