14、使用 JavaServer Faces 构建 Java EE 应用程序的表示层

用 JavaServer Faces 构建 Java EE 应用表示层

使用 JavaServer Faces 构建 Java EE 应用程序的表示层

1. 构建表示层概述

在开发应用程序时,数据库、持久化和业务逻辑层固然重要,但如果没有实现表示层(也称为应用程序的前端层),这些层的功能就无法为用户所用。表示层实现了应用程序的展示逻辑,使用户能够利用应用程序其他层实现的所有功能。本文将介绍如何使用 JavaServer Faces (JSF) 技术为示例应用程序构建表示层。

构建表示层的主要步骤包括:
- 构建 JSF 豆(beans),通过它们访问已有的会话豆。
- 在 JSF 豆的基础上开发 JSF 页面。
- 保障 Web 应用程序的安全。
- 配置 Web 应用程序。
- 将 Web 应用程序部署到应用服务器。

2. 从表示层访问 Java EE 功能

在某些情况下,不一定需要构建 Web 组件来访问 EJB 和 JTA 组件,例如可以创建一个应用程序客户端,使用 appclient 命令行工具来访问企业豆。但在大多数情况下,构建 Java EE 应用程序的表示层意味着构建一个利用 Java EE 组件提供业务逻辑的 Web 应用程序。

3. 选择 Web 层技术

在实现 Java EE Web 应用程序时,有多种技术可供选择,如 JSP 技术。但 JavaServer Faces 技术相比 JSP 具有许多优势,其中最重要的是业务逻辑和表示的清晰分离。

由于已经构建了企业豆,因此可以开发利用这些企业豆功能的 JSF 豆,而不是从头开始实现应用程序的业务逻辑。企业豆作为构建在持久化层之上的外观(Facade),将客户端与实体模型解耦,这种解决方案被称为 Facade 模式。

4. 规划表示层

为了说明如何在 JSF 管理豆中利用 EJB 组件,需要构建一个简单的 JSF 应用程序。该应用程序至少需要以下两个页面:
- index.jsp :显示所有可供购买的书籍,并允许用户将感兴趣的书籍添加到购物车。
- showcart.jsp :显示购物车中的物品,允许用户删除不必要的物品并最终下单。

还需要创建以下 JSF 管理豆:
- OrderJSFBean :用于与 CartBean OrderBean 企业豆进行交互。
- BookJSFBean :用于访问书籍表中的数据。

此外,为了保证应用程序的安全性,还需要创建两个页面:
- login.jsp :用于基于表单的身份验证。
- login_error.jsp :如果身份验证失败,用户将被重定向到该页面。

5. 使用 JAAS 保障 Java EE 应用程序的安全

由于示例应用程序需要处理敏感信息,因此需要采用一种能够实现用户身份验证和授权的安全机制。Java Authentication and Authorization Service (JAAS) 提供了一种标准的方法来实现这些目标。

在 GlassFish 中使用 JAAS,首先需要设置一个安全领域(security realm),将用户组织到安全组中。然后,需要在应用程序中添加一个登录页面,用户可以在该页面输入凭据。经过身份验证的用户将只能访问其所属组允许的资源。

6. 在 GlassFish 中创建 JDBC 领域

GlassFish 自带了三个预定义的安全领域: admin-realm file realm certificate realm 。除了这些预配置的领域,还可以创建以下领域:
- LDAP 领域:将身份验证信息存储在 LDAP 数据库中。
- JDBC 领域:将身份验证信息存储在关系数据库中。
- Solaris 领域:仅可在 Solaris 操作系统上使用。

以下是创建 JDBC 领域的具体步骤:

6.1 创建存储账户信息的数据库表

在创建 JDBC 领域之前,需要创建数据库表来存储用户的账户信息。具体需要设置以下三个表:
- 用户凭据表:可以使用现有的 customers 表,但需要修改其结构,添加一个 password 列来存储用户的密码。
- 用户组信息表。
- 两个表之间的多对多关系连接表。

创建这些表后,可以创建一个视图,以便 JDBC 领域只使用一个数据库接触点。以下是在 MySQL 服务器上执行的 SQL 命令:

use dbsample;
ALTER TABLE customers ADD COLUMN password CHAR(32);
CREATE TABLE groups(
    group_id VARCHAR(25) PRIMARY KEY,
    group_desc VARCHAR(100)
);
CREATE TABLE customergroups(
    cust_id INTEGER,
    group_id VARCHAR(25),
    PRIMARY KEY(cust_id, group_id),
    FOREIGN KEY(cust_id) REFERENCES customers(cust_id),
    FOREIGN KEY(group_id) REFERENCES groups(group_id)
);
CREATE VIEW login_v
AS SELECT c.cust_id, c.password, g.group_id FROM 
customers c, groups g, customergroups r
WHERE c.cust_id = r.cust_id AND g.group_id = r.group_id;

注意, customers 表中添加的 password 列定义为 CHAR(32) ,因为 GlassFish 中默认的加密算法是 MD5,MD5 哈希值是一个 32 字符的字符串。

接下来,需要向这些表中填充数据,以下是插入两个客户记录身份验证信息的 SQL 语句:

INSERT INTO groups VALUES('testRole', 'Security group for users of the sample app');
INSERT INTO customergroups VALUES(2, 'testRole');
UPDATE customers
SET password = '42766beab1dc267fbf26df32e1addfff'
WHERE cust_id = 1;
UPDATE customers
SET password = '0f8031d929f89ecb1d251f0f8bc9d9f9'
WHERE cust_id = 2;
6.2 创建 JDBC 领域

完成数据库设置后,可以在应用服务器中创建 JDBC 领域。具体步骤如下:
1. 以 admin 身份连接到管理控制台(Admin Console)。
2. 导航到 Configuration/Security/Realm 页面。
3. 点击 New 按钮,在 Name 字段中输入名称,例如 myjdbc ,在 Class Name 框中选择 com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm
4. 在对话框的 Properties Specific to This Class 部分,按照以下表格填写字段:

属性名称 属性值 描述
JAAS context jdbcRealm 要使用的登录模块类型,默认为 jdbcRealm。
JNDI jdbc/mysqlpool 可通过其访问安全表的数据源的 JNDI 名称。
User Table login_v 包含要使用的用户信息的表,这里使用从 customers groups 表派生的视图。
User Name Column cust_id 用户表中的用户名列。
Password Column password 用户表中的密码列。
Group Table login_v 包含安全组信息的表,这里使用从 customers groups 表派生的视图。
Group Name Column group_id 组表中的用户名列。
  1. 完成属性设置后,点击 OK 按钮,将创建一个新的 JDBC 领域。
7. 使用 JSF 构建示例应用程序的表示层
7.1 绘制项目结构图

在开始构建之前,需要创建一个表示项目目录结构的图表,包括要创建的文件。以下是项目结构的示意图:

graph LR
    A[sampleapp] --> B[target]
    B --> C[WEB-INF]
    C --> D[classes]
    D --> E[ejbjpa]
    E --> F[jsfbeans]
    C --> G[lib]
    A --> H[src]
    H --> I[ejbjpa]
    I --> J[jsfbeans]
    A --> K[WebContent]
    K --> L[stylesheet.css]
    K --> M[index.jsp]
    K --> N[showcart.jsp]
    K --> O[login.jsp]
    K --> P[login_error.jsp]

根据这个结构图,创建相应的目录和文件。

7.2 开发 JSF 管理豆

在 JSF 应用程序中,管理豆封装了应用程序的业务逻辑,以便 JSF 页面可以引用豆的属性。以下是 OrderJSFBean 管理豆的源代码:

package ejbjpa.jsfbeans;
import javax.ejb.EJB;
import javax.naming.InitialContext;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpSession;
import java.util.List;
import java.util.Map;
import ejbjpa.ejb.*;
import ejbjpa.entities.*;
@EJB(name="ejb/CartBean", beanInterface=Cart.class)
public class OrderJSFBean {
    private Cart cart;
    @EJB
    private OrderSample order;
    private List<ShoppingCart> cartItems;
    private Integer custId;
    public OrderJSFBean() {
        custId = Integer.parseInt(
            FacesContext.getCurrentInstance().getExternalContext().
            getUserPrincipal().getName());
        try{
            if (cart == null) {
                cart = (Cart) (new InitialContext()).lookup("java:comp/env/ejb/CartBean");
            }
            cart.initialize(custId);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public Integer getCustId() {
        return custId;
    }
    public List<ShoppingCart> getCartItems() {
        cartItems = null;
        try {
            cartItems = cart.getItems();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return cartItems;
    }
    public void addToCart() {
        try {
            FacesContext cxt = FacesContext.getCurrentInstance();
            Map params = cxt.getExternalContext().getRequestParameterMap();
            String isbn = (String)params.get("isbn");
            String price_str = (String)params.get("price");
            Double price =new Double(price_str);
            cart.addItem(isbn, 1, price);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public void removeFromCart() {
        try {
            FacesContext cxt = FacesContext.getCurrentInstance();
            Map params = cxt.getExternalContext().getRequestParameterMap();
            String itemId = (String)params.get("itemId");
            cart.removeItem(itemId);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public String ProceedToCheckout() {
        try {
            order.placeOrder(custId, 1);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "continue";
    }
}

BookJSFBean 管理豆与 OrderJSFBean 不同,它不引用企业豆,而是直接通过 JDBC 连接到基础数据库,并从 books 表中检索所有记录。以下是 BookJSFBean 管理豆的源代码:

package ejbjpa.jsfbeans;
import javax.ejb.EJB;
import javax.naming.InitialContext;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpSession;
import java.util.List;
import ejbjpa.ejb.*;
import ejbjpa.entities.*;
import java.sql.*;
import javax.sql.DataSource;
public class BookJSFBean {
    private Connection connDb;
    public void openConnection() throws Exception {
        if(connDb != null)
            return;
        DataSource dataSource = (DataSource) (new 
            InitialContext()).lookup("java:comp/env/jdbc/mysqlpool");
        connDb = dataSource.getConnection();
    }
    public ResultSet getAllBooks() throws Exception {
        ResultSet rslt = null;
        this.openConnection();
        Statement stmt = connDb.createStatement();
        rslt = stmt.executeQuery("SELECT * FROM books");
        return rslt;
    }
}

创建管理豆的源文件后,可以编译它们。在 sampleapp 目录下执行以下命令:

javac -cp target/WEB-INF/lib/appejb.jar;yourglassfishdir/lib/javaee.jar -d target/WEB-INF/classes src/ejbjpa/jsfbeans/*.java

编译完成后, ejbjpa/jsfbeans 目录将出现在 sampleapp/target/WEB-INF/classes 目录中,并且会生成 BookJSFBean.class OrderJSFBean.class 文件。

使用 JavaServer Faces 构建 Java EE 应用程序的表示层

7.3 开发 JSF 页面

构建应用程序的下一步是创建 JSF 页面。以下是 index.jsp 页面的源代码,该页面引用了 BookJSFBean 管理豆的 allBooks 属性:

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<f:view>
    <head>
        <link href="stylesheet.css" rel="stylesheet" type="text/css"/>
    </head>
    <h:form>
        <h2>List of books</h2>
        <br/>
        <h:dataTable value="#{book.allBooks}" var ="book"
                     headerClass= "header"
                     columnClasses="evenCol, oddCol">
            <h:column>
                <f:facet name="header">
                    <h:outputText value="ISBN"/>
                </f:facet>
                <h:outputText value="#{book.isbn}"/>
            </h:column>
            <h:column>
                <f:facet name="header">
                    <h:outputText value="Title"/>
                </f:facet>
                <h:outputText value="#{book.title}"/>
            </h:column>
            <h:column>
                <f:facet name="header">
                    <h:outputText value="Author"/>
                </f:facet>
                <h:outputText value="#{book.author}"/>
            </h:column>
            <h:column>
                <f:facet name="header">
                    <h:outputText value="Price"/>
                </f:facet>
                <h:outputText value="#{book.price}"/>
            </h:column>
            <h:column>
                <f:facet name="header">
                    <h:outputText  value="Copies left"/>
                </f:facet>
                <h:outputText value="#{book.quantity}"/>
            </h:column>
            <h:column>
                <h:commandLink action="#{OrderJSFBean.addToCart}" value="Add to cart">
                    <f:param name = "isbn" value = "#{book.isbn}"/>
                    <f:param name = "price" value = "#{book.price}"/>
                </h:commandLink>
            </h:column>
        </h:dataTable>
        <p/>
        <h:commandButton action="showcart" value="Move to cart"/>
    </h:form>
</f:view>

从代码中可以看出, index.jsp 页面将显示所有书籍记录,允许用户将感兴趣的书籍添加到购物车。

以下是 showcart.jsp 页面的源代码,该页面可以从 index.jsp 页面中启动:

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<f:view>
    <head>
        <link href="stylesheet.css" rel="stylesheet" type="text/css"/>
    </head>
    <h:form>
        <h2>Your shopping cart items to buy now</h2>
        <br/>
        <h:dataTable value="#{OrderJSFBean.cartItems}" var ="shoppingCart"
                     headerClass= "header"
                     columnClasses="evenCol, oddCol">
            <h:column>
                <f:facet name="header">
                    <h:outputText value="Book isbn"/>
                </f:facet>
                <h:outputText value="#{shoppingCart.book_id}"/>
            </h:column>
            <h:column>
                <f:facet name="header">
                    <h:outputText value="Quantity"/>
                </f:facet>
                <h:outputText value="#{shoppingCart.units}"/>
            </h:column>
            <h:column>
                <f:facet name="header">
                    <h:outputText value="Unit price"/>
                </f:facet>
                <h:outputText value="#{shoppingCart.unit_price}"/>
            </h:column>
            <h:column>
                <h:commandLink action="#{OrderJSFBean.removeFromCart}" value="Delete">
                    <f:param name = "itemId" value = "#{shoppingCart.book_id}"/>
                </h:commandLink>
            </h:column>
        </h:dataTable>
        <p/>
        <h:commandButton action="#{OrderJSFBean.ProceedToCheckout}" value="Proceed to checkout"/>
        <h:commandButton action="continue" value="Continue shopping"/>
    </h:form>
</f:view>

showcart.jsp 页面允许用户查看购物车内容,必要时删除物品,然后可以下单,下单后购物车将自动清空。

7.4 创建安全页面

接下来创建基于表单身份验证所需的页面。以下是 login.jsp 页面的源代码:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<html>
<head><title>Login Page</title></head>
<h2>Please login:</h2>
<form method="POST" action="j_security_check">
    <p>Enter Customer ID: <input type="text" name="j_username" size="25"></p>
    <p>Enter Password:<input type="password" size="15" name="j_password"></p>
    <input type="submit" value="Submit">
    <input type="reset" value="Reset">
</form>
</html>

如果身份验证失败,将显示 login_error.jsp 页面,其源代码如下:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head><title>Login error page</title></head>
<body>
    <c:url var="url" value="index.faces"/>
    <h2>User name or password is wrong.</h2>
    <p>Please enter a valid user name and password. 
       Click here to <a href="${url}">try again.</a></p>
</body>
</html>

login_error.jsp 页面会告知用户身份验证失败,并提供再次尝试的链接。

8. 配置应用程序

在将应用程序打包成部署存档之前,需要创建部署描述符 sun-web.xml web.xml ,以及配置资源文件 faces-config.xml

以下是 faces-config.xml 配置文件的源代码:

<?xml version="1.0"?>
<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">
    <managed-bean>
        <managed-bean-name>OrderJSFBean</managed-bean-name>
        <managed-bean-class>ejbjpa.jsfbeans.OrderJSFBean</managed-bean-class>
        <managed-bean-scope>session</managed-bean-scope>
    </managed-bean>
    <managed-bean>
        <managed-bean-name>book</managed-bean-name>
        <managed-bean-class>ejbjpa.jsfbeans.BookJSFBean</managed-bean-class>
        <managed-bean-scope>session</managed-bean-scope>
    </managed-bean>
    <navigation-rule>
        <navigation-case>
            <description>
                By clicking the "Move to cart" button on the index.jsp page 
                you move to showcart.jsp
            </description>
            <from-outcome>showcart</from-outcome>
            <to-view-id>/showcart.jsp</to-view-id>
        </navigation-case>
        <navigation-case>
            <description>
                Clicking the "Continue shopping" turns you back to index.jsp 
                showing the list of books available
            </description>
            <from-outcome>continue</from-outcome>
            <to-view-id>/index.jsp</to-view-id>
        </navigation-case>
    </navigation-rule>
</faces-config>

以下是 web.xml 配置文件的源代码:

<?xml version="1.0" encoding="UTF-8"?>
<web-app 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-app_2_5.xsd"
         version="2.5">
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>testing web app</web-resource-name>
            <url-pattern>/*</url-pattern>
            <http-method>POST</http-method>
            <http-method>GET</http-method>
        </web-resource-collection>
        <auth-constraint>
            <role-name>testRole</role-name>
        </auth-constraint>
        <user-data-constraint>
            <transport-guarantee>NONE</transport-guarantee>
        </user-data-constraint>
    </security-constraint>
    <login-config>
        <auth-method>FORM</auth-method>
        <realm-name>myjdbc</realm-name>
        <form-login-config>
            <form-login-page>/login.jsp</form-login-page>
            <form-error-page>/login_error.jsp</form-error-page>
        </form-login-config>
    </login-config>
    <security-role>
        <role-name>testRole</role-name>
    </security-role>
    <servlet>
        <display-name>FacesServlet</display-name>
        <servlet-name>FacesServlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>FacesServlet</servlet-name>
        <url-pattern>*.faces</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>index.faces</welcome-file>
    </welcome-file-list>
    <ejb-ref>
        <ejb-ref-name>ejb/CartBean</ejb-ref-name>
        <ejb-ref-type>Session</ejb-ref-type>
        <remote>ejbjpa.ejb.Cart</remote>
    </ejb-ref>
    <resource-ref>
        <res-ref-name>jdbc/mysqlpool</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
        <res-sharing-scope>Shareable</res-sharing-scope>
    </resource-ref>
</web-app>

以下是 sun-web.xml 运行时部署描述符的源代码:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-web-app PUBLIC "-//Sun Microsystems, Inc.//
DTD Application Server 8.0 Servlet 2.4//EN" 
"http://www.sun.com/software/appserver/dtds/sun-web-app_2_4-0.dtd">
<sun-web-app>
    <context-root>/sampleapp</context-root>
    <security-role-mapping>
        <role-name>testRole</role-name>
        <group-name>testRole</group-name>
    </security-role-mapping>
</sun-web-app>

完成所有文件的创建后,将它们打包成部署包。在 sampleapp/target 目录下执行以下命令:

jar cvf jsfapp.war .

然后使用以下命令部署存档:

asadmin deploy jsfapp.war
9. 总结

通过上述步骤,完成了一个示例应用程序的开发。展示了如何使用 JavaServer Faces 技术实现 Java EE 应用程序的表示层,并利用封装了应用程序业务逻辑的企业豆。后续可以对该示例应用程序进行测试,找出可能需要改进的地方以提高效率。

【电能质量扰动】基于ML和DWT的电能质量扰动分类方法研究(Matlab实现)内容概要:本文研究了一种基于机器学习(ML)和离散小波变换(DWT)的电能质量扰动分类方法,并提供了Matlab实现方案。首先利用DWT对电能质量信号进行多尺度分解,提取信号的时频域特征,有效捕捉电压暂降、暂升、中断、谐波、闪变等常见扰动的关键信息;随后结合机器学习分类器(如SVM、BP神经网络等)对提取的特征进行训练与分类,实现对不同类型扰动的自动识别与准确区分。该方法充分发挥DWT在信号去噪与特征提取方面的优势,结合ML强大的模式识别能力,提升了分类精度与鲁棒性,具有较强的实用价值。; 适合人群:电气工程、自动化、电力系统及其自动化等相关专业的研究生、科研人员及从事电能质量监测与分析的工程技术人员;具备一定的信号处理基础和Matlab编程能力者更佳。; 使用场景及目标:①应用于智能电网中的电能质量在线监测系统,实现扰动类型的自动识别;②作为高校或科研机构在信号处理、模式识别、电力系统分析等课程的教学案例或科研实验平台;③目标是提高电能质量扰动分类的准确性与效率,为后续的电能治理与设备保护提供决策依据。; 阅读建议:建议读者结合Matlab代码深入理解DWT的实现过程与特征提取步骤,重点关注小波基选择、分解层数设定及特征向量构造对分类性能的影响,并尝试对比不同机器学习模型的分类效果,以全面掌握该方法的核心技术要点。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值